NoSQL技术交流 ICT-存储部-马健
目录 什么是NoSQL CAP理论 NoSQL解决的问题 NoSQL的不足 较成熟的NoSQL数据库对比 TC/TT MongoDB
什么是NoSQL NoSQL是一种与关系型数据库管理系统截然不同的数据库管理系统,它的数据存储格式可以是松散的、通常不支持Join操作并且易于横向扩展。也可以称之为非关系型数据库。 关系型数据库 nosql Name Age Gender Birthday a 20 M 1990-10-1 b 40 F 1970-8-24 c 30 1980-1-18 … Name : a Age : 20 Gender : M Birthday : 1990-10-1 Hobby : travel Name : b Age : 40 Birthday : 1970-8-24 Tel : 12345678 Name : c Age : 30 …
CAP理论 Availability Partition Tolerance Consistency 可用性 RDBMS NoSQL 一致性 某个节点的宕机不会影 响其他节点继续完成操 作 Consistency 一致性 Partition Tolerance 分区容错性 RDBMS NoSQL 数据一致更新,所有 数据变动都是同步的 尽管有一些信息丢失 ,系统依旧继续运转
NoSQL解决的问题 对数据库的高并发读写 对海量数据的高效率存储和访问 数据库的可扩展性 Key-Value型数据库:TC/TT、Redis等 对海量数据的高效率存储和访问 文档型数据库:MongoDB、CouchDB 数据库的可扩展性 分布式数据库:Cassandra、MongoDB
NoSQL的不足 没有完美的技术,NoSQL也存在着它的不足: 功能相对贫乏,NoSQL数据库大部分功能集仅仅旨在满足现代的Web 2.0应用的需求。在一些要求事务一致性较高、业务逻辑比较复杂或者一些需要复杂分析查询的环境中,NoSQL难以担当重任。
较成熟的NoSQL数据库对比 Redis TC&TT MongoDB Cassandra 数据模型 Key-value Documents Column-family Value尺寸限制 1G 1MB 4MB 2G 功能和特性 复制:一主多从。 复制:双主。 多种数据结构。 M-S、Replica-set。 Shard集群。 GridFS。 强大的查询功能。 分布式集群,可以动态扩展集群节点。 点对点的集群 性能 由于是单纯地键值内存操作,其性能四者中居首位。 85000 SETs/second 72000 GETs/second 总体性能略逊于Redis。 Insert : 43000/second, Search : 46000/second 结构相比TC复杂,所以性能不如TC。吞吐量:写 92129 KB/S 读 104052 KB/S 单点性能最差,但会随着节点的增长而提升。 成功案例 github,Engine Yard 日本最大的SNS网站mixi.jp。2000万数据量、10000并发量、双主架构。 Foursquare、shutterfly等 Facebook, digg等。 应用场景 由于受到物理内存的限制,只适合较小数据量的高性能操作和运算场景。 适用于逻辑关系相对简单,但数据量较大的业务。 主要解决海量数据的存储效率、数据分布和数据扩展等问题。 适用于亿万级数据量,存储于多台的大数据量集群环境。 协议 基于TCP的文本协议 memcached protocol 、HTTP protocol 轻量级的TCP/IP写入协议 客户端:socket 协议 集群:Gossip 协议 支持语言 Python,Ruby,Erlang,PHP,Java ,C Perl, Ruby, Java, Python, PHP, Erlang C、Java、Perl、PHP、Python、Ruby C++, Java, Python, PHP, Ruby, Erlang, Perl 不足 不适于较大数据对象的读写,性能下降明显。 TC的主要缺点是在TDB的数据量达到上亿级别以后,并发写数据性能会大幅度下降 Shard集群的稳定性并不是很好:数据的分布和安全性在高负载的情况下无法得到100%的保障。 Thrift 端口收到非协议标准的随机数据可能导致Cassandra 崩溃。
TC/TT TC:Tokyo Cabinet。它其实是一套数据库管理的功能类库。它只将字符串和二进制数据以Key-value的形式存储。既没有表的概念,也没有数据类型的概念,数据库的记录都被组织在哈希表、B+Tree或者定长数组中。TC为不同的应用场景设计了不同的数据库类型。虽然具有很高的性能,但TC本身并不支持远程访问。 TT:Tokyo Tyrant。TC的网络接口,为其提供了高并发读写、远程访问以及复制等更丰富的管理功能。支持Memcached兼容协议,也可以通过HTTP协议进行数据交换。 TC/TT由C语言编写,为C、Perl、Ruby、Java均提供了程序接口。
TC的一些概念 物理层面上,TC的数据库其实就是包含了以简单的key-value对为记录的数据文件。 逻辑层面上,TC的数据库相当于RDBMS中的表。 每个数据库都拥有它自己的缓存 TC提供了一系列的shell工具来对每个数据库(数据文件)进行创建、读、写、优化等操作。当然你也可以通过程序API调用相应的方法来实现。
TC的几种数据引擎 根据不同的使用场景,TC提供了几种不同的数据存储方式: TCHDB 哈希数据库 TCBDB B+Tree数据库 tchmgr: http://www.nixway.net/index.php?manitem&mid=2444 TCBDB B+Tree数据库 tcbmgr: http://www.nixway.net/index.php?manitem&mid=2438 TCFDB 定长数据库 tcfmgr: http://www.nixway.net/index.php?manitem&mid=2441 TCTDB 表格数据库 tctmgr: http://www.nixway.net/index.php?manitem&mid=2449 TCMDB 内存哈希数据库 TCNDB 内存B+Tree数据库
TC的几种数据引擎 - TCHDB 在哈希表数据库中,每个key必须是唯一的。它提供了遍历访问的功能,但其中的数据是无序的。性能仅次于FDB。 TC通过读取内存中的bucket array来提升访问速度,这里的bucket array实际上就是TC为某个数据库设置的缓存,它存放了映射到内存中的全部/部分数据文件的数据。 哈希值冲突由separate chaining策略来管理。 TC在存入数据时,除了传统的insert和replace,还提供了concatenate模式。
TC的几种数据引擎 - TCBDB 与TCHDB不同,B+ Tree数据库的key可以是重复的。它的数据是排序并排列在逻辑页中的,所以除了拥有TCHDB的基本功能以外,它还可以根据用户的需求对数据进行排序。 由于每个逻辑页都被安排成双向链表,所以可以通过游标按某种顺序访问每一条记录,也可以跳到某一个指定的key的位置。基于这个特性,实现了字符串的正向匹配和数字的范围查询。 BDB中的数据页是可以被压缩的,支持ZLIB和BZIP2两种方法。
TC的几种数据引擎 - TCFDB 在定长数组数据库中,每条记录都被给予唯一的自然数作为它的key,而这些记录(Value)的长度是被限制死的。整个数据库可被看做是一个数组。FDB提供了和HDB同样的功能。 因为FDB所有的数据都会作为一个多维数组通过mmap映射到内存中,最小化文件I/O,所以只要在满足限制的情况下,FDB的性能在这几种数据库中是最好的。 数据库的大小和所定义的key范围和value大小成正比。Key范围越小、value size越小,空间效能就越高。
TC的几种数据引擎 - TCTDB TCTDB是TCHDB的一个变种,每条记录都由主键来识别,并且拥有一组被命名的字段。虽然数据结构可以是松散的,但是依然可以通过索引来加速一些相对复杂的查询。它既具备了Key-Value数据库的高效读写性能,又具备了MySQL单表能实现的一些功能,即:SELECT …. FROM table WHERE …. ORDER BY …. LIMIT xxx,xxx 支持字符串的完全匹配、正向匹配、正则表达式匹配等。支持数值型的完全匹配和范围匹配。 支持标签搜索和全文搜索 支持多条件查询、UNION、正反向排序。
TC的使用示例 http://fallabs.com/tokyocabinet/
MongoDB MongoDB官方给自己的定义是Key-value存储(高性能和高扩展)和传统RDBMS(丰富的查询和功能)之间的一座桥梁。 功能:MongoDB所提供的查询和各种功能在NoSQL产品中的确是佼佼者,如GridFS提供了文件存储功能、Map/Reduce的数据聚合操作为分布式系统的高性能读写提供了技术基础、分布式的动态sharding集群提供了高可扩展性、Replica-set的投票和复制机制又提供了高可用性…… 性能:丰富的功能无疑带来的就是性能的下降,它无法像TC/TT一样达到如此高的性能指标,但它的性能依然可以达到MySQL的17倍。
MongoDB - document MongoDB的最小存储单位就是文档对象,数据在Mongo中以BSON(Binary-JSON)文档的格式存储在磁盘上。每一个文档对象,MongoDB都会为它分配一个唯一的id号,名为“_id”。如果硬要和关系型数据库扯上关系,你可以把文档视作关系型数据库中的行。 下图描述了一个最简单的Mongo document: { _id : ObjectId( “xxxxxxxx" ) , name : "majian", gender : "M", address : "Beijing" } 如果不显式创建_id,mongo会自动创建并为它分配一个唯一的值,作为该文档对象的唯一标识。
MongoDB - document 跟一般的key-value数据库不一样的是,它的value中存储了结构信息,所以它提供了嵌入式的文档结构。 { _id : ObjectId( “xxxxxxx" ) , name : "majian", gender : "M", address : "Beijing", score : } [ Chinese : "60", English : "50" ]
MongoDB - collection Collection就是documents的集合,可以理解为关系型数据库中的表,也可以看成一个文件夹,用来专门储存同一类文档。 COLLECTION ____________ _document_
MongoDB - Database MongoDB的最外层结构,和关系型数据库一样,是存放多个Collections的容器。 可以把MongoDB看成一个文件仓库,每个document就如同一页纸;成千上万张纸被存放在文件夹里,这些文件夹就可以看做是Collection;多个文件夹存放在一个储藏柜里,也就是Database。 Document Collection Database
MongoDB – GridFS 元数据 二进制数据 物理文件 文件切分 id : ObjectId("xxxxx"), filename : "file", chunkSize : 262144, uploadDate : "xxx", md5 : "xxx", length : 668734007 物理文件 文件切分 _id : ObjectId("yyyyy"), files_id : ObjectId("xxxxx"), n : chunk_number, data : data_binary,
MongoDB – GridFS性能 针对物理文件的存储,我们对MySQL和MongoDB做了一次性能对比,下面的图标反映了二者对于存储3MB物理文件的性能差异。 并发 MySQL MongoDB 耗时 吞吐量 1 1.63 2004 0.57 5677 50 34.9 4682 2.24 72928 100 76 4300 4.08 79981 150 126 3891 6.87 71340 200 175.32 3728 6.93 94309 250 233.46 3500 20.27 40304 300 251.1 3905 18.01 54442 350 481.93 2374 37.04 30879 400 686.41 1905 24.05 54341 450 1126.32 1306 41.33 35585 500 1325.87 1233 178.32 9164
MongoDB –数据查询 db.users.find({'last_name': 'Smith'}) http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bcount%28%29%7D%7D db.users.find({'last_name': 'Smith'}) Select * from users where last_name=‘Smith’; db.users.find({last_name: 'Smith'}, {'ssn': 1}); Select ssn from users where last_name=‘Smith’; db.users.find().skip(20).limit(10); Select * from users limit 20,10; db.things.find({j:{$nin: [2,4,6]}}); Select * from things where j is not in (2,4,6); db.address.distinct( "zip-code" , { age : 30 } ) Select distinct zip-code from address where age=30; <, <=, >, >=, $all, $exists, $mod, $ne, $in, $nin, $nor, $or, $size, $type等操作符。 游标方法:count(), limit(), skip(), snapshot(), sort()
MongoDB –References MongoDB是非关系型数据库,不支持join操作(无法select a.*,b.* from a,b where a.id=b.id;也无法select cols from a, b where a.id=b.id;)。但是可以通过References的方式去实现两个document之间的关联查询。 > p = db.postings.findOne({title:'Brewing Methods'}); { "_id" : ObjectId("4b866f08234ae01d21d89604"), "author" : "jim", "title" : "Brewing Methods" } > db.users.find( { _id : p.author } ) { "_id" : "jim", "email" : "jim@gmail.com" }
MongoDB –跨库跨实例查询 利用MongoDB提供的函数connect()可以为我们的查询提供更丰富的功能:通过它,你可以进行跨实例的查询: 假设我们在两个服务器sv1和sv2上各跑一个mongod,sv1上建db1数据库,sv2上建db2数据库,分别创建数据如下: 现在要知道pencil这个物品的拥有者的详细信息,假设我们正在连接sv1,具体实现方法如下: > use db1 switched to db db1 > db.person.save({name:'xiaoming',job:'student',age:'18',address:'Beijing'}) db.person.save({name:'qiangqiang',job:'Teacher',age:'28',address:'Kunming'}) > use db2 switched to db db2 > db.stuff.save({item:'pencil',owner:'xiaoming'}) > db.stuff.save({item:'table',owner:'qiangqiang'}) > conn = connect("sv2:27017/db2") connecting to: sv2:27017/db2 db2 > res = conn.stuff.findOne({item:'pencil'}) { "_id" : ObjectId("4c93720a859fbb4e384da194"), "item" : "pencil", "owner" : "xiaoming" } > db.person.findOne({name:res.owner}) "_id" : ObjectId("4c93775d1d031ab611413187"), "name" : "xiaoming", "job" : "student", "age" : "18", "address" : "Beijing"
MongoDB –Map/Reduce http://www.mongodb.org/display/DOCS/MapReduce MapReduce是一种编程模式,也是一种处理和生成大型数据集的联合运算方式。MongoDB使用这个技术去实现对单个实例和sharding集群中的数据聚合操作,数据库会自动生成临时的collection来保存这些结果集, 当关闭连接时,这些临时的collection会自动被删除。在sharding集群中,map/reduce方法会让查询平行地在每个shard上面执行,最终返回混合后的结果。
MongoDB – 缓存 MongoDB采用的是内存映像存储引擎,它通过内存映像文件进行全部的磁盘I/O操作。这样使MongoDB具有如下特性: 不存在操作系统缓存和数据库缓存之间的冗余,它们是一样的。 不需要人为修改设置,MongoDB会自动使用系统剩余的所有内存作为cache。 缓存动作,如LRU等页操作等,都由操作系统来控制。
MongoDB – shard集群 MongoDB的shard集群支持数据的自动切分和平衡分布。 每个shard节点都可以由一个replica-set高可用架构组成,也就是说实现了自动的故障恢复功能,避免了单点故障。 Shard集群由以下几个关键点组成:Shard keys、Chunks、Shard nodes、Congfigure servers、Routine servers
MongoDB集群 – 架构图 Replica-set Configure servers … client … mongod Shard 1 mongod Shard 2 mongod Shard 3 Replica-set Configure servers c1 mongod c2 mongod c3 mongod mongos … client …
MongoDB集群 – shard keys 当要将一个Collection进行分割的时候,需要指定一个shard key。shard key和索引key有些相像,它定义了在分布存储数据时,这一个或几个字段值的上限。这样,相邻的数据都会存储在满足key值范围的节点上。mongodb的shard key是需要你提前预设的,在congfig数据库中,你可以找到所有shard key的元数据。
MongoDB集群 – chunks chunks指的是在某一个collection中的一系列相邻的数据。可以从三方面去描述它:collection、minkey、maxkey。假设一个document的shard key是K,那么只有当minkey <= K < maxkey的时候,这个document才会被分配到这个chunk中来。 假如,一个chunk的最大size是200M,当超过这个值时,它会分裂出两个100M的chunk来,当一个shard节点存储了过多的数据时,那么一些chunks将被迁移到其他的shard节点上。(新加入节点也会引起chunk的迁移)
MongoDB集群 – shard nodes 每一个shard 都是由一个或者多个存储着数据的mongod server组成的。对于生产环境而言,它可以由replica-set来组成,以提供高可用性,保证无单点故障。mongodb shard集群需要至少两个的shard节点。 replica-set基于异步的replication技术,由几台(2-7)replication的节点组成。在集群初始化时,集群的几个节点会进行投票,决定谁做master,其余的作为slave去复制写入master的数据。一个replica-set中只有master是可写的,保证了数据的一致性。当一个master宕掉的时候,内部的投票机制会重新选举出一个新的master出来提供服务。当原来宕掉的节点恢复的时候,只要启动mongd进程(带有replica-set相应参数)就可以自动地加入集群,并且作为一个slave去提供备障和分摊读压力(可选项)。 vote vote vote
MongoDB集群 – config server Config server的config库中存储了集群的元数据,包括每个shard server和 chunks的基本信息。当然,主要的还是chunks的信息。每个config server完全地拷贝了所有chunks的信息。 当任何一个Config server宕掉的时候,那么这些元数据都将变为只读,但是这并不影响数据节点的读写操作。mongodb shard集群需要至少一个config server。 > use config switched to db config > show collections chunks collections databases lockpings locks mongos settings shards system.indexes Version
MongoDB集群 – routine server 你可以把mongos程序看做是一个路由,它可以使各种不同的组件一起协同工作,看起来就像是一个单独的系统,这有点像mysql的proxy。当客户端发送一条查询,mongos会依据config server的信息,把这条查询分配到相应的shard节点上,再将查询结果整合起来,一起返回客户端。你可以开启任意数量的mongos对外提供服务。
概述
Hash table Hash Function http://en.wikipedia.org/wiki/Hash_table 00 01 521-9655 02 521-1234 03 … 13 14 521-8976 15 00 01 521-9655 02 521-1234 03 … 13 14 521-8976 15 00 01 521-9655 02 521-1234 03 … 13 14 521-8976 15 00 01 521-9655 02 521-1234 03 … 13 14 521-8976 15 Tom Jerry Smith Key Value Tom 521-1234 Jerry 521-8976 … Smith 521-9655
Separate chaining 在separate chaining策略中,每一个bucket array的插槽都是一个指向链表的指针。每个链表包含了所有hash到同一个值的key的key-value对。 查找操作需要去扫描这个列表中和给出的key相对应的条目。 插入操作需要在这个列表的结尾处增加新的条目。 删除则需要找到并且从列表中移除这个条目。
B+Tree 主干 分枝 叶节点 存储数据 内部节点 存储key Key Value 1 d1 2 d2 3 d3 4 d4 5 d5 6 7 d7
Doubly-linked list 双向链表是一种数据记录集被连接起来的数据结构。每条记录都包含了两个连接字段,里面包含了对序列中前一条和后面一条的记录的引用。它可以被视为同一数据对象的两个方向相反的单向链表。 双向链表 单向链表 连接字段中存储的引用通常是指针;但也可能是目标所在地址的偏移值或数组中的索引。