NoSql概述 NoSql(Not Only SQL),意即"不仅仅是SQL" 指的是非关系型的数据库,是一项全新的数据库革命性运动 NoSql和关系型数据库的对比 关系型数据库 关系型数据库中的表都是存储一些结构化的数据,每条记录的字段的组成都一样, 即使不是每条记录都需要所有的字段,但数据库会为每条数据分配所有的字段 非关系型数据库 非关系型数据库以键值对(key-value)存储,它的结构不固定,每一条记录可以有不一样的键, 每条记录可以根据需要增加一些自己的键值对,这样就不会局限于固定的结构,可以减少一些时间和空间的开销. 对应关系(仅为mongoDB和关系型数据库对比) 关系型数据库 mongoDB 表(table) 集合 记录(record) 文档(document) 字段(field) 键(key) 值(value) 值(value) 主外键(PK,FK) 无 扩展性差 扩展性强 非关系型数据库中存储的数据没有太大的关联关系 NoSql数据库的优缺点 优点 简单的扩展 快读的读写 低廉的成本 灵活的数据模型 不足 不提供对sql的支持 支持的特性不够丰富 现有的产品不够成熟 MongoDB基本概念 文档(document)是MongoDB中数据的基本单元,非常类似于关系型数据库系统中的行(但是比行要复杂的多) 集合(collection)就是一组文档,如果说MongoDB中的文档类似于关系型数据库中的行,那么集合就如同表 MongoDB的单个计算机可以容纳多个独立的数据库,每个数据库都有自己的集合和权限 MongoDB自带简洁但功能强大的JavaScript shell,这个工具对于管理MongoDB实例和操作数据作用非常大 每一个文档都有一个特殊的键"_id",它在文档所处的集合中是唯一的,相当于关系型数据库中的表的主键 数据类型 null 表示空值或未定义的对象 {"x":null} 布尔值 真或者假:true或false {"x":true} 32位整数 shell是不支持该类型的,shell中默认会转换为64位浮点数 64位整数 shell是不支持该类型的,shell中默认会转换为64位浮点数 64位浮点数 shell中的数字就是这一种类型 {"x":3.14,"y":3} 字符串 utf-8字符串 {"foo":"bar"} 符号 shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串 对象id 文档的12字节的唯一id {"id":ObjectId()} 日期 从标准纪元开始的毫秒数 {"date":new Date()} 正则表达式 文档中可以包含正则表达式,遵循JavaScript的语法 {"foo":/foobar/i} 代码 文档中可以包含JavaScript代码 {"x":function(){}} 未定义 undefined {"x":undefined} 数组 值的集合或者列表 {"arr":["a","b"]} 内嵌文档 文档可以作为文档中某个key的value {"x":{"foo":"bar"}} 安装 下载: 官网http://www.mongodb.org/ 解压,并配置环境变量 启动 服务端 新建一个目录,用来存放数据 启动服务,默认端口号:27017 格式: mongod --dbpath=路径 --port 端口号 例: mongod --dbpath=D:\test\mongodb 客户端 连接服务端 格式: mongo 地址:端口 例: mongo localhost:27017 常用命令 数据库 显示所有数据库 show dbs 显示当前数据库 db 更改当前数据库 或 创建新数据库 use 库名 --如果数据库不存在,则自动创建 删除当前数据库 db.dropDatabase() 集合 显示当前数据库下的集合 show collections show tables 创建集合 显示创建 db.createCollection("集合名称") 隐式创建(创建集合的时候,同时往集合中添加数据) db.集合名称.insert({name:"zhangsan",age:18}) 文档 下面的语句都是对集合的操作,所以用法如: db.集合.find() 查询(支持链式操作) 查询所有 find() 条件查询 find({name:"zhangsan"}) 查询指定键的值 find({name:"zhangsan"},{age:1}) 查询name为张三的记录的age列,取决于find的第二个位置的参数,1表示只显示某列,0表示排除某列 大于,大于等于,小于,小于等于,不等于 find({age:($ge:99995)}) 大于,大于等于,小于,小于等于,不等于分别为:gt,gte,lt,lte,ne 查询第一条 findOne() 查询个数 count() 排序 sort({age:1}) 升序为1,降序为-1 分页 skip(2).limit(3) 表示从2开始,查询一页,一页为3条记录 插入 插入一条记录 insert({name:"zhangsan",age:18}) 删除 删除所有 remove({}) 条件删除 remove({name:"zhangsan"}) 更新 update(criteria,objNew,upsert,multi) criteria:用于设置查询条件的对象 objNew:用于设置更新内容的对象 upsert:如果记录不存在,是否要插入一条记录,0或1,默认为0 multi:如果有多个符合条件的记录,是否全部更新,0或1,默认为0 $set 只修改记录中指定key的值,其它key保持不变 例:db.aaa.update({name:"zhangsan"},{$set:{age:20}}) 如果不使用$set,则那条记录所有字段会被清空,只留一个age字段为20!!!! $inc 表示使某个键值加减指定的数值 例:db.aaa.update({name:"zhangsan"},{$inc:{age:1}}) $unset 用来删除某个键 例:db.aaa.update({name:"zhangsan",{$unset:{age:1}}}) 注意:这里支持javascript语句,如插入一万条记录,可在命令行键入 for(var i=0;i<10000;i++){ db.集合.insert({name:"zhangsan",age:i}) } 其它类型数据 数组 插入一条记录 db.集合.insert({class:"1211",student:["zhangsan","lisi"]}) 条件表达式 $or name等于zhangsan 或 age等于18 find($or:[{name:"zhangsan"},{age:18}]) $nor name不等于zhangsan 且 age不等于18 find($nor:[{name:"zhangsan"},{age:18}]) $exists 存在name键的记录,1表示存在,0表示不存在 find({name:{$exists:1}}) $in age为18或20的记录 find({age:{$in:[18,20]}}) $nin agebu为18且不为20的记录 find({age:{$nin:[18,20]}}) $all 用于数组,student数组包含zhangsan和lisi的记录 find({student:{$all:["zhangsan","lisi"]}}) 游标 var cc = db.集合.find(); while(cc.hasNext()){ cc.next(); } 其它命令 显示操作的执行信息 explain() 例: db.集合.find().explain() 显示命令帮助,可显示指定操作后可以进行的操作 help() 例: db.help() , db.集合.help() , db.集合.find().help() 索引 在数据库中,默认存在一个集合system.indexes,可显示数据库中存在的索引 mongoDB默认为每个表的_id建立唯一索引 为age列建立索引 db.集合.ensureIndex({age:1}) 为age列建立唯一索引 db.集合.ensureIndex({age:1},{unique:true}) 为age列删除索引 db.集合.dropIndex({age:1}) 固定集合 是指事先创建好的固定大小的集合 固定集合很像环形队列,如果空间不足,则最早的文档就会被删除 固定集合适用于任何想要自动淘汰过期属性的场景 命令 db.createCollection("集合名称",{capped:true,size:1000,max:100}) capped表示是否固定大小 size表示文档总大小限制,单位为kb max表示文档总条数限制 如果达到容量大小或条数限制,则前面的文档会被自动删除释放空间 备份和恢复,导入和导出 使用mongoDB提供的工具mongodump/mongorestore进行备份和恢复 使用mongoDB提供的工具mongoimport/mongoexport进行导入和导出 在控制台进行操作,不用使用mongo连接 备份(mongodump) 命令 mongodump -h 服务地址 -d 数据库名称 -o 备份文件地址 例: mongodump -h localhost:27017 -d test -o d:\dump 恢复(mongorestore) 命令 mongorestore -h 服务地址 -d 数据库名称 -o 备份文件地址 例: mongorestore -h localhost:27017 -d test -o d:\dump 导出(mongoexport) 命令 mongoexport -h 服务地址 -d 数据库名称 -c 集合名称 -o 导出目标地址 导出目标地址需要精确到文件名称,支持txt,word,excel等等文件 导入(mongoimport) 命令 mongoimport -h 服务地址 -d 数据库名称 -c 集合名称 导入目标地址 这条语句后不能加分号! 安全认证(分配用户和用户登录) 添加管理员 use amdin db.addUser("root","root") 添加普通用户 切换到要添加用户的库 use test 添加读/写用户 db.addUser("zhangsan","1234") 添加读用户 db.addUser("zhangsan","1234",true) 开启安全验证 启动服务器时 mongod --dbpath=路径 --auth 连接服务器进行操作前需要登录,否则报错 db.auth("用户名","密码") 集群 主从集群(主从复制) 即对主服务数据的修改都会在从服务上自动同步 从节点可以有多个 从节点只能进行数据查询,不能进行数据修改 启动一个主服务 mongod --dbpath d:/master --port 10000 --master 启动一个从服务 mongod --dbpath d:/slaver --port 10001 --slave --source localhost:10000 这样,则主服务上的数据改变,则自动同步到所有从服务中 副本集 副本集是为了避免当服务器崩溃后服务不能正常进行的情况发生 副本集可以理解为有自动故障恢复功能的主从集群。 主从集群和副本集最为明显的区别就是副本集没有固定的主节点 副本集总有一个主节点(primary)和多个备份节点(secondary) 当主节点崩溃后,副本集会从备份节点中选择一个做为主节点,继续提供服务 只有主节点可以对数据进行读写操作,从节点不能对数据进行读和写的操作 数据会自动从主节点同步到备份节点 示例 启动节点1 mongod --dbpath d:\test\mongodb\dbs\node1 --logpath d:\test\mongodb\logs\node1\logs.txt --logappend --port 10001 --replSet setOne/localhost:10002 启动节点2 mongod --dbpath d:\test\mongodb\dbs\node2 --logpath d:\test\mongodb\logs\node2\logs.txt --logappend --port 10002 --replSet setOne/localhost:10001 启动节点3 mongod --dbpath d:\test\mongodb\dbs\node3 --logpath d:\test\mongodb\logs\node3\logs.txt --logappend --port 10003 --replSet setOne/localhost:10001,localhost:10002 连接任意一个服务,执行初始化操作 mongo localhost:10001 use admin db.runCommand({ "replSetInitiate":{ "_id":"setOne", "members":[ { "_id":1, "host":"localhost:10001", "priority":3 },{ "_id":2, "host":"localhost:10002", "priority":2 },{ "_id":3, "host":"localhost:10003", "priority":1 } ] } }) setOne为副本集名称,members为副本集所有节点 priority为优先级,10001的优先级最高,则10001为主节点,10002和10003为备份节点 查询初始化操作是否成功,连接另一个服务,执行查询 mongo localhost:10002 db.$cmd.findOne({ismaster:1}) 查看结果信息是否有误 这样,如果主节点10001崩溃,则10002自动成为主节点,如果10002也崩溃,则10003自动成为主节点 分布式存储(分片) 分片(sharding)是指将数据拆分,将其分散存在不同的机器上的过程 将数据分散到不同的机器上,不需要功能强大的大型计算机就可以存储更多的数据,处理更多的负载 mongoDB分片的基本思想是将集合切分成小块,这些块分散到若干个片里面 在分片之前要运行一个路由进程(mongos),这个路由器知道所有数据的存放位置 应用可以连接它来正常发送请求,而不需要知道数据已经被拆分了 片键: 设置分片时,需要从集合中选一个键,用该键的值作为数据拆分的依据 步骤: 启动存放配置信息的服务 mongod --dbpath D:\test\mongodb\config2222 --port 2222 启动路由服务 mongos --port 3333 --configdb=localhost:2222 启动三个普通服务 mongod --dbpath D:\test\mongodb\node4444 --port 4444 mongod --dbpath D:\test\mongodb\node5555 --port 5555 mongod --dbpath D:\test\mongodb\node6666 --port 6666 连接路由服务添加配置 mongo localhost:3333 use admin db.runCommand({"addshard":"localhost:4444",allowLocal:true}) db.runCommand({"addshard":"localhost:5555",allowLocal:true}) db.runCommand({"addshard":"localhost:6666",allowLocal:true}) db.runCommand({"enablesharding":"test"}) db.runCommand({"shardcollection":"test.parson","key":{"age":1}}) ------------------------------------------------------- addshard添加分片,allowLocal表示是否可以直接连接普通服务 enablesharding表示开启test数据库的分片功能 shardcollection表示要分片的集合是parson,片键是age 连接路由服务添加数据 这时就可以连接路由服务添加数据了(test数据库的parson集合) 数据会自动分布到三个普通服务上,数据量一般为均分(与片键的值有关系) 使用java操作mongoDB 导入jar包 mongo-java-driver-3.0.2 测试代码 @Test public void testAdd() throws UnknownHostException{ Mongo mongo=new Mongo("127.0.0.1", 10000); DB db=mongo.getDB("test"); DBCollection collection=db.getCollection("person"); BasicDBObject dbObject=new BasicDBObject(); dbObject.put("id", 1); dbObject.put("name", "找力宏"); collection.insert(dbObject); mongo.close(); } @Test public void testDelete() throws UnknownHostException{ Mongo mongo=new Mongo("127.0.0.1", 10000); DB db=mongo.getDB("test"); DBCollection collection=db.getCollection("person"); DBObject dbObject=new BasicDBObject(); dbObject.put("name", "wangxiaowang"); collection.remove(dbObject); mongo.close(); } @Test public void testUpdate() throws UnknownHostException{ Mongo mongo=new Mongo("127.0.0.1", 10000); DB db=mongo.getDB("test"); DBCollection collection=db.getCollection("person"); BasicDBObject condtion = new BasicDBObject("_id",new ObjectId("5396bf0b0e78ffdd37b084b2")); BasicDBObject object = (BasicDBObject) collection.findOne(condtion); object.put("name", "wangwu"); collection.update(condtion, object); mongo.close(); } @Test public void testQuery() throws UnknownHostException{ Mongo mongo=new Mongo("127.0.0.1", 10000); DB db=mongo.getDB("test"); DBCollection collection=db.getCollection("person"); DBCursor dbCursor=collection.find(); while(dbCursor.hasNext()){ DBObject basicDBObject=dbCursor.next(); System.out.println(basicDBObject.toString()); } mongo.close(); }