MongoDB学习08–驱动实践

驱动实践

  1. 目前驱动有两种:
    1. 官方驱动:
      1. 官方页面:https://github.com/mongodb;
      2. java驱动下载:https://github.com/mongodb/mongo-java-driver/downloads;
      3. mongodb主页:http://docs.mongodb.org/ecosystem/drivers/;
    2. samus驱动:
      1. 官方页面:https://github.com/samus;
      2. c#驱动下载: https://github.com/samus/mongodb-csharp/downloads;
  2. 有时间使用java写一下CRUD的例子;

MongoDB学习07–运维技术

运维技术

  1. 常见的运维技术:
    1. 安装部署;
    2. 状态监控;
    3. 安装认证;
    4. 备份与恢复;
  2. 安装部署:
    1. mongod进程总是停留在命令行窗口下,很容易误操作给结束掉;而且日志信息总是在命令行下打印,不便于之后的查看,可以把mongod作为后台进程运行,并把日志输出到文件中;
    2. Linux中:mongod –dbpath=/mongo/data –logpath=/mongo/logs/mongod.log –logappend –port=27017 –fork;                         
    3. Windows中:mongod –dbpath=D:\mongo\data –logpath=D:\mongo\logs\mongod.log –logappend –port=27017 –install;之后就可以通过[net start/stop MongoDB]命令来开启和关闭mongodb数据库了;
    4. Linux下开机启动,可以编辑/etc/rc.d/rc.local文件,把mongod启动的脚本添加进去即可;
    5. Linux下关闭mongod服务器:
      1. 如果没有使用–fork命令,则直接退出终端即可,mongodb会自动清理;
      2. 如果使用了–fork命令,在admin数据库下执行:db.shutdownServer();
      3. 如果在Master-Slave集群中,如果备机和主机时间相差超过10s的话不会关闭mongod,可以通过配置timeoutSecs来完成mongod的数据更新,时间差在10s以内的话就可以关闭了:db.shutdownServer({force : true, timeoutsec : 5});
  3. 监控状态:
    1. http监控器,通过页面打开,网页使用端口是mongod服务端口加1000:http://mongod-host:port;
    2. 打开客户端调用db.serverStatus()获得服务器的状态,包括服务器基本信息,锁,索引,内存,连接,游标,网络和用户操作行为等信息;
    3. 使用客户端工具mongostat来实时查看统计信息(具体的字段含义可以通过mongostat -h查看),每秒刷新一次:mongostat –port=27017 –all;                           
  4. 安全认证:
    1. mongodb身份验证的机制:
      1. 当启动mongod服务的时候不加–auth参数,默认是没有权限验证的,可以直接登陆数据库做任何操作而且可以远程访问数据库,相当于都是超级用户的权限;
      2. 当启动mongod服务的时候添加–auth参数,如果没有在admin数据库中添加过用户(即admin.system.users不存在或者为空),那么还是可以做任何操作,直到在admin.system.users中添加了一个用户(如果没有在admin.system.users中添加用户,而在其它数据库下添加了用户,它们依然是超级用户的权限,因为只有admin.system.users中有用户后mongod的认证授权服务才生效);
      3. admin.system.users中保存的用户都是超级用户,可以对其它的数据库进行操作,而其它数据库中的用户只能对自己数据库中的集合(不论是哪个用户创建的集合)进行操作;
      4. mongodb中,数据库是由超级用户来创建的,一个数据库可以包含多个用户,但是一个用户只能在一个数据库中操作,不同的数据库中的用户可以同名;
    2. 创建用户的语法:db.addUser(username, password[, readOnly=false]);
      1. username:用户名;
      2. password:访问的密码;
      3. readOnly:默认是false,即有读写的权限,如果设置为true的话,则只有读的权限;
    3. 创建超级用户:mongo –port=27017 admin;db.addUser(“root”, “mongo”);                                           
    4. 验证用户:
      1. 可以在登陆时指定用户名密码:mongo 127.0.0.1:27017/admin -uroot -pmongo;                                
      2. 登陆之后使用db.auth()函数验证用户:db.auth(“root”, “mongo”),返回1表示成功,返回0表示失败;                      
    5. 查看用户:db.getCollection(‘system.users’).find();                               
    6. 删除用户:db.removeUser(“root”),也可以使用db.system.users.remove()操作,如果删除所有admin.system.users中的用户,则就没有安全验证了;                                    
    7. 其它数据库中创建用户的做法相同;
  5. 备份与恢复:
    1. 冷备份:在关闭服务器的情况下直接拷贝mongodb的数据目录,不推荐;
    2. mongodump和mongorestore:mongodb提供的内置工具,提供了热备功能,备份的父目录为/mongo/backup;
      1. mongodump:mongodump -h 127.0.0.1 –port 27017 -d test -o /mongo/backup;                      
      2. mongorestore:mongorestore -h 127.0.0.1 –port 27017 -d test -drop /mongo/backup/test;(–drop选项是指定导入数据之前先删除原来的数据)                             
      3. 在导出的时候可能数据的实时性不能保证,因为导出的时候可能数据还在内存中,可以使用mongodb提供的fsync+lock机制把缓冲区数据暴力刷入硬盘,然后给数据库一个写入锁,所有的写入都会被阻塞,直到fsync+lock释放锁为止;
        1. 加锁:db.runCommand({“fsync”:1, “lock”:1});
        2. 释放锁:db.$cmd.unlock.findOne();
    3. Master-Slave架构;

MongoDB学习06–分片技术

分片技术

  1. 当数据量达到T级别的时候,对CPU,内存和磁盘的压力都很大,这个时候就需要采用分片技术,将MongoDB中的集合进行拆分,分担到多个片(即多台MongoDB服务器)上;
  2. 跨服务器的数据拆分中,Sharding是一个有效的方法;MongoDB中支持自动化Sharding,但是对数据库性能会造成很大影响;因此建议用户尽早进行Sharding,使用MMS:Munin (+ Mongo plugin)和CloudWatch等工具对MongoDB进行监控,确保系统资源使用达到80%之前就完成Sharding工作;
  3. 分片技术中各个组件的职能及拓扑图:
    1. clients:客户端,即发出数据请求,并得到返回结果;
    2. config:保存数据和片的对应关系以及相应的配置信息,配置节点推荐使用使用主备,防止单点故障;
    3. mongos:即路由服务器,根据设置的片键(即集合拆分的依据)将数据分摊到不同的mongod集群;
    4. mongod:普通的MongoDB服务器,就是所谓的片;
    5. 拓扑图;                                                                                                 
  4. 使用不同的目录和端口来模拟不同的服务器:
    1. config:mongod –dbpath=/mongo/config –port=20001;
    2. mongos:mongos –port=20002 –configdb=127.0.0.1:20001;
    3. mongod1:mongod –dbpath=/mongo/mongod1 –port=30001;
    4. mongod2:mongod –dbpath=/mongo/mongod2 –port=30002;
    5. mongod3:mongod –dbpath=/mongo/mongod3 –port=30003;
  5. 开启config服务器:mongod –dbpath=/mongo/config –port=20001;                             
  6. 启动mongos服务器(不需要指定数据目录,因为它只是起到路由器的功能),同时要指定config服务器,因为要写入配置信息:mongos –port=20002 –configdb=127.0.0.1:20001;                               
  7. 分别启动三台mongod服务器:mongod –dbpath=/mongo/mongodX –port=3000X;
  8. 配置mongos服务:
    1. 连接mongos服务并添加分片节点:mongo 127.0.0.1:20002/admin;                                
    2. 开启数据库的分片功能,以test数据库为例:db.runCommand({“enablesharding”:”test”});                                  
    3. 指定集合的分片键,以user集合的name列为例:                                             
  9. 查看效果:
    1. 通过mongos插入10w条测试数据;                                         
    2. 查看数据的分布情况:db.printShardingStatus();                                
    3. 结果分析:
      1. shards:查看到已经分成了shard0000,shard0001,shard0002三个片;
      2. databases:看到test的partitioned属性为true;
      3. chundks:被分成了$minKey(无穷小)->user0, user0->user9999, user9999->$maxKey(无穷大)三段;
———————— 配置mongos服务器 ————————
— 1.连接mongos服务器;
mongo 127.0.0.1:20002/admin
— 2.添加分片;
db.runCommand({addshard:”127.0.0.1:30001″, allowLocal:true})
db.runCommand({addshard:”127.0.0.1:30002″, allowLocal:true})
db.runCommand({addshard:”127.0.0.1:30003″, allowLocal:true})
— 3.开启数据库的分片功能;
db.runCommand({“enablesharding”:”test”})
— 4.指定片键;
db.runCommand({“shardcollection”:”test.user”, “key”:{“name”:1}})
———————— 配置mongos服务器 ————————
———————— 插入测试数据并查看结果 ————————
— 1.连接mongos服务器的test数据库;
mongo 127.0.0.1:20002/test
— 2.插入10w条测试数据;
for(var i = 0; i< 100000; i++){ db.user.insert({name:”user”+i, age:i}) }
— 3.查看数据分布情况;
db.printShardingStatus()
———————— 插入测试数据并查看结果 ————————

MongoDB学习05–Master-Slave架构及副本集

Master-Slave架构及副本集

  1. Master-Slave架构的优点及拓扑图:
    1. 主要的优点:
      1. 解决单点故障问题;
      2. 实现数据的备份和恢复;
      3. 实现数据的读写分离;
    2. 拓扑图;                                                                                                              
    3. 使用不同的目录和端口来模拟不同的服务器:
      1. master:mongod –dbpath=/mongo/master –port=27017 –master;
      2. slave1:mongod –dbpath=/mongo/slave1 –port=30001 –slave –source=127.0.0.1:27017;
      3. slave2:mongod –dbpath=/mongo/slave2 –port=30002 –slave;
  2. 启动Master数据库:mongod –dbpath=/mongo/master –port=27017 –master,在master和slave的服务器上都会有一个local的集合,用来存放内部的复制信息;                      
  3. 配置Salve1数据库,并在启动时就与Master数据库保持同步:mongod –dbpath=/mongo/slave1 –port=30001 –slave –source=127.0.0.1:27017,会看到有一个rep1的进程从Master服务器同步数据;           
  4. 把原来的Slave2服务器添加到Master-Slave架构中:
    1. 首先启动一台Slave2服务器,并不指定Master源:mongod –dbpath=/mongo/slave2 –port=30002 –slave;                               
    2. 连接到Slave2服务器,在local数据库下添加Master服务器信息,然后查看,同步完成后会出现同步的时间戳;                                    
    3. 可以通过观察系统日志或者查看集合的记录数来查看同步的状况;
  5. 查看Master, Slave的状态:
    1. 查看master服务器的状态:db.printReplicationInfo();                     
    2. 查看slave服务器的状态:db.printReplicationInfo();                           
  6. 读写分离:
    1. 默认情况下Salve服务器不支持数据的读取,可以通过驱动中的slaveOkay来显示读取Slave数据库,从而减轻Master服务器的压力;
    2. 也可以让前端程序直接连接到Slave服务器读取数据;
  7. 副本集:
    1. 与Master-Slave不同的是,它没有特定的主数据库,如果哪个主数据库宕机了,集群中就会推选出一个从属的数据库作为主数据库,具有自动故障恢复功能,所以一定要保证副本集为奇数个,否则会发生主节点宕机,其它节点为只读的情况;
    2. 创建一个名为snda的集群;
    3. 使用不同的服务器和端口来模拟不同的服务器:
      1. rep1:mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003;
      2. rep2:mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003;
      3. rep3:mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
      4. arbriter:mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;
  8. 打开第一个副本服务器rep1:mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003,replSet参数表示让服务器知道副本集snda下还有其它的服务器;     
  9. 打开第二个副本服务器rep2:mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003,会与其它的副本进行通信,并读取副本集的配置;             
  10. 同样的方法打开第三个副本集rep3:mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
  11. 添加副本集的配置信息,在任意一个副本上的admin数据库进行配置,这里选择rep1:db.runCommand({replSetInitiate:{_id:”snda”, members:[{_id:1, host:”127.0.0.1:30001″}, {_id:2, host:”127.0.0.1:30002″}, {_id:3, host:”127.0.0.1:30003″}]}});                      
  12. 此时可以从日志看出哪一个副本已经成为了Primary服务器,哪一个副本是Secondary服务器,可以看到rep2和rep3都投票给rep1作为primary服务器;当然也可以通过查看状态查询:rs.status();               
  13. 启动仲裁服务器:mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;                            
  14. 添加仲裁服务器配置,可以通过snda副本集中任意一个副本的admin数据库添加,这里以rep1为例:rs.addArb(“127.0.0.1:30000”);                         
  15. 查看副本集群中各个服务器的状态:rs.status();                                          
  16. 测试自动故障恢复功能,关掉当前的主服务器,之后通过rs.status()查看状态,发现自动投票选择了新的主服务器;                                        
  17. 当rep1重新修复后加入集群,自动成为了Secondary服务器;
—————————– 已开启的Slave服务器加入到Master-Slave架构中 —————————–
mongo 127.0.0.1:30002/local
db.sources.insert({host:”127.0.0.1:27017″})
db.sources.find()
db.sources.find()
—————————– 已开启的Slave服务器加入到Master-Slave架构中 —————————–
—————————– MongoDB副本集的配置 —————————–
— 1.添加三个副本,并互相告知剩余的成员;
mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003;
mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003;
mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
— 2.添加副本集的配置信息;
mongo 127.0.0.1:30001/admin
db.runCommand({replSetInitiate:{_id:”snda”, members:[{_id:1, host:”127.0.0.1:30001″}, {_id:2, host:”127.0.0.1:30002″}, {_id:3, host:”127.0.0.1:30003″}]}})
— 3.启动仲裁服务器;
mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;
— 4.添加仲裁服务器的配置信息;
mongo 127.0.0.1:30001/admin
rs.addArb(“127.0.0.1:30000”)
— 5.查看副本集各个成员的状态;
rs.status()
—————————– MongoDB副本集的配置 —————————–

MongoDB学习04–性能分析及索引操作

MongoDB中的性能分析及索引操作

  1. 性能分析函数explain();
    1. 语法:cursor.explain(verbose),verbose为true或者1的时候会输出所有的执行计划和旧的执行计划;
    2. 一般是跟在find()操作后使用(eg:db.mycoll.find().explain()),它的执行速度决定于实际查询的速度,尽管explain()会额外生成一系列候选的执行计划,但是跟实际查询的性能相差不大;
    3. explain函数的输出结果;                                                          
    4. explain()函数输出结果含义:
      1. cursor:查找使用的cursor类型和索引名称;
      2. isMultiKey:是否是联合索引;
      3. n:返回记录的个数;
      4. nscannedObjects:遍历的文档对象的个数;
      5. nscanned:遍历的文档的个数;
      6. nscannedObjectsAllPlans: <num>;
      7. nscannedAllPlans: <num>;
      8. scanAndOrder: <boolean>;
      9. indexOnly: <boolean>;
      10. nYields: <num>;
      11. nChunkSkips: <num>;
      12. millis:消耗的时间,单位是ms;
      13. indexBounds:索引的范围,如果为空表示没有用到索引;
      14. llPlans:产生的所有的候选的执行计划;
      15. oldPlan:旧的执行计划;
      16. server:主机名和端口号<host:port>;
  2. 创建/删除索引,及前后效率的对比;
    1. 创建索引语法:db.mycoll.ensureIndex(keypattern[,options]);
      1. keypattern:是索引的键;
      2. options:可以取name,unique,dropDups,sparse等值;
      3. 只有在某个列上没有索引才会创建,如果存在索引的话就不再创建;
      4. 默认创建的是b-tree索引;
      5. 可以在子文档上创建索引:db.mycoll.ensureIndex({“object.attritude”:1});
    2. 没有创建索引时,查找name=index10000的效率:db.index.find({“name”:”index10000″}).explain();使用的是BasicCursor即没有索引,扫描了10w条记录后返回1条记录,消耗了192ms;              
    3. 在name列创建一个名为idx_index_name的索引:db.index.ensureIndex({name:1}, {name:”idx_index_name”}),如果不指定第二个参数中的name的话,则系统会自动生成一个索引的名称;          
    4. 查看创建索引后的效率,发现使用了Btree索引,一共扫描一条记录,返回1条记录,并且瞬间返回;                                            
    5. 删除索引语法:db.mycoll.dropIndex(index),可以根据名称删除索引,或者根据创建时指定的键值;
      1. db.mycoll.dropIndex(“indexName”):根据索引名称删除;
      2. db.mycoll.dropIndex({ “indexKey” : 1 }):根据key值删除索引;
      3. 如果要删除某个集合下所有的索引,则使用db.mycoll.dropIndexes(),但是无法删除系统自动创建的基于_id列的索引,它由数据库自己维护;
    6. 重建索引:db.mycoll.reIndex(),是把原来的索引删掉后重建;                                                        
  3. 唯一索引:
    1. 语法:db.mycoll.ensureIndex(keypattern, {unique:true});
    2. 创建一个基于name列的唯一索引unq_index_name:db.index.ensureIndex({name:1}, {name:”unq_index_name”, unique:true});
    3. 如果某一个列上已经存在的重复值,在这样的列上创建唯一索引的话可以使用dropDups选项,这样系统会保留第一条记录,删除剩下重复的记录:db.mycoll.ensureIndex(keypattern, {unique:true, dropDups:true});
    4. 如果某一个列是唯一列,插入数据时没有插入此列的话会保存一个null值,一个唯一列中只能有一个null值;(这点与关系型数据库不同,关系型数据库中唯一列中可以保存null值,但是在mongodb中null值作为了一个值来比较)
  4. 组合索引:
    1. 语法与普通索引相同,只是需要多指定几个列:db.mycoll.ensureIndex({key1:value1, key2:value2, …});
    2. 键后面的数字表示索引的存储顺序,其中1表示升序,-1表示降序.在随机访问的时候意义不大,但是在排序或者是范围查询时很重要;
    3. 如果在a,b,c三列上创建了这索引,则以a,ab,abc开头的条件都可以使用到此索引;
  5. 稀疏索引:
    1. Sparse indexes only contain entries for documents that have the indexes field, any document that is missing the field is not indexes;
    2. By contrast, non-sparse indexes contrain all documents in collection, and store null values for documents that do not contrain the indexes field;
    3. 语法:db.mycoll.ensureIndex(keypattern, {sparse:true});
    4. 稀疏索引与非稀疏索引的对比;
  6. hint:
    1. 查询优化器会选择最优的执行计划,而且第一次执行后的执行计划将会被保存;如果需要使用自己指定的查询方案的话可以使用hint;
    2. 语法:cursor.hint(index);
      1. db.mycoll.find().hint(“index_name”):通过指定索引名称来实现hint;
      2. db.mycoll.find().hint({key:value}):通过指定创建索引时的key的顺序来实现hint,可以之前通过db.mycoll.getIndexes()查看;
    3. 查看hint的性能:db.mycoll.find().hint(index).explain();
  7. 注意事项:
    1. MongoDB中的索引是大小写敏感的;
    2. 当更新对象时,只有在索引上的这些key发生变化才更新,所以提高了性能;当对象增长了或者移动时,所有的索引都必须更新,会很慢;
    3. 索引的信息会保存在system.indexes集合中,运行db.system.indexes.find()可以查看这些数据;
    4. 索引的字段的大小限制目前是800bytes,大于之后可以在这个字段上创建索引,但是该字段不会被索引;
    5. 如果数据集合比较小(4M),可以联合使用limit()和sort()函数而不需要创建索引来查询数据;
———————- 10w条测试数据 ———————-
db.index.remove()
for(var i = 0; i < 100000; i++){
     db.index.insert({name:”index”+i, age:i})
}
———————- 10w条测试数据 ———————-
———————- 稀疏索引与非稀疏索引的对比 ———————-
— 1.测试数据;
db.sparse.insert({name:”sparse1″, age:20})
db.sparse.insert({name:”sparse2″})
db.sparse.find()
— 2.在age列创建一个稀疏索引;
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”, sparse:true})
db.sparse.getIndexes()
— 3.查看数据,稀疏索引中不包含列为null的文档,只有稀疏索引中的文档才会被返回;
db.sparse.find()
db.sparse.find().sort({age:1})
db.sparse.find({name:{$ne:0}})
— 4.删除稀疏索引,并创建一般的索引;
db.sparse.dropIndex(“idx_sparse_age”)
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”})
db.sparse.getIndexes()
— 查看数据,非稀疏索引中包含了列为null的文档;
———————- 稀疏索引与非稀疏索引的对比 ———————-

MongoDB学习03–聚合操作,游标的使用及排序分页操作

聚合操作,游标的使用及排序分页操作

  1. 聚合操作:
    1. count:查看符合某些条件的集合中记录的个数;
      1. 查看集合user的记录数:db.user.count();                                        
      2. 查看集合user中年龄大于20的记录的个数:db.user.count({“age”:{$gt:20}});                                  
    2. distinct:查看集合中某个属性的独立存在的个数;
      1. 获得不同年龄的数组:db.user.distinct(“age”);                               
      2. 获得不同年龄值的个数:db.user.distinct(“age”).length;                           
    3. group:对集合按照某一属性分组,形成一个K-V模型;
      1. 语法:db.mycoll.group( { key : …, initial: …, reduce : … [, finalize: …] [, condition: …] } );
        1. key:分组的键;
        2. initial:每个分组使用的初始化函数;
        3. reduce:此函数的第一个参数是当前的文档对象,第二个参数是上次函数(即initial函数)操作的累计对象,有多少个文档,$reduce就会调用多少次;
        4. finalize:每一组文档执行完之后,会触发执行的一个函数,参数是上次函数(即initial函数)操作的累计对象;
        5. condtion:过滤条件;
      2. 按照年龄分组的例子:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}}),push函数是把一个值加入到数组中;       
      3. 按照年龄分组,过滤掉年龄大于25的记录,并且添加一个用户数量的属性count:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}, finalize:function(prev){prev.count=prev.user.length;}, condition:{age:{$lte:25}}});                               
    4. mapReduce:其实是一种编程模型,用在分布式计算中.可以把问题划分为多个不同的部分并分发到不同的服务器并行处理,每台服务器都把分配给自己的一部分处理完成后把结果集返回给主服务器,主服务器汇总结果后完成问题的处理;
      1. 语法:db.mycoll.mapReduce( mapFunction , reduceFunction , <optional params> );
        1. mapFunction:映射函数,里面会调用emit(key, value),集合会按照指定的key进行映射分组;
        2. reduceFunction:;简化函数,会对map分组后的数据进行分组简化.其中reduce(key, value)中的key就是emit中的key,value为emit分组后的emit(value)的集合,它有一个或者多个对应于键的文档组成;
        3. 可选参数;
      2. 原理:map首先将文档映射到集合并操作文档,这一步可能产生多个键和多个值或者什么也没有(文档中要处理的值为空).而后按照键分组,并将产生的值组成列表放到对应的键中,reduce则把列表中的值化简为一个值,这个值被返回,而后继续按键分组,进行化简,直到每个键在列表中只有一个值为止,这个值也就是最终结果;
      3. 例子;
  2. 游标的使用:
    1. 使用while循环遍历输出:var cursor=db.user.find(); while(cursor.hasNext()) { printjson(cursor.next()); };(其中hasNext()函数判断是否有记录,next()函数返回下一条记录)             
    2. pringjson()函数是内置函数,能够将结果输出为json格式;
    3. 得到数据集集合,遍历完之后游标便销毁;
    4. 使用forEach()函数遍历:db.user.find().forEach(printjson);                          
    5. 像访问数组一样使用游标:var cursor=db.user.find();printjson(cursor[0]);(会把访问的数据都加载ram中,如果数据量很大的话非常消耗内存)                
    6. 直接转换成数组访问:var array=db.user.find().toArray();array[0];                            
  3. 排序操作:
    1. 语法:db.mycoll.find().sort({col:value, …});
      1. col:表示按照排序的列;
      2. value:可以取1和-1,1表示升序,-1表示降序;
      3. 查找所有记录,并按照年龄升序,名称降序显示:db.user.find().sort({“age”:1, “name”:-1}),select name, age from user order by age desc, name asc;              
    2. 还可以通过其它方法实现,db.mycoll.find([query], [fields])操作是根据query过滤记录,先后显示fields里面指定的列,如果query给默认的话({}就是默认的参数),只需要指定列名及排序方式即可:select name, age from user order by age desc, name asc;                                 
    3. 先按年龄升序,然后按照名称升序排序:select name from user order by age asc, name asc;                      
  4. 分页操作:
    1. 使用db.mycoll.find().skip(n)操作和db.mycoll.find().limit(n)实现,前者是跳过之前多少条记录,后者是显示多少条记录:skip((pageIndex-1) * pageSize).limit(pageSize);
    2. 排序后的结果,每页两条记录,显示第二页:db.user.find().sort({“age”:1, “name”:-1}).skip(2).limit(2);                 
— 测试数据;
db.user.drop()
db.user.insert({“name”:”jack”, “age”:20})
db.user.insert({“name”:”joe”, “age”:22})
db.user.insert({“name”:”mary”, “age”:26})
db.user.insert({“name”:”kobe”, “age”:20})
db.user.insert({“name”:”wade”, “age”:22})
db.user.insert({“name”:”yi”, “age”:22})
db.user.find()
— mapReduce操作;
1.创建map函数;
function map(){ emit(this.age, {count:1}); }
2.创建reduce函数;
function reduce(key, value){
     var result = {count:0};
     for (var i = 0; i < value.length; i++){
          result.count += value[i].count;
     }
     return result;
}
3.执行mapReduce操作,并把结果集存放在集合collection中;
db.user.mapReduce(map, reduce, {out:”collection”})
result:存放集合名;
timeMillis:执行的时间,单位是毫秒;
input:传入文档的个数;
emit:emit函数被调用的次数;
reduce:reduce函数被调用的次数;
output:最后返回文档的个数;
4.查看collection中的结果;
db.collection.find()

MongoDB学习02–增删改查操作

MongoDB的增删改查操作

  1. INSERT操作:
    1. 单条插入(可以使用js的语法);                                          
    2. 批量插入:mongodb中并没有提供批量插入的语法,但是高级语言中提供了与mongodb批量插入的接口,也可以通过for循环来模拟;
    3. insert和save的区别:
      1. 如果不使用_id列,两个函数没有区别都是插入数据;
      2. 如果使用了_id列,insert插入_id重复的话会报错;save插入_id重复的话会调用upsert,即存在就更新,不存在就插入;
  2. FIND操作:
    1. 根据条件查询:[>, >=, <, <=, !=, =]分别对应[$gt, $gte, $lt, $lte, $ne, 无]关键字;                    
    2. 多条件查询:[and, or, in, notin]分别对应[无, $or, $in, $nin]关键字;                      
    3. 正则表达式查询;                                                       
    4. 使用$where子句查询;                                                   
    5. findOne()函数返回满足条件的第一条记录或者是null:printjson(db.user.findOne({name:”joe”}));                                 
  3. UPDATE操作:
    1. 整体更新;                                                            
    2. 局部更新,使用修改器$inc和$set来更新某一个字段;
      1. $inc:increase的缩写,即在原来值的基础上增加$inc指定的值,可以用于自增的主键;                                
      2. $set:即设置某个列的值为$set指定的值;                                         
    3. upsert操作:update操作的第一个参数是查询条件,第二个参数是要更新的值,如果设置第三个参数为true的话,就表示如果存在记录就更新,如果不存在就插入;                           
    4. multi操作:批量更新,如果update操作匹配到了多条记录,默认情况下只更新第一条,如果需要全部更新的话,需要把update操作中第四个参数设置为true;                           
  4. REMOVE操作:
    1. 根据条件删除:db.mycoll.remove(query);                                       
    2. 全部删除:db.mycoll.remove();                                                        
— insert操作;
var user={“name”:”jack”, “password”:”12345″, “age”:20, “address”:{“province”:”shanghai”, “city”:”shanghai”}, “favourite”:[“apple”, “banana”]}
db.user.insert(user);
user.name=”joe”
user.age=21
user.address={“province”:”beijing”, “city”:”beijing”}
user.favourite=[“sanguosha”, “dota”]
db.user.insert(user)
db.user.find()
— find操作;
1.查找年龄大于等于21岁的集合;
db.user.find({“age”:{$gte:21}})
2.查找年龄等于20岁的集合;
db.user.find({“age”:20})
3.查找名称为jack而且省份为shanghai的集合:name=’jack’ and province=’shanghai’;
db.user.find({“name”:”jack”, “address.province”:”shanghai”})
4.查找省份为上海或者北京的集合:province=’shanghai’ or province=’beijing’;
db.user.find({$or:[{“address.province”:”shanghai”}, {“address.province”: “beijing”}]})
5.查找省份在上海或者北京的集合:province in (‘shanghai’, ‘beijing’);
db.user.find({“address.province”:{$in:[“shanghai”, “beijing”]}})
6.查找省份不在上海的集合:province not in (‘shanghai’);
db.user.find({“address.province”:{$nin:[“shanghai”]}})
7.查找姓名以’j’开头并且以’e’结尾的集合:name like ‘j%e’;
db.user.find({“name”:/^j/, “name”:/e$/})
8.条件复杂的时候,可以使用$where子句,其实就是js的函数,查找名称为’jack’的集合:name=’jack’;
db.user.find({$where:function(){return this.name == ‘jack’}})
— update操作;
1.整体更新一条记录;
db.user.find({“name”:”jack”})
var model=db.user.findOne({“name”:”jack”})
model.age=30
db.user.update({“name”:”jack”}, model)
db.user.find({“name”:”jack”})
2.使用$inc修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$inc:{“age”:-5}})
db.user.find({“name”:”jack”})
3.使用$set修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$set:{“age”:20}})
db.user.find({“name”:”jack”})
4.upsert操作:查找到就更新,没有就插入;
db.user.find({“name”:”kobe”})
db.user.update({“name”:”kobe”}, {$set:{“age”:20}}, true)
db.user.find({“name”:”kobe”})
5.multi操作:批量更新记录;
db.user.find()
db.user.update({“age”:{$gte:20}}, {$inc:{“age”:5}}, false, true)
db.user.find()
— remove操作;
1.根据条件删除;
db.user.find()
db.user.remove({“name”:”kobe”})
db.user.find()
2.全部删除;
db.user.remove()
db.user.find()
db.user.count()

MongoDB学习01–Linux下安装MongoDB v2.2

Linux下安装MangoDB

  1. 官网地址:http://www.mongodb.org;
  2. 下载文件:从http://www.mongodb.org/downloads页面下载相应的版本,这里选择linux下32bit的2.2.4版本;
  3. 解压文件:tar -zxvf /tools/mongodb-linux-i686-2.2.4.tgz;
  4. 修改文件名,并移动到/usr/local目录下:mv mongodb-linux-i686-2.2.4 mongodb; cp -R  /tools/mongodb /usr/local/;
  5. 把mangodb的路径添加到path中:PATH=$PATH:/usr/local/mongodb/bin;
  6. 启动mongod服务并修改默认数据目录:
    1. mongodb默认的数据文件的路径是/data/db,需要预先创建,如果使用的是mongo用户的话,需要修改此目录的权限:chown mongo /data/db;
    2. 修改数据文件的路径:mkdir -p /mongo/data/journal;mongod –journal –dbpath /mongo/data/;(默认是不开启journal功能的,可以指定参数打开)                         
    3. 访问相应的端口查看信息:http://192.168.10.112:28017/;                                
  7. 使用配置文件方式启动mongod服务,每次启动都需要输入很长的参数比较麻烦,可以把参数写在/etc/mongod.conf文件中,然后采用:mongod -f /etc/mongod.conf的方式启动;             
  8. 登陆客户端,使用mongo命令,同时也是js的编辑器(可以使用一切js的语法),默认连接test数据库;(使用SecureCRT的话,命令总是会重复一次,推荐使用xshell)                                                   
  9. 获得帮助,使用help命令;                                                   
  10. 关闭mongod服务:mongod –dbpath /mongo/data –shutdown;                                           
  11. 如果非法关闭后再次打开时报错:mongod –journal –dbpath=/mongo/data,此时需要删除rm -rf /mongo/data/mongod.lock文件,然后再打开;                                        
  12. 对MongoDB的基本CRUD操作:
    1. 插入一个集合,是以bson(json的扩展)的形式插入的;                              
    2. find操作,_id字段是数据库默认加的guid,目的是保证数据的唯一性;                              
    3. update操作,第一个参数为查找的条件,第二个参数为更新的值;                                 
    4. remove操作,如果不加参数就会删除所有的数据,而且不能回滚;                                
  13. 官方给定的使用mongodb的一些建议:
    1. MongoDB分成32位版本和64位版本,由于MongoDB使用内存映射文件,所以32位版本只能存储2GB左右的数据;建议存储更多数据的用户使用64位版本;
    2. MongoDB是文档型数据库,数据以BSON形式存储在文档中;最新版本的MongoDB能够支持最大16MB的文档大小;建议用户尽量不要存储大型对象,将文档控制在1 MB以内;
    3. MongoDB的写入和更新速度非常快,所以错误提示并不明确;要确保写入正确,建议用户使用getLastError或者使用安全写入;
    4. 关系型数据库往往会有预定义的schema,你想添加额外的列就需要在整个表上添加;MongoDB没有这个约束,这使得开发和管理变得更简单;但这并不意味着你就可以完全忽视MongoDB的schema设计,一个设计良好的schema能够让MongoDB的性能达到最佳;
    5. MongoDB的更新在默认情况下会使用类似于传统数据库的LIMIT语句,即LIMIT 1.因此更新不会影响到所有的文档,如果你想要一次更新许多文档,那么请把multi设为true;
    6. MongoDB默认情况下是区分大小写的,例如db.people.find({name: ‘Russell’}) 和db.people.find({name: ‘russell’})就是不一样的;所以用户需要知道MongoDB的大小写限制;
    7. 传统数据库中,如果插入错误的数据类型,通常会提示错误或者强制转换成预定义的数据值;MongoDB中没有这种限制,所以输入错误数据类型不会出现提示;建议用户确保输入正确的数据类型;
    8. 全局锁是一直被MongoDB用户诟病的特性,MongoDB 2.2中增加了数据库级锁,这是一个很大的改进;建议用户使用稳定版的MongoDB 2.2数据库,避免全局锁限制;
    9. 过期版本MongoDB用户在下载程序包时会出问题,建议用户使用10gen最新版本的官方程序包;
    10. Replica Set是MongoDB中受关注最多的功能,它能为MongoDB集群增加冗余并提供良好的读性能;但由于Replica Set的选举机制,必须保证Replica Set成员数目为奇数;如果是偶数的话,主节点宕机就会导致其他节点变为只读;解决方法也可以使用一个仲裁节点(arbiter),它也是一个Replica Set的成员,但并不存储用户数据;所以请记住设置Replica Set成员时要定为奇数;
    11. MongoDB中不存在join,你要针对多个集合进行数据检索的时候,必须使用多个查询;所以当你遇到这个问题时,可以考虑重新设计MongoDB的schema;
    12. Journaling日志是MongoDB中非常好的功能,能够增强节点的可用性;在2.0版本之后,MongoDB默认是开启Journaling日志功能的;虽然Journaling日志会对数据库性能造成一定的影响,但这部分影响是可以忽略的;因此建议用户开启Journaling功能,特别是对于可用性要求较高的用户;
    13. MongoDB默认情况下是没有认证功能的,因此建议用户使用防火墙对MongoDB进行保护;
    14. Replica Set的工作是通过传送oplog来完成的,主节点发生故障后,新的数据将会存放在数据目录下的一个特定文件夹内,即rollback文件夹;你可以用来手动完成数据恢复;所以在每次故障发生之后,你一定要看看这个文件夹,MongoDB自带的工具就能够帮助你轻松地完成手动数据恢复;
    15. 跨服务器的数据拆分中,Sharding是一个有效的方法;MongoDB中支持自动化Sharding,但是对数据库性能会造成很大影响;因此建议用户尽早进行Sharding,使用MMS:Munin (+ Mongo plugin)和CloudWatch等工具对MongoDB进行监控,确保系统资源使用达到80%之前就完成Sharding工作;
    16. MongoDB使用shard key来决定特定的文档在哪个分片上,当插入一个文档之后,你是无法更新shard key的;这里建议用户删除文档并重新插入,这样就能够将其分配到合适的分片上;
    17. MongoDB对分片的限制还包括集合的大小,当超过256 GB的时候,MongoDB将不允许进行分片;相信10gen公司会在未来放弃这一限制,但在此之前用户需要留意;
    18. MongoDB中跨分片并没有强制要求唯一性,MongoDB只针对独立的分片进行强制而非全局性;当然除shard key之外;
    19. 进行拆分的时候,MongoDB会要求你选择一个键;用户需要注意选择正确的键,否则会造成不必要的麻烦;如何进行选择并无定式,主要取决于你的应用,比如针对news feed使用时间戳就是错的;在下一版本中,MongoDB将对此进行改进;
    20. MongoDB连接默认情况下是不加密的,也就是说你的数据是能够被第三方记录和使用的;所以你在公共网中访问MongoDB的话,就一定要进行加密;
    21. MongoDB只支持单一文档的原子性,这一点与传统的数据库有所不同,如MySQL.因此MongoDB中跨多个文档是不提供内置的transaction支持的;
    22. 当MongoDB显示ready的时候,其实内部还在进行journal的配置;因此针对速度较慢的文件系统,MongoDB的journal配置也会很慢;
    23. 不建议尝试NUMA + Linux + MongoDB的组合,如果你的MongoDB跑在NUMA服务器上,建议将它关掉;
    24. 在Linux上运行MongoDB遭遇segfault错误时,这主要是因为open files / process限制过低;建议用户将限制设定为4K+;
————————– 基本的增删改查操作 ————————–
— insert操作;
db.person.insert({“name”:”kobe”,”age”:32})
db.person.insert({“name”:”james”,”age”:28})
— find()操作;
db.person.find()
— update()操作;
db.person.update({“name”:”kobe”},{“name”:”kobe”,”age”:33})
db.person.find({“name”:”kobe”})
— delete()操作;
db.person.remove({“name”:”james”})
db.person.find()
db.person.count()
————————– 基本的增删改查操作 ————————–
————————– mongodb的配置文件/etc/mongod.conf ————————–
vi /etc/mongod.conf
port=27017
dbpath=/mongo/data
directoryperdb=true
logpath=/mongo/logs/mongod.log
logappend=true
auth=true
journal=true
fork=true
————————– mongodb的配置文件/etc/mongod.conf ————————–

分布式文档存储数据库–MongoDB

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:

  • 面向集合存储,易存储对象类型的数据。
  • 模式自由。
  • 支持动态查询。
  • 支持完全索引,包含内部对象。
  • 支持查询。
  • 支持复制和故障恢复。
  • 使用高效的二进制数据存储,包括大型对象(如视频等)。
  • 自动处理碎片,以支持云计算层次的扩展性
  • 支持RUBY,PYTHON,JAVA,C++,PHP等多种语言。
  • 文件存储格式为BSON(一种JSON的扩展)
  • 可通过网络访问

所谓“面向集合”(Collenction-Orented),意思是数据被分组存储在数据集中,被称为一个集合(Collenction)。每个 集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定 义任何模式(schema)。
模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。
存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各中复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized dOcument Format)。

MongoDB服务端可运行在Linux、Windows或OS X平台,支持32位和64位应用,默认端口为27017。推荐运行在64位平台,因为MongoDB在32位模式运行时支持的最大文件尺寸为2GB。

MongoDB把数据存储在文件中(默认路径为:/data/db),为提高效率使用内存映射文件进行管理。

NoSQL的现状

经过了至少4年的激烈争论,现在是对NoSQL的现状做一个阶段性结论的时候了。围绕着NoSQL发生了如此之多的事情,以至于很难对其作出一个简单概括,也很难判断它达到了什么目标以及在什么方面没有达到预期。

在很多领域,NoSQL不仅在行业内也在学术领域中取得了成功。大学开始认识到NoSQL必须要加入到课程中。只是反复讲解标准数据库已经不够了。当然,这不意味着深入学习关系型数据库是错误的。相反,NoSQL是很好的很重要的补充。

发生了什么?

NoSQL领域在短短的4到5年的时间里,爆炸性地产生了50到150个新的数据库。nosql-database.org列出了150个这样的数据库,包括一些像对象数据库这样很古老但很强大的。当然,一些有意思的合并正在发生,如CouchDB和Membase交易产生的CouchBase。但是我们稍后会在本文中讨论每一个主要的系统。

很多人都曾经假设在NoSQL领域会有一个巨大地整合。但是这并没有发生。NoSQL过去是爆炸性地增长,现在依旧如此。就像计算机科学中的所有领域一样——如编程语言——现在有越来越多的空白领域需要大量的数据库。这是与互联网、大数据、传感器以及将来很多技术的爆炸性增长同步的,这导致了更多的数据以及对它们进行处理的不同需求。在过去的四年中,我们只看到了一个重要的系统离开了舞台:德国的Graph数据库Sones。为数众多的NoSQL依然快乐地生存着,要么在开源社区,不用考虑任何的金钱回报,要么在商业领域。

可见性与金钱?

另外一个重要的方面就是可见性与行业采用的情况。在这个方面,我们可以看到在传统的行业中——要保护投资——与新兴的行业(主要是初创公司)之间有很大的差别。几乎所有热门的基于Web的创业公司如Pinterest和Instagram 都在使用混合式(SQL + NoSQL)的架构,而传统的行业依然纠结于是否采用NoSQL。但是观察显示,越来越多这样的公司正在试图将它们的一部分数据流用NoSQL方案进行处理并在以后进行分析,这样的方案包括Hadoop、MongoDB以及Cassandra等。

这同时导致了对具备NoSQL知识的架构师和开发人员的需求持续增长。最近的调查显示行业中最需要的开发人员技能如下:

  1. HTML5
  2. MongoDB
  3. iOS
  4. Android
  5. Mobile Apps
  6. Puppet
  7. Hadoop
  8. jQuery
  9. PaaS
  10. Social Media

在前十名的技术需求中,有两个NoSQL数据库。有一个甚至排在了iOS前面。如果这不是对它的赞扬,那是什么呢?!

但是,跟最初预计相比,对NoSQL的采用变得越来越快,越来越深入。在2011年夏天,Oracle曾经发布过一个著名白皮书,它提到NoSQL数据库感觉就像是冰淇淋的风味,但是你不应该过于依附它,因为它不会持续太长时间。但是仅仅在几个月之后,Oracle就展现了它们将Hadoop集成到大数据设备的方案。甚至,他们建立了自己的NoSQL数据库,那是对BerkeleyDB的修改。从此之后,所有的厂商在集成Hadoop方面展开了竞赛。Microsoft、Sybase、IBM、Greenplum、Pervasive以及很多的公司都已经对它有了紧密的集成。有一个模式随处可见:不能击败它,就拥抱它。

但是,关于NoSQL被广泛采用的另一个很重要但不被大家关注的重要信号就是NoSQL成为了一个PaaS标准。借助于众多NoSQL数据库的易安装和管理,像Redis和MongoDB这样的数据库可以在很多的PaaS服务中看到,如Cloud Foundry、OPENSHIFT、dotCloud、Jelastic等。随着所有的事情都在往云上迁移,NoSQL会对传统的关系型数据库产生很大的压力。例如当面临选择MySQL/PostGres或MongoDB/Redis时,将会强制人们再三考虑他们的模型、需求以及随之而来的其他重要问题。

另外一个很有意思的技术指示器就是ThoughtWorks的技术雷达,即便你可能不完全同意它所包含的所有事情,但它总会包含一些有意思的事情。让我们看一下他们2012年10月份的技术雷达,如图1:

图1:ThoughtWorks技术雷达,2012年10月——平台

在他们的平台象限中,列出了5个数据库:

  1. Neo4j (采用)
  2. MongoDB(试用阶段但是采用)
  3. Riak(试用)
  4. CouchBase(试用)
  5. Datomic(评估)

你会发现它们中至少有四个获得了很多的风险投资。如果你将NoSQL领域的所有风险投资加起来,结果肯定是在一亿和十亿美元之间!Neo4j就是一个例子,它在一系列的B类资助中得到了一千一百万美元。其他得到一千万到三千万之间资助的公司是Aerospike、Cloudera、DataStax、MongoDB以及CouchBase等。但是,让我们再看一下这个列表:Neo4j、MongoDB、Riak以及CouchBase已经在这个领域超过四年了并且在不断地证明它们是特定需求的市场领导者。第五名的数据库——Datomic——是一个令人惊讶的全新数据库,它是由一个小团队按照全新的范式编写的。这一定是很热门的东西,在后面简要讨论所有数据库的时候,我们更更深入地了解它们。

标准

已经有很多人要求NoSQL标准了,但他们没有看到NoSQL涵盖了一个范围如此之大的模型和需求。所以,适用于所有主要领域的统一语言如Wide Column、Key/Value、Document和Graph数据库肯定不会持续很长时间,因为它不可能涵盖所有的领域。有一些方式,如Spring Data,试图建立一个统一层,但这取决于读者来测试这一层在构建多持久化环境时是不是一个飞跃。

大多数的Graph和Document数据库在它们的领域中已经提出了标准。在Graph数据库世界,因为它的tinkerpop blueprints、Gremlin、Sparql以及Cypher使得它更为成功一些。在Document数据库领域,UnQL和jaql填补了一些位置,尽管前者缺少现实世界NoSQL数据库的支持。但是借助Hadoop的力量,很多项目正在将著名的ETL语言如Pig和Hive使用到其他NoSQL数据库中。所以标准世界是高度分裂的,但这只是因为NoSQL是一个范围很广的领域。

格局

作为最好的数据库格局图之一,是由451 Group的Matt Aslett在一个报告中给出的。最近,他更新了该图片从而能够让我们可以更好得深入理解他所提到的分类。你可以在下面的图片中看到,这个格局是高度碎片化和重叠的:

(点击图片放大)

图2:Matt Aslett(451 Group)给出的数据库格局

你可以看到在这个图片中有多个维度。关系型的以及非关系型的、分析型的以及操作型的、NoSQL类型的以及NewSQL类型的。最后的两个分类中,对于NoSQL有著名的子分类Key-Value、Document、Graph以及Big Tables,而对于NewSQL有子分类Storage-Engine、Clustering-Sharding、New Database、Cloud Service Solution。这个图有趣的地方在于,将一个数据放在一个精确的位置变得越来越难。每一个都在拼命地集成其他范围数据库中的特性。NewSQL系统实现NoSQL的核心特性,而NoSQL越来越多地试图实现“传统”数据库的特性如支持SQL或ACID,至少是可配置的持久化机制。

这一切都始于众多的数据库都提供与Hadoop进行集成。但是,也有很多其他的例子,如MarkLogic开始参与JSON浪潮,所以也很难对其进行定位。另外,更多的多模型数据库开始出现,如ArangoDB、OrientDB和AlechemyDB(现在它是很有前途的Aerospike DB的一部分)。它们允许在起始的时候只有一个数据库模型(如document/JSON模型)并在新需求出现的时候添加新的模型(Graph或key-value)。

图书

另外一个证明它开始变得成熟的标志就是图书市场。在2010年和2011年两本德语书出版之后,我们看到Wiley出版了Shashank Tiwari的书。它的结构很棒并且饱含了深刻伟大的见解。在2012年,这个竞赛围绕着两本书展开。“七周七数据库”(Seven Databases in Seven Weeks)当然是一本杰作。它的特点在于新颖的编写以及实用的基于亲身体验的见解:它选取了6种著名的NoSQL数据库以及PostGreSQL。这些都使得它成为一本高度推荐的图书。另一方面,P.J. Sandalage以及Martin Fowler采取了一种更为全面的方法,涵盖了所有的特征并帮助你评估采用NoSQL的路径和决策。

但是,会有更多的书出现。Manning的书出现在市场上只是个时间问题:Dan McCreary和Ann Kelly正在编写一本名为“Making Sense of NoSQL”的书,首期的MEAP(指的是Manning Early Access Program——译者注)章节已经可以看到了。

在介绍完理念和模式后,他们的第三章看起来保证很有吸引力:

  • 构建NoSQL大数据解决方案
  • 构建NoSQL搜索解决方案
  • 构建NoSQL高可用性解决方案
  • 使用NoSQL来提高敏捷性

只是一个全新的方式,绝对值得一读。

领导者的现状

让我们快速了解一下各个NoSQL的领导者。作为市场上很明显的领导者之一,Hadoop是一个很奇怪的动物(作者使用这个词,可能是因为Hadoop的标识是一只大象——译者注)。一方面,它拥有巨大的发展势头。正如前面所说,每个传统的数据库提供商都急切地声明支持Hadoop。像Cloudera和MapR这样的公司会持续增长并且新的Hadoop扩展和继承者每周都在出现。
即便是Hive和Pig也在更好地得到接受。不过,有一个美中不足之处:公司们依然在抱怨非结构化的混乱(读取和解析文件本应该更快一些),MapReduce在批处理上做的还不够(甚至Google已经舍弃了它),管理依旧很困难,稳定性问题以及在本地很难找到培训/咨询。即便你可以解决一些上面的问题,如果Hadoop继续像现在这样发展或发生重大变化的话,它依然会是热点问题。

第二位领导者,MongoDB,同样面临激烈的争论。处于领导地位的数据库会获得更多的批评,这可能是很自然的事情。不过,MongoDB经历了快速的增长,它受到的批评主要如下:

a)就老版本而言或者
b)缺少怎样正确使用它的知识。尽管MongoDB在下载区域清楚地表明32位版本不能处理2GB的数据并建议使用64位版本,但这依然受到了很多近乎荒谬的抱怨。

不管怎样,MongoDB合作者和资助者推动了雄心勃勃的发展路线,包含了很多热门的东西:

  • 行业需要的一些安全性/LDAP特性,目前正在开发
  • 全文本搜索很快会推出
  • 针对MapReduce的V8将会推出
  • 将会出现比集合级别更好的锁级别
  • Hash分片键正在开发中

尤其是最后一点吸引了很多架构师的兴趣。MongoDB经常被抱怨(同时也被竞争对手)没有实现简洁一致的哈希,因为key很容易定义所以不能保证完全正确。但在将来,将会有一个对hash分片键的配置。这意味着用户可以决定使用hash key来分片,还是需要使用自己选择分片key所带来的优势(可能很少)。

Cassandra是这个领域中的另一个产品,它做的很好并且添加了更多更好的特性,如更好的查询。但是不断有传言说运行Cassandra集群并不容易,需要一些很艰难的工作。但这里最吸引人的肯定是DataStax。Cassandra的新公司——获得了两千五百万美元的C类资助——很可能要处理分析和一些操作方面的问题。尤其是分析能力使得很多人感到惊讶,因为早期的Cassandra并没有被视为强大的查询机器。但是这种现状在最近的几个版本中发生了变化,查询功能对一些现代分析来讲已经足够了。

Redis的开发进度也值得关注。尽管Salvatore声明如果没有社区和Pieter Noordhuis的帮助,他做不成任何的事情,但是它依旧是相当棒的一个产品。对故障恢复的良好支持以及使用Lua的服务器端脚本语言是其最近的成就。使用Lua的决策对社区带来了一些震动,因为每个人都在集成JavaScript作为服务器端的语言。但是,Lua是一个整洁的语言并为Redis开启新的潘多拉盒子带来了可能性。

CouchBase在可扩展性和其他潜在因素方面看起来也是一个很好的选择,尽管Facebook以及Zynga面临着巨大的风波。它确实不是很热门的查询机器,但如果他们能够在将来提高查询能力,那它的功能就会相当完整了。与CouchDB创立者的合并毫无疑问是很重要的一个步骤,CouchDB在CouchBase里面的影响值得关注。在每个关于数据库的会议上,听到这样的讨论也是很有意思的,那就是在Damien、Chris和Jan离开后,CouchDB会变得更好呢还是更坏呢?大家在这里只能听到极端的观点。但是,只要数据库做得好谁关心这个呢。现在看起来,它确实做的很好。

最后一个需要提及的NoSQL数据库当然是Riak,在功能性和监控方面它也有了巨大的提升。在稳定性方面,它继续得到巨大的声誉:“像巨石一般稳定可靠且不显眼,并对你的睡眠有好处”。Riak CS fork在这种技术的模块化方面看起来也很有趣。

有意思的新加入者

除了市场领导者,评估新的加入者通常是很有意思的。让我们深入了解它们中的一部分。

毫无疑问,Elastic Search是最热门的新NoSQL产品,在一系列的A轮资助中它刚刚获得了一千万美元,这是它热门的一个明证。作为构建在Lucene之上的高扩展性搜索引擎,它有很多的优势:a)它有一个公司提供服务并且b)利用了Lucene在过去的多年中已被充分证明的成就。它肯定会比以往更加深入得渗透到整个行业中,并在半结构化信息领域给重要的参与者带来冲击。

Google在这个领域也推出了小巧但是迅速的LevelDB。在很多特殊的需求下,如压缩集成方面,它作为基础得到了很多的应用。即使是Riak都集成了LevelDB。考虑到Google的新数据库如Dremel和Spanner都有了对应的开源项目(如Apache Drill或Cloudera Impala),它依然被视为会继续存在的。

另外一个技术变化当然就是在2012年初的DynamoDB。自从部署在Amazon中,他们将其视为增长最快的服务。它的可扩展性很强。新特性开发地比较慢但它关注于SSD,其潜力是很令人振奋的。

多模块数据库也是值得关注的一个领域。最著名的代表者是OrientDB,它现在并不是新的加入者但它在很迅速地提高功能。可能它变化得太快了,很多使用者也许会很开心地看到OrientDB已经到达了1.0版本,希望它能更稳定一些。对Graph、Document、Key-Value的支持以及对事务和SQL的支持,使得我们有理由给它第二次表现的机会。尤其是对SQL的良好支持使得它对诸如Penthao这样的分析解决方案方面很有吸引力。这个领域另一个新的加入者是ArangoDB,它的进展很快,并不畏惧将自己与已确定地位的参与者进行比较。
但是,如果有新的需求必须要实现并且具有不同类型的新数据模型要进行持久化的话,对原生JSON和Graph的支持会省去很多的努力。

到目前位置,2012年的最大惊喜来自于Datomic。它由一些摇滚明星采用Clojure语言以难以令人置信的速度开发的,它发布了一些新的范式。另外,它还进入了ThoughtWorks的技术雷达,占据了推荐关注的位置。尽管它“只是”已有数据库中一个参与者,但是它有很多的优势,如:

  • 事务
  • 时间机器
  • 新颖且强大的查询方式
  • 新的模式方式
  • 缓存以及可扩展性的特性

目前,支持将DynamoDB、Riak、CouchBase、Infinispan以及SQL作为底层的存储引擎。它甚至允许你同时混合和查询不同的数据库。很多有经验的人都很惊讶于这种颠覆性的范式转变是如何可能实现的。但幸运的是它就是这样。

总结

作为总结,我们做出三点结论:

  1. 关于CAP理论,Eric Brewer的一些新文章应该几年前就发表。在这篇文章中这篇佳文的中文版地址——译者注),他指出“三选二”具有误导性,并指出了它的原因,世界为何远比简单的CP/AP更为复杂,如在ACID/BASE之间做出选择。虽然如此,近些年来有成千上万的对话和文章继续赞扬CAP理论而没有任何批评性的反思。Michael Stonebraker是NoSQL最强有力的审查者之一(NoSQL领域也对他颇多感激),他在多年前就指出了这些问题!遗憾的是,没有多少人在听。但是,既然Eric Brewer更新了他的理论,简单的CAP叙述时代肯定要结束了。在指出CAP理论的真实和多样性的观点上,请站在时代的前列。
  2. 正如我们所了解的那样,传统关系型数据库的不足导致了NoSQL领域的产生。但这也是传统帝国发起回击的时刻。在“NewSQL”这个术语之下,我们可以看到许多新的引擎(如database.com、VoltDB、GenieDB等,见图2),它们提高了传统的解决方案、分片以及云计算方案的能力。这要感谢NoSQL运动。

    但是随着众多的数据库尝试实现所有的特性,明确的边界消失了
    确定使用哪种数据库比以前更为复杂了。
    你必须要知道50个用例、50个数据库并要回答至少50个问题。关于后者,笔者在过去两年多的NoSQL咨询中进行了收集,可以在以下地址找到:选择正确的数据库在NoSQL和NewSQL间进行选择

  3. 一个通用的真理就是,每一项技术的变化——从客户端-服务端技术开始甚至更早——需要十倍的成本才能进行转移。例如,从大型机到客户端-服务端、客户端-服务端到SOA、SOA到WEB、RDBMS到混合型持久化之间的转换都是如此。所以可以推断出,在将NoSQL加入到他们的产品决策上,很多的公司在迟疑和纠结。但是,大家也都知道,最先采用的公司会从这个两个领域获益并且能够快速集成NoSQL,所以在将来会占据更有利的位置。就这一点而言,NoSQL解决方案会一直存在并且评估起来会是有利可图的领域。

关于作者

Prof. Dr. Stefan Edlich是德国柏林Beuth HS技术(University of App. Sc.)的高级讲师。他为诸多出版社如Apress、OReilly、Spektrum/Elsevier等编写了超过10本IT图书。他维护着NoSQL Archive网站, 从事NoSQL咨询并组织NoSQL技术会议,编写了世界上最早的两本NoSQL图书,现在他热衷于Clojure编程语言。