本文共 33991 字,大约阅读时间需要 113 分钟。
NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。
在现代的计算系统上每天网络上都会产生庞大的数据量。
这些数据有很大一部分是由关系数据库管理系统(RDBMS)来处理。 1970年 E.F.Codd's提出的关系模型的论文 "A relational model of data for large shared data banks",这使得数据建模和应用程序编程更加简单。
通过应用实践证明,关系模型是非常适合于客户服务器编程,远远超出预期的利益,今天它是结构化数据存储在网络和商务应用的主导技术。
NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。
事务在英文中是transaction,和现实世界中的交易很类似,它有如下四个特性:
1、A (Atomicity) 原子性
原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。
比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
2、C (Consistency) 一致性
一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。
例如现有完整性约束a+b=10,如果一个事务改变了a,那么必须得改变b,使得事务结束后依然满足a+b=10,否则事务失败。
3、I (Isolation) 独立性
所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。
比如现在有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的。
4、D (Durability) 持久性
持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
分布式系统(distributed system)由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。
分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。
因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。
分布式系统可以应用在不同的平台上如:Pc、工作站、局域网和广域网上等。
可靠性(容错) :
分布式计算系统中的一个重要的优点是可靠性。一台服务器的系统崩溃并不影响到其余的服务器。
可扩展性:
在分布式计算系统可以根据需要增加更多的机器。
资源共享:
共享数据是必不可少的应用,如银行,预订系统。
灵活性:
由于该系统是非常灵活的,它很容易安装,实施和调试新的服务。
更快的速度:
分布式计算系统可以有多台计算机的计算能力,使得它比其他系统有更快的处理速度。
开放系统:
由于它是开放的系统,本地或者远程都可以访问到该服务。
更高的性能:
相较于集中式计算机网络集群可以提供更高的性能(及更好的性价比)。
故障排除:
故障排除和诊断问题。
软件:
更少的软件支持是分布式计算系统的主要缺点。
网络:
网络基础设施的问题,包括:传输问题,高负载,信息丢失等。
安全性:
开放系统的特性让分布式计算系统存在着数据的安全性和共享的风险等问题。
NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。
NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL 数据库的发展却能很好的处理这些大的数据。
社会化关系网:
Each record: UserID1, UserID2
Separate records: UserID, first_name,last_name, age, gender,...Task: Find all friends of friends of friends of ... friends of a given user.Wikipedia 页面 :
Large collection of documents
Combination of structured and unstructured dataTask: Retrieve all pages regarding athletics of Summer Olympic before 1950.RDBMS
- 高度组织化结构化数据- 结构化查询语言(SQL) (SQL)- 数据和关系都存储在单独的表中。- 数据操纵语言,数据定义语言- 严格的一致性- 基础事务NoSQL
- 代表着不仅仅是SQL- 没有声明性查询语言- 没有预定义的模式-键 - 值对存储,列存储,文档存储,图形数据库- 最终一致性,而非ACID属性- 非结构化和不可预知的数据- CAP定理- 高性能,高可用性和可伸缩性NoSQL一词最早出现于1998年,是Carlo Strozzi开发的一个轻量、开源、不提供SQL功能的关系数据库。
2009年,Last.fm的Johan Oskarsson发起了一次关于分布式开源数据库的讨论[2],来自Rackspace的Eric Evans再次提出了NoSQL的概念,这时的NoSQL主要指非关系型、分布式、不提供ACID的数据库设计模式。
2009年在亚特兰大举行的"no:sql(east)"讨论会是一个里程碑,其口号是"select fun, profit from real_world where relational=false;"。因此,对NoSQL最普遍的解释是"非关联型的",强调Key-Value Stores和文档数据库的优点,而不是单纯的反对RDBMS。
在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer's theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
优点:
缺点:
BASE:Basically Available, Soft-state, Eventually Consistent。 由 Eric Brewer 定义。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。
BASE是NoSQL数据库通常对可用性及一致性的弱要求原则:
ACID | BASE |
---|---|
原子性(Atomicity) | 基本可用(Basically Available) |
一致性(Consistency) | 软状态/柔性事务(Soft state) |
隔离性(Isolation) | 最终一致性 (Eventual consistency) |
持久性 (Durable) |
类型 | 部分代表 | 特点 |
---|---|---|
列存储 | HbaseCassandraHypertable | 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。 |
文档存储 | MongoDBCouchDB | 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。 |
key-value存储 | Tokyo Cabinet / TyrantBerkeley DBMemcacheDBRedis | 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能) |
图存储 | Neo4JFlockDB | 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。 |
对象存储 | db4oVersant | 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。 |
xml数据库 | Berkeley DB XMLBaseX | 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。 |
现在已经有很多公司使用了 NoSQL:
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库中功能最丰富,最像关系型数据库的。
不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库,下面我们挨个介绍。
下表将帮助您更容易理解Mongo中的一些概念:
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
通过下图实例,我们也可以更直观的了解Mongo中的一些概念:
一个mongodb中可以建立多个数据库。
MongoDB的默认数据库为"db",该数据库存储在data目录中。
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
"show dbs" 命令可以显示所有数据的列表。
$ ./mongoMongoDB shell version: 3.0.6connecting to: test> show dbslocal 0.078GBtest 0.078GB>
执行 "db" 命令可以显示当前数据库对象或集合。
$ ./mongoMongoDB shell version: 3.0.6connecting to: test> dbtest>
运行"use"命令,可以连接到一个指定的数据库。
> use localswitched to db local> dblocal>
以上实例命令中,"local" 是你要链接的数据库。
在下一个章节我们将详细讲解MongoDB中命令的使用。
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
一个简单的文档例子如下:
{"site":"www.runoob.com", "name":"菜鸟教程"}
下表列出了 RDBMS 与 MongoDB 对应的术语:
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键 (MongoDB 提供了 key 为 _id ) |
数据库服务和客户端 | |
Mysqld/Oracle | mongod |
mysql/sqlplus | mongo |
需要注意的是:
文档键命名规范:
集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
比如,我们可以将以下不同数据结构的文档插入到集合中:
{"site":"www.baidu.com"}{"site":"www.google.com","name":"Google"}{"site":"www.runoob.com","name":"菜鸟教程","num":5}
当第一个文档插入时,集合就会被创建。
如下实例:
db.col.findOne()
Capped collections 就是固定大小的collection。
它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 "RRD" 概念类似。
Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。
Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。
由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。
要注意的是指定的存储大小包含了数据库的头信息。
db.createCollection("mycoll", {capped:true, size:100000})
数据库的信息是存储在集合中。它们使用了系统的命名空间:
dbname.system.*
在MongoDB数据库中名字空间
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 列出所有名字空间。 |
dbname.system.indexes | 列出所有索引。 |
dbname.system.profile | 包含数据库概要(profile)信息。 |
dbname.system.users | 列出所有可访问数据库的用户。 |
dbname.local.sources | 包含复制对端(slave)的服务器信息和状态。 |
对于修改系统集合中的对象有如下限制。
在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。
{{system.users}}是可修改的。 {{system.profile}}是可删除的。
下表为MongoDB中常用的几种数据类型。
数据类型 | 描述 |
---|---|
String | 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。 |
Integer | 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。 |
Boolean | 布尔值。用于存储布尔值(真/假)。 |
Double | 双精度浮点值。用于存储浮点值。 |
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。 |
Array | 用于将数组或列表或多个值存储为一个键。 |
Timestamp | 时间戳。记录文档修改或添加的具体时间。 |
Object | 用于内嵌文档。 |
Null | 用于创建空值。 |
Symbol | 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。 |
Date | 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。 |
Object ID | 对象 ID。用于创建文档的 ID。 |
Binary Data | 二进制数据。用于存储二进制数据。 |
Code | 代码类型。用于在文档中存储 JavaScript 代码。 |
Regular expression | 正则表达式类型。用于存储正则表达式。 |
下面说明下几种重要的数据类型。
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象
由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId()> newObject.getTimestamp()ISODate("2017-11-25T07:21:10Z")
ObjectId 转为字符串
> newObject.str5a1919e63df83ce79df8b38f
BSON 字符串都是 UTF-8 编码。
BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:
序数
在单个 mongod 实例中,时间戳值通常是唯一的。
在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。
BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。
表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。
> var mydate1 = new Date() //格林尼治时间> mydate1ISODate("2018-03-04T14:58:51.233Z")> typeof mydate1object> var mydate2 = ISODate() //格林尼治时间> mydate2ISODate("2018-03-04T15:00:45.479Z")> typeof mydate2object
这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。
返回一个时间类型的字符串:
> var mydate1str = mydate1.toString()> mydate1strSun Mar 04 2018 14:58:51 GMT+0000 (UTC) > typeof mydate1strstring
或者
> Date()Sun Mar 04 2018 15:02:59 GMT+0000 (UTC)
1.启动服务端mongod.exe --dbpath "C:\\MongoDB\data\db" --logpath "C:\\MongoDB\data\log\mongo.log"2.启动客户端mongo.exe --port 27017# 默认端口:27017 可以自行指定端口启动3.直接创建库use students4.自主创建student表,在mongodb中称为集合。写入数据db.student.save({name:"aliang", age:18})5.插入数据db.student.insert({name:"aliang", age:20, city:"shenzhen"})# 注意:无则添加,有则修改6.查看数据db.student.find()# 查找全部数据7.按条件查找db.student.find({age:20})8.统计数据db.student.count()9.按查找条件统计数据db.student.find({name:"aliang"}).count()10.按查找出来的某一字段的大小排序db.student.find().sort({age:1})# 1 即表示从小到大顺序排序db.student.find().sort({age:-1})# -1 即表示从大到小倒序排序11.美化查询结果db.student.find().sort({age:-1}).pretty(0)
BSON是一种计算机的数据交换格式
主要被用作MongoDB数据库中的数据存储和网络传输格式
是一种二进制表示形式,能用来表示简单的数据结构、关系数组(MongoDB中被称为“对象”或者“文档”)以及MongoDB中的各种数据类型
Binary JSON——>二进制的JSON
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 可以更好的存储大于16M的文件。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
以下是简单的 fs.files 集合文档:
{ "filename": "test.txt", "chunkSize": NumberInt(261120), "uploadDate": ISODate("2014-04-13T11:32:33.557Z"), "md5": "7b762939321e146569b07f72c62cca4f", "length": NumberInt(646)}
以下是简单的 fs.chunks 集合文档:
{ "files_id": ObjectId("534a75d19f54bfec8a2fe44b"), "n": NumberInt(0), "data": "Mongo Binary Data"}
现在我们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles.exe工具。
打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:
>mongofiles.exe -d gridfs put song.mp3
-d gridfs 指定存储文件的数据库名称,如果不存在该数据库,MongoDB会自动创建。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。
使用以下命令来查看数据库中文件的文档:
>db.fs.files.find()
以上命令执行后返回以下文档数据:
{ _id: ObjectId('534a811bf8b4aa4d33fdf94d'), filename: "song.mp3", chunkSize: 261120, uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41", length: 10401959 }
我们可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个 _id 获取区块(chunk)的数据:
>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})
以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。
MongoDB 中使用了 find 和 find_one 方法来查询集合中的数据,它类似于 SQL 中的 SELECT 语句。
本文使用的测试数据如下:内容显示是按照年龄大小倒序排列之后美化展示的效果
> show dbsadmin 0.000GBconfig 0.000GBlocal 0.000GBstudent 0.000GB> use studentswitched to db student> show tablesstudent> db.student.find().sort({age:-1}).pretty(0){ "_id" : ObjectId("5f805008cda441be27325c00"), "name" : "frank", "age" : 23}{ "_id" : ObjectId("5f804dd4cda441be27325bfd"), "name" : "aliang", "age" : 18}{ "_id" : ObjectId("5f804e32cda441be27325bfe"), "name" : "yexiang", "age" : 18, "city" : "shanghai"}{ "_id" : ObjectId("5f804e53cda441be27325bff"), "name" : "yexiang", "age" : 18, "city" : "shanghai", "mobile" : "17361537209"}{ "_id" : ObjectId("5f804d9acda441be27325bfc"), "name" : "frank" }
我们可以使用 find_one() 方法来查询集合中的一条数据。
查询 student 文档(集合)中的第一条数据:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表即集合 "student"mycollection = mydb["student"]# mycollection 即选择的数据集合的实例化对象x = mycollection.find_one()print(x)
输出结果为:
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}
find() 方法可以查询集合中的所有数据,类似 SQL 中的 SELECT * 操作。
以下实例查找 student 集合中的所有数据:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find()print(x)for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai'}{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}
我们可以使用 find() 方法来查询指定字段的数据。
mycollection.find({}, {'_id': 0, 'name': 1, 'age': 1})# 即表示'_id'不需要查询,仅查询'name'以及'age'对应的数据,其他字段的数据信息没有标明则表示不需要查询!
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象# 该注意的是:一条按需查找的语句中,只能设置'_id'这个字段和其他字段的值不同,比如以下查询语句。# 除过'_id'字段,其他字段如果需要查询就设置成1,如果不需要查询就干脆不要写,如果写成{'name': 0, 'age': 1},会报错的。x = mycollection.find({}, {'_id': 0, 'name': 1, 'age': 1})for i in x: print(i)
输出结果为:
{'name': 'frank'}{'name': 'aliang', 'age': 18.0}{'name': 'yexiang', 'age': 18.0}{'name': 'yexiang', 'age': 18.0}{'name': 'frank', 'age': 23.0}
以下实例除了city字段外,其他都返回:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find({}, {'city': 0})for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0}{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'mobile': '17361537209'}{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}
以下代码同时指定了 0 和 1 则会报错:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find({}, {'name': 1, 'city': 0})for i in x: print(i)
错误内容大概如下:
pymongo.errors.OperationFailure: Projection cannot have a mix of inclusion and exclusion., full error: {'ok': 0.0, 'errmsg': 'Projection cannot have a mix of inclusion and exclusion.', 'code': 2, 'codeName': 'BadValue'}
我们可以在 find() 中设置参数来过滤数据。
以下实例查找 age 字段为 18 的数据:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find({'age': 18})for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}{'_id': ObjectId('5f804e32cda441be27325bfe'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai'}{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}
查询的条件语句中,我们还可以使用修饰符($gt:大于, $lt:小于)。
以下实例用于读取 age 字段大于18的数据信息,大于的修饰符条件为 {"$gt": 18} :
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find({'age': {'$gt': 18}})for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}
我们还可以使用正则表达式作为修饰符。
正则表达式修饰符只用于搜索字符串的字段。
以下实例用于读取 name 字段中第一个字母为 "a" 的数据,正则表达式修饰符条件为 {"$regex": "^a"} :
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find({'name': {'$regex': '^a'}})for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
如果我们要对查询结果设置指定条数的记录可以使用 limit() 方法,该方法只接受一个数字参数。
以下实例返回 2 条文档记录:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.find().limit(2)for i in x: print(i)
输出结果为:
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}
我们可以使用 delete_one() 方法来删除一个文档,
该方法第一个参数为查询对象,指定要删除哪些数据。
以下实例删除 name 字段值为 "aliang" 的文档:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象# 只删除第一个出现的'yexiang'mycollection.delete_one({'name': 'yexiang'})# 删除后输出:for i in mycollection.find(): print(i)
输出结果为:
{'_id': ObjectId('5f804d9acda441be27325bfc'), 'name': 'frank'}{'_id': ObjectId('5f804dd4cda441be27325bfd'), 'name': 'aliang', 'age': 18.0}{'_id': ObjectId('5f804e53cda441be27325bff'), 'name': 'yexiang', 'age': 18.0, 'city': 'shanghai', 'mobile': '17361537209'}{'_id': ObjectId('5f805008cda441be27325c00'), 'name': 'frank', 'age': 23.0}
我们可以使用 delete_many() 方法来删除多个文档,该方法第一个参数为查询对象,指定要删除哪些数据。
删除所有 name 字段中以 f 开头的文档:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象x = mycollection.delete_many({'name': {'$regex': '^f'}})print(x.deleted_count, "个文档已经删除!")
输出结果为:
2 个文档已删除
delete_many() 方法如果传入的是一个空的查询对象,则会删除集合中的所有文档:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象# 集合中的所有文档全部被删除mycollection.delete_many({})
我们可以使用 drop() 方法来删除一个集合。
以下实例删除了 student 集合:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "student"mycollection = mydb["student"]# mycollection 即选择的数据表的实例化对象# 删除student集合mycollection.drop()
如果删除成功 drop() 返回 true,如果删除失败(集合不存在)则返回 false。
db.getCollection("TempSample").update({},{$unset:{'tag_id_list':''}},false, true)
sort() 方法可以指定升序或降序排序。
sort() 方法第一个参数为要排序的字段,第二个字段指定排序规则,1 为升序,-1 为降序,默认为升序。
我们可以在 MongoDB 中使用 update_one() 方法修改文档中的记录。
该方法第一个参数为查询的条件,第二个参数为要修改的字段。
如果查找到的匹配数据多于一条,则只会修改第一条。
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象mycollection.update_one({"age": 18}, {"$set": {"age": 24}})for i in mycollection.find(): print(i)
执行输出结果为:
{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 24}
update_one() 方法只能修匹配到的第一条记录,如果要修改所有匹配到的记录,可以使用 update_many()。
以下实例将查找所有以 a 开头的 name 字段,并将匹配到所有记录的 age 字段修改为 18:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象x = mycollection.update_many({"name": {"$regex": "^a"}}, {"$set": {"age": 18}})print(x.modified_count, "个文档被修改!")for i in mycollection.find(): print(i)
输出结果为:
2 个文档被修改!{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 18}{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 18}
MongoDB 中的一个文档类似 SQL 表中的一条记录。
集合中插入文档使用 insert_one() 方法,该方法的第一参数是字典 name: value 对。
以下实例向 user 集合中插入文档:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象mycollection.insert_one({'name': 'huayignxiong', 'age': 180})for i in mycollection.find(): print(i)
执行输出结果为:
{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}
insert_one() 方法返回 InsertOneResult 对象,该对象包含 inserted_id 属性,它是插入文档的 id 值。
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象x = mycollection.insert_one({'name': '狗哥', 'age': 0})print(x.inserted_id)
执行输出结果为:
5f85740edca4eb3a6e781c29
如果我们在插入文档时没有指定 _id,MongoDB 会为每个文档添加一个唯一的 id。
集合中插入多个文档使用 insert_many() 方法,该方法的第一参数是字典列表。
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象x = mycollection.insert_many([{'name': '天行', 'age': 10}, {'name': '阿良', 'age': 20}])print(x.inserted_ids)for i in mycollection.find(): print(i)
输出结果类似如下:
[ObjectId('5f857538711bf2a6c03f2a9e'), ObjectId('5f857538711bf2a6c03f2a9f')]{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}{'_id': ObjectId('5f85740edca4eb3a6e781c29'), 'name': '狗哥', 'age': 0}{'_id': ObjectId('5f857538711bf2a6c03f2a9e'), 'name': '天行', 'age': 10}{'_id': ObjectId('5f857538711bf2a6c03f2a9f'), 'name': '阿良', 'age': 20}
insert_many() 方法返回 InsertManyResult 对象,该对象包含 inserted_ids 属性,该属性保存着所有插入文档的 id 值。
我们也可以自己指定 id,插入,以下实例我们在 user 集合中插入数据,_id 为我们指定的:
import pymongo# 连接本地数据库,默认端口是27017myclient = pymongo.MongoClient("mongodb://localhost:27017/")# myclient即连接对象,使用连接对象选择要操作的数据库 "student"mydb = myclient["student"]# mydb 即选择的数据库的实例化对象,使用此库对象选择要操作的表 "user"mycollection = mydb["user"]# mycollection 即选择的数据表的实例化对象x = mycollection.insert_many([{'id': 1, 'name': '嗨', 'age': 100}, {'id': 2, 'name': 'man', 'age': 200}])print(x.inserted_ids)for i in mycollection.find(): print(i)
输出结果为:
[ObjectId('5f8576fae5b453fa5cbe98b8'), ObjectId('5f8576fae5b453fa5cbe98b9')]{'_id': ObjectId('5f856c81878d502dbe841498'), 'name': 'aliang', 'age': 10}{'_id': ObjectId('5f856ecc878d502dbe841499'), 'name': 'ahuang', 'age': 10}{'_id': ObjectId('5f8572fcc9ed7fc1e0eab03f'), 'name': 'huayignxiong', 'age': 180}{'_id': ObjectId('5f85740edca4eb3a6e781c29'), 'name': '狗哥', 'age': 0}{'_id': ObjectId('5f857538711bf2a6c03f2a9e'), 'name': '天行', 'age': 10}{'_id': ObjectId('5f857538711bf2a6c03f2a9f'), 'name': '阿良', 'age': 20}{'_id': ObjectId('5f8576fae5b453fa5cbe98b8'), 'id': 1, 'name': '嗨', 'age': 100}{'_id': ObjectId('5f8576fae5b453fa5cbe98b9'), 'id': 2, 'name': 'man', 'age': 200}
mongodb集群搭建有三种方式。1、Master-Slave模式2、Replica-Set方式3、Sharding方式其中,第一种方式基本没什么意义,官方也不推荐这种方式搭建。另外两种分别就是副本集和分片的方式。今天介绍副本集的方式搭建mongodb高可用集群。 副本集的方式也很容易理解,这里需要一个主节点,一个备节点,如果主节点发生故障,那么会启用备节点,当主节点修复之后,主节点再次恢复为主节点,备节点不再是主节点的角色。副本集的方式还需要一个角色,那就是仲裁节点,它不存储数据,他的作用就是当主节点出现故障,选举出备节点作为主节点,继续保证集群可用。客户端连接时只连接主节点或者备节点,不用连接仲裁节点。修改配置文件master.confdbpath=/data/mongodb/masterlogpath=/data/mongodb/master.logpidfilepath=/data/mongodb/master.piddirectoryperdb=truelogappend=truereplSet=testrsbind_ip=192.168.61.21port=27017oplogSize=10000fork=truenoprealloc=trueslave.confdbpath=/data/mongodb/slavelogpath=/data/mongodb/slave.logpidfilepath=/data/mongodb/slave.piddirectoryperdb=truelogappend=truereplSet=testrsbind_ip=192.168.61.22port=27017oplogSize=10000fork=truenoprealloc=truearbiter.confdbpath=/data/mongodb/arbiterlogpath=/data/mongodb/arbiter.logpidfilepath=/data/mongodb/arbiter.piddirectoryperdb=truelogappend=truereplSet=testrsbind_ip=192.168.61.23port=27017oplogSize=10000fork=truenoprealloc=true启动节点,保证每个节点上存储数据的文件夹都存在/data/mongodb/master,/data/mongodb/slave,/data/mongodb/arbiter,然后依次启动每个节点。mongod -f master.confmongod -f slave.confmongod -f arbiter.conf创建集群[root@linux-node1 mongodb]# mongo 192.168.61.21:27017MongoDB shell version: 3.2.9connecting to: 192.168.61.21:27017/testServer has startup warnings: 2017-11-16T22:44:06.170+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.2017-11-16T22:44:06.170+0800 I CONTROL [initandlisten] 2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] 2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] 2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never'2017-11-16T22:44:06.171+0800 I CONTROL [initandlisten] > use adminswitched to db admin> cfg={_id:"testrs",members:[{_id:0,host:"192.168.61.21:27017",priority:2},{_id:1,host:"192.168.61.22:27017",priority:1},{_id:2,host:"192.168.61.23:27017",arbiterOnly:true}]};{ "_id" : "testrs", "members" : [ { "_id" : 0, "host" : "192.168.61.21:27017", "priority" : 2 }, { "_id" : 1, "host" : "192.168.61.22:27017", "priority" : 1 }, { "_id" : 2, "host" : "192.168.61.23:27017", "arbiterOnly" : true } ]}> rs.initiate(cfg){ "ok" : 1 }查看集群状态testrs:OTHER> rs.status(){ "set" : "testrs", "date" : ISODate("2017-11-16T14:51:33.788Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "192.168.61.21:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 447, "optime" : { "ts" : Timestamp(1510843888, 2), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-16T14:51:28Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1510843888, 1), "electionDate" : ISODate("2017-11-16T14:51:28Z"), "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "192.168.61.22:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 15, "optime" : { "ts" : Timestamp(1510843877, 1), "t" : NumberLong(-1) }, "optimeDate" : ISODate("2017-11-16T14:51:17Z"), "lastHeartbeat" : ISODate("2017-11-16T14:51:32.718Z"), "lastHeartbeatRecv" : ISODate("2017-11-16T14:51:31.944Z"), "pingMs" : NumberLong(0), "configVersion" : 1 }, { "_id" : 2, "name" : "192.168.61.23:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 15, "lastHeartbeat" : ISODate("2017-11-16T14:51:32.718Z"), "lastHeartbeatRecv" : ISODate("2017-11-16T14:51:29.987Z"), "pingMs" : NumberLong(1), "configVersion" : 1 } ], "ok" : 1}
数据测试:
主节点测试
从节点连接,查询数据库出错:not master and slaveOk=false,这个错误是正常的,备节点不允许读操作。 show dbs2017-11-16T23:10:05.041+0800 E QUERY [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 }
可以利用rs.slaveOk()命令来解决这个问题,但是在集群环境中没有必要这么做。
故障测试:
[root@linux-node1 mongodb]# ps -ef |grep mongodroot 24676 1 1 22:44 ? 00:00:21 mongod -f master.confroot 24856 11281 0 23:18 pts/0 00:00:00 grep --color=auto mongod[root@linux-node1 mongodb]# kill -9 24676主节点挂掉:在备节点上查看集群状态,一会会发现备节点成为主节点:primary。testrs:SECONDARY> rs.status(){ "set" : "testrs", "date" : ISODate("2017-11-16T15:18:56.862Z"), "myState" : 1, "term" : NumberLong(2), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "192.168.61.21:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2017-11-16T15:18:55.651Z"), "lastHeartbeatRecv" : ISODate("2017-11-16T15:18:31.988Z"), "pingMs" : NumberLong(1), "lastHeartbeatMessage" : "Connection refused", "configVersion" : -1 }, { "_id" : 1, "name" : "192.168.61.22:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1986, "optime" : { "ts" : Timestamp(1510845524, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2017-11-16T15:18:44Z"), "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1510845523, 1), "electionDate" : ISODate("2017-11-16T15:18:43Z"), "configVersion" : 1, "self" : true }, { "_id" : 2, "name" : "192.168.61.23:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 1655, "lastHeartbeat" : ISODate("2017-11-16T15:18:55.632Z"), "lastHeartbeatRecv" : ISODate("2017-11-16T15:18:51.951Z"), "pingMs" : NumberLong(1), "configVersion" : 1 } ], "ok" : 1}这里如果再次将挂掉的master节点启动,那么一会master节点会变为primary主节点,而不是由slave备节点继续充当主节点
转载地址:http://bfnuz.baihongyu.com/