Login
网站首页 > 文章中心 > 其它

第五部分_三) 数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

作者:小编 更新时间:2023-08-16 10:01:48 浏览量:281人看过


非关系型数据库存储

NoSQL全称Not Only SQL,意为不仅仅是SQL,泛指非关系型数据库.NoSQL基于键值对,不经过SQL层的解析,数据间没有耦合性,性能高.

非关系型数据库细分如下:

键值存储数据库:代表有Redis,Voldemort和Oracle BDB等.

列存储数据库:代表有Cassandra,HBase和Riak等.

文档型数据库:代表有CouchDB和MongoDB等.

图形数据库:代表有:Neo4J,InfoGrid和Infinite Graph等.

爬取一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整.另外,数据之间还存在嵌套关系.如果用关系型数据库存储,一是需要提前建表,二是如果存在数据嵌套关系的话,需要进行序列化操作才可以存储,这非常不方便.用非关系型数据库,可以避免一些麻烦,更简单高效.下面重点介绍MongoDB和Redis数据存储操作.

(一) MongoDB存储

MongoDB是由C++编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活.

在使用之前需安装好MongoDB并启动其服务,并且要安装Python的PyMongo库.

安装PyMongo库:pip3 install pymongo

PyMongo库下载:https://pypi.python.org/pypi/pymongo

MongoDB源码安装方式参考:https://zhuanlan.zhihu.com/p/50240932


MongoDB配置文件db目录设置参考:https://www.jianshu.com/p/938539b2a9d8


1 连接MongoDB

使用pymongo库的MongoClient()方法连接MongoDB,第一个参数是IP地址Host,第二个参数是端口Port,端口默认是2710⑦例如下面所示:

import pymongo

client = pymongo.MongoClient(host='localhost', port=27107)

运行上面代码就创建MongoDB连接对象.其中host参数还可以传入连接字符串,以mongodb开头,例如:

client = pymongo.MongoClient('mongodb://michael:michael123@localhost:27017/')

2 指定数据库

MongoDB可以创建多个数据库,在使用时要指定数据库,这里以test数据库为例:

db = client.test

上面代码是调用client的test属性可返回数据库,还可以使用下面这种方式:

db = client['test']

3 指定集合

每个数据库包含很多集合(collection),类似关系型数据库中的表.

collection = db.students

collection = db['students']

4 插入数据

在students这个集合中插入数据,数据以字典形式表示:

student = {

'id': '20170101',

'name': 'Jordan',

'age': 20,

'gender': 'mail',

}

直接调用collection.insert()方法插入数据,如下所示:

result = collection.insert(student)

print(result) # 插入成功则输出ID值:

5c6fb47bf3721d3445bd09bf

在MongoDB中,每条数据都有一个_id属性来唯一标识.如果没有显式指明该属性,MongoDB会自动产生一个Objectid类型的_id 属性.insert()方法会在执行后返回_id值.

插入数据时可以同时插入多条数据,参数以列表形式传递,如下所示:

from pymongo import MongoClient

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host) # 连接Mongodb

db = client.test # 指定数据库

collection = db.students # 指定集合

student1 = {

'id': '20170101',

'name': 'Jordan',

'age': 20,

'gender': 'male',

}

student2 = {

'id': '20170102',

'name': 'Mike',

'age': 21,

'gender': 'male',

}

result = collection.insert([student1, student2]) # 插入多条数据,以列表形式指定参数

print(result)

输出信息如下,可以看出返回结果是对应的_id的集合:

[ObjectId('5c734026f3721d0f4f0be288'), ObjectId('5c734026f3721d0f4f0be289')]

在PyMongo ③x版本中,不建议使用insert()方法,面是推荐使用insert_one()和insert_many()方法来分别插入单条和多条记录,如下所示:

from pymongo import MongoClient

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host)

db = client.test

collection = db.students

result = collection.insert_one({"name":"michael", "age":20})

print(result) # 输出结果是InsertOneResult对象

print(result.inserted_id)

输出如下所示:

5c7346baf3721d117c1a132f

这次的返回结果是InsertOneResult对象,与insert()方法返回的结果是不同的.可以调用inserted_id属性获取_id值.

对于insert_many()方法将数据以列表形式传递,获取_id值是调用属性inserted_ids,如下所示:

from pymongo import MongoClient

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host) # 连接Mongodb

db = client.test # 指定数据库

collection = db.students # 指定集合

student1 = {

'id': '20170101',

'name': 'Jordan',

'age': 20,

'gender': 'male',

}

student2 = {

'id': '20170102',

'name': 'Mike',

'age': 21,

'gender': 'male',

}

result = collection.insert_many([student1, student2]) # 用insert_many方法插入多条数据

print(result) # 输出结果是:InsertManyResult

print(result.inserted_ids) # 输出是列表

输出如下所示:

[ObjectId('5c7347a3f3721d11d768a561'), ObjectId('5c7347a3f3721d11d768a562')]

5 查询

利用find_one()或find()方法进行查询,在进行查询前也需要先连接MongoDB,指定数据库,指定集合.find_one()方法查询的是单个结果,find()则返回一个生成器对象.如下所示:

from pymongo import MongoClient

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host) # 连接

db = client.test # 指定数据库

collection = db.students # 指定数据表(集合)

result = collection.find_one({'name':'Mike'}) # 利用find_one()方法查询

print(type(result)) # 字典类型

print(result) # 在结果中有_id属性

查询到的数据是字典类型,输出如下所示:

{'_id': ObjectId('5c734026f3721d0f4f0be289'), 'id': '20170102', 'name': 'Mike', 'age': 21, 'gender': 'male'}

在输出结果中,多了_id属性,这是MongoDB在插入过程中自动添加的.也可以使用ObjectID进行查询,需要借助于bson库里面的objectid:

from pymongo import MongoClient

from bson.objectid import ObjectId

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host) # 连接

db = client.test # 指定数据库

collection = db.students # 指定数据表(集合)

result = collection.find_one({'_id':ObjectId('5c734026f3721d0f4f0be289')}) # 利用find_one()方法查询

print(result) # 在结果中有_id属性

此时输出结果与前面的一样.如果结果不存在,则返回None.

要查询多条数据,可使用find()方法,例如查找age为20为数据,示例如下:

from pymongo import MongoClient

from bson.objectid import ObjectId

host = 'mongodb://michael:michael123@localhost:27017/'

client = MongoClient(host) # 连接

db = client.test # 指定数据库

collection = db.students # 指定数据表(集合)

results = collection.find({'age': 20}) # 使用find()方法查找多条数据

print(results) # 类型是Cursor类型

for result in results:

print(result)

输出如下所示:

{'_id': ObjectId('5c733e96f3721d0ec2ec3357'), 'name': 'michael', 'age': 20}

{'_id': ObjectId('5c734026f3721d0f4f0be288'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'}

输出可以看出,结果是Cursor类型,相当于一个生成器,需要遍历获取每个结果,其中的每个结果都是字典类型.如果要查询年龄大于20的数据,find()方法的写法如下所示:

results = collection.find({'age': {'$gt':20}})

查询条件的键值也不是单纯的数字,而是一个字典,其键名是比较符号$gt,其意是键值大于20.

比较符号归纳为下面的表5-3(表5-3 比较符号)

第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

除了用比较符号查询外,还可以用正则匹配查询.例如,查询name字段以M开头的数据,find()方法可改为:

results = collection.find({'name': {'$regex': '^M.*'}})

这里使用$regex指定正则匹配,^M.*代表以M开头的正则表达式.

正则表达式是功能符号,功能符号归类为表5-4(表5-4 功能符号)

第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

这些操作的详细用法,参考MongoDB官方文档:

https://docs.mongodb.com/manual/reference/operator/query/


6 计数(count())

统计查询结果的数据条数,调用count()方法.例如要统计所有数据条数:

count = collection.find().count()

print(count)

要统计符合某个条件的数据,在find()方法中添加指定的条件,例如:

count = collection.find({'age': 20}).count()

print(count)

上面2个count的值是整数值.

7 排序(sort())

直接调用查询结果的sort()方法,并传入排序字段及升序标志即可,如下所示:

results = collection.find().sort('name', pymongo.ASCENDING)

print([result['name'] for result in results])

输出如下所示:

['Jordan', 'Mike', 'michael']

参数pymongo.ASCENDING指定升序排序.要指定以降序排序,可传入pymongo.DESCENDING.

8 偏移

对排序的结果还可以调用偏移方法skip(),该方法的参数指定要偏移几个位置.例如要偏移1个元素,得到第二个及后面的元素:

results = collection.find().sort('name', pymongo.ASCENDING).skip(1)

print([result['name'] for result in results]) # 输出如下所示:

['Mike', 'michael']

另外,也可以用limit()方法指定要取的结果个数,例如下面这样:

results = collection.find().sort('name', pymongo.ASCENDING).skip(1).limit(1)

print([result['name'] for result in results]) # 输出如下所示:

['Mike']

这里使用limit()方法会限制返回结果个数,原本返回的是2个,现只返回1个.要注意的是,在数据库数量非常大的时候,如千万、亿级别,就不要使用大的偏移量来查询数据,因为这样可能导致内存溢出.可用类似下面操作来查询:

from bson.objectid import ObjectId

collection.find({'_id': {'$gt': ObjectId('5c734026f3721d0f4f0be288')}})

要这样查询,需要记录好上次查询的_id.

9 更新

要更新数据,使用update()方法,指定更新的条件和更新后的数据即可.例如:

"""使用update()方法查询数据"""

from pymongo import MongoClient

host = "mongodb://michael:michael123@localhost:27017"

client = MongoClient(host) # 连接

db = client.test # 指定数据库

collection = db.students # 指定集合(数据表)

condition = {'name': 'michael'} # 设定查询条件

student = collection.find_one(condition) # 根据查询条件查询1条数据

student['age'] = 25 # 修改age字段的值是25

result = collection.update(condition, student) # 要指定更新条件和更新值

print(result) # 输出信息:{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True}

上面代码中更新name为michael的数据的age,根据设定的查询条件,将数据查询出来,修改age值后调用update()方法将原条件和修改后的数据传入.返回结果是字典形式,ok表示执行成功,nModified代表影响的数据条数.还可以使用$set操作符对数据进行更新,例如下面这样:

result = collection.update(condition, {'$set': student})

使用$set操作符只更新student字典内存在的字段.如果该字典内还有其他字段,则不会更新,也不会删除.不用$set操作符,则会把之前的数据全部用student字典替换;如果原来存在其它字段,则会被删除.

官方不推荐使用update()方法,而是使用更严格的update_one()方法和update_many()方法,它们的第二个参数需要使用$类型操作符作为字典的键名,如下所示:

condition = {'name': 'Jordan'}

student = collection.find_one(condition)

student['age'] = 26

result = collection.update_one(condition, {'$set': student})

print(result)

print(result.matched_count, result.modified_count)

输出结果如下所示:

1 1

这里调用update_one()方法,第二个参数不能再直接传入修改后的字典,需要使用{'$set': student}的形式,返回结果是UpdateResult类型.对其调用matched_count和modified_count属性,可获得匹配的数据条数和影响的数据条数.

下面来看下update_one()方法和update_many()方法的区别.update_one()方法只操作第一条符合条件的数据.update_many()方法会操作所有符合条件的数据.例如下面示例:

# 使用update_one()方法

condition = {'age': {'$gt': 20}}

result = collection.update_one(condition, {'$inc': {'age': 1}}) # $inc是满足条件对应后面的值加1

print(result)

print(result.matched_count, result.modified_count)

# 使用update_many()方法

condition = {'age': {'$gt': 20}}

result = collection.update_many(condition, {'$inc': {'age': 1}}) # $inc是满足条件对应后面的值加1

print(result)

print(result.matched_count, result.modified_count)

这里指定查询条件age为大于20,然后更新条件为{'$inc': {'age': 1}},即age的值加1,执行后将第一条符合条件的数据加1,update_one()方法运行结果如下所示:

1 1

update_many()方法运行结果如下所示:

4 4

从输出可知,只要匹配到的数据都会被更新.

10 删除

直接调用remove()方法指定删除的条件即可,此时符合条件的所有数据均会被删除.例如:

result = collection.remove({'name': 'Kevin'})

print(result) # 输出:{'n': 1, 'ok': 1.0}

remove方法不常用,推荐使用delete_one()和delete_many()方法,示例如下:

result = collection.delete_one({'name': 'Kevin'})

print(result) # 此时的类型是:DeleteResult类型

print(result.deleted_count)

result = collection.delete_many({'age': {'$lt': 25}})

print(result.deleted_count)

输出如下所示:

1

6

从输出可知,delete_one()删除第一条符合条件的数据,delete_many()删除所有符合条件的数据.它们的结果类型都是DeleteResult类型.调用deleted_count属性可获取删除的数据条数.

除了上面的基本操作外,PyMongo还提供一些组合方法,如find_one_and_delete()、fine_one_and_replace()和find_one_and_update(),依次是查找后删除、替换和更新操作,用法与前面的方法基本一致.此外,还有对索引进行操作的方法,有create_index()、create_indexes()和drop_index()等.

PyMongo详细用法参考官方文档:http://api.mongodb.com/python/current/api/pymongo/collection.html.

对数据库和集合本身的操作,参考官方文件:http://api.mongodb.com/python/current/api/pymongo/

(二) Redis存储

Redis是基于内存的高效键值型非关系型数据库,存取效率极高,支持多种存储数据结构,使用简单.下面说下Python的Redis操作,主要以redis-py库的用法作说明.

首先要正确安装Redis及redis-py库.要做数据导入/导出操作,还要安装RedisDump.

redis-py库有两个类Redis和StrictRedis来实现Redis的命令操作.

StrictRedis有大部分官方命令, 参数也一 一对应,比如set()方法就对应Redis命令的set方法.Redis是StrictRedis的子类,它的主要功能是用于向后兼容旧版本库里的几个方法.为了做兼容,它将方法做了改写,比如lrem()方法就将value和num参数的位置互换,这和Redis命令行的命令参数不一致.

官方推荐使用StrictRedis,此时此刻呢也用StrictRedis类的相关方法作介绍.

1 连接Redis

Redis正常运行在6379端口,无密码,下面进行Redis连接测试:

from redis import StrictRedis

redis = StrictRedis(host='localhost', port=6379, db=0)

redis.set('name', 'Bob')

print(redis.get('name')) # 输出:b'Bob'

这段连接测试代码中,StrictRedis的参数依次是:主机地址、端口、使用的数据库.如果有密码的话,后面还可以传密码参数password='foobared',这里假设密码是foobared.这4个参数默认不传时,分别默认为:localhost、6379、0和None.首先声明redis为StrictRedis对象,调用其set()方法设置键值对.运行程序未报错,则表示连接成功,同时可以执行set()和get()操作.

还可以使用ConnectionPool(连接池)来连接,如下面代码所示:

from redis import StrictRedis, ConnectionPool

pool = ConnectionPool(host='localhost', port=6379, db=0) # 有密码时也可以今天这一节传

redis = StrictRedis(connection_pool=pool)

在StrictRedis内其实就是用host和port等参数又构造一个ConnectionPool,所以直接将ConnectionPool当做参数传给StrictRedis也一样.ConnectionPool还可以通过URL构建.URL格式支持下面3种:

redis://[:password]@host:port/db

rediss://[:password]@host:port/db

unix://[:password]@/path/to/socket.sock?db=db

这3种URL分别表示创建Redis TCP连接、Redis TCP◆SSL连接、Redis UNIX socket连接.当有password时可以写,没有时可以省略.使用URL连接实例如下:

url = 'redis://@localhost:6379/0'

pool = ConnectionPool.from_url(url)

redis = StrictRedis(connection_pool=pool)

先声明一个Redis连接字符串,然后调用from_url()方法创建ConnectionPool,接着将其传给StrictRedis就可完成连接.

2 键操作

Redis的键操作如图5-1所示:



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-1 Reids键的一些判断和操作方法

3 字符串操作

Redis支持基本的键值对形式存储,用法如图5-2所示:



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-2 Redis用法总结

4 列表操作

Redis提供了列表存储,列表内元素可以重复,可以从两端存储,用法如图5-3所示.



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-3 Redis列表操作

5 集合操作

Redis提供了集合存储,集合中的元素不能重复,用法如图5-4所示.



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-4 Redis集合操作

6 有序集合

有序集合比集合多了一个分数字段,利用它可以对集合中的数据进行排序,用法总结如图5-5所示.



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-5 Redis有序集合操作

7 散列操作

Redis还提供了散列表的数据结构.可用name指定散列表的名称,表内存储各个键值对,用法总结如图5-6所示.



第五部分_三)   数据存储_非关系型数据库存储:MongoDB存储、Redis存储)

图5-6 Redis散列操作

8 RedisDump,Redis数据的导入和导出功能

RedisDump有强大的Redis数据的导入和导出功能.要使用该功能需要先进行安装.RedisDump基于Ruby实现,在安装前要先安装Ruby.

RedisDump的GitHub连接和官方连接如下:

GitHub: https://github.com/delano/redis-dump

Redis官方:http://delanotes.com/redis-dump

Ruby的安装参考:http://www.ruby-lang.org/zh_cn/documentation/installation

安装完Ruby后可使用gem命令安装RedisDump,命令如下:

gem install redis-dump

RedisDump提供的两个命令:redis-dump是导出数据,redis-load是导入数据.

(1) redis-dump

redis-dump -h ,输入这个命令可以看所有可选项.其中 –u 代表Redis连接字符串,-d 代表数据库代号,-s 代表导出之后的休眠时间,-c 代表分块大小,默认是10000,-f 代表导出时的过滤器,-O 代表禁用运行时优化,-V 用于显示版本,-D 表示开启调试.

导出测试,有密码和无密码的使用方式如下所示:

redis-dump -u :foobared@localhost:6379 # 有密码,密码是:foobared

redis-dump -u localhost:6379 # 无密码

运行上面命令,可将本地0到15号数据库的所有数据导出来,输出如下所示:

{"db":0,"key":"name","ttl":-1,"type":"string","value":"michael","size":7}

每条数据包含6个字段,其中db即数据库代号,key即键名,ttl即该键值对的有效时间,type即键值类型,value即内容,size是占用空间.

要导出所有数据库的所有数据为JSON文件,可使用如下命令:

redis-dump -u localhost:6379 > ./redis_data.json

可使用 -d 参数指定某个数据库的导出,例如要导出10号数据库内容,命令如下:

redis-dump -u localhost:6379 -d 10 > ./py_data.json

要导出特定的内容,可使用 –f 参数过滤,并指出过滤条件,例如要导出以py开头的数据,命令如下:

redis-dump -u localhost:6379 -f py:* > ./py_data.json

(2) redis-load

redis-load -h ,使用该命令查看所有可选项.其中 –u 是Redis连接字符串,-d 是数据库代号,默认是全部,-s 是导入之后的休眠时间,-n 是不检测UTF-8编码,-V 是显示版本,-D 是开启调试.

将JSON行文件导入到Redis数据库中,命令如下:

< redis_data.json redis-load -u @localhost:6379

在linux下使用下面的命令是同样的效果:

cat redis_data.json | redis-load -u @localhost:6379

Redis可实现很多架构,如维护代理池、Cookies池、ADSL拨号代理池、Scrapy-Redis分布式架构等,对于Redis的操作需要熟练.

以上就是土嘎嘎小编为大家整理的第五部分_三) 数据存储_非关系型数据库存储:MongoDB存储、Redis存储)相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!

版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章