0%

Python-非关系数据库

非关系数据的基本操作,如:

  • redis:key-value 内存数据库
  • mongodb:备用的非关系数据库(首选 redis)
  • TinyDB:一个微型的、面向文档型数据库
  • ZODB:一个 Python 原生对象数据库

概述

redis

redis:key-value 内存数据库

  • 特点:
    • 高效
1
2
3
4
34MB 的数据,275 张图像,0.044 秒,每张 0.2 毫秒
常规语句,一万次 0.35 秒
4.8MB 的图像,存字符串,使用 base64,约 7MB,需 5-7ms 左右
单个小字符串数据从 redis 存取、不到 1ms
  • 原子性:
  • 五大数据结构
  • 稳定性:可持久化、主从复制
  • 特性:过期时间
  • 应用场景:
    • 构建队列、最新 n 个数据:list 进行添加
    • 排行榜,top N:zset
    • 时效性,如验证码:Expire
    • 计数器,秒杀:原子性,incr, decr
    • 去除重复:set
    • 发布订阅消息系统:pub/sub 模式

基本使用

1
2
3
4
5
6
7
8
9
10
pip install redis

import redis

# 存储相关配置值:检测输入
pool = redis.ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
r_cli = redis.Redis(connection_pool=pool)
# 存储相关图像、结果:检测输出
pool_db1 = redis.ConnectionPool(host='localhost', port=6379, db=1, decode_responses=True)
r_cli_db1 = redis.Redis(connection_pool=pool_db1)
  • Redis 与 StrictRedis 类:
    • StrictRedis 用于实现大部分官方的命令
    • Redis 是 StrictRedis 的子类,用于向后兼用旧版本。
  • 不存在,会创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import redis
# 使用连接池来管理对 redis server 的所有连接,避免每次建立、释放的开销
pool = redis.ConnectionPool(host='localhost', port=6379,db=0,decode_responses=True)

# redis 取值默认为字节,可设置为字符串
r=redis.Redis(host='localhost',port=6379,db=0,decode_responses=True)
r=redis.Redis(connection_pool=pool) # 建议用这个
# r=redis.StrictRedis(host='localhost',port=6379,db=0)

r.set('name','chen')
r.get('name')

pool=redis.ConnectionPool(host="localhost",port=6379) #连接池
r=redis.Redis(connection_pool=pool)
r.set("name","chen")
print(r.get("name"))



# chen.use
import redis

pool_db1 = redis.ConnectionPool(host='localhost', port=6379, db=1, decode_responses=True)
r_cli_db1 = redis.Redis(connection_pool=pool_db1)


print(r_cli_db1.delete('xx'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 键的数量
r_conn.dbsize()
# 键是否存在
r.exists('key_name')
# 键的数据类型
r_conn.type(key_name)
# 根据 key_name 删除
r_conn.delete('key_name')
# 获取所有符合规则的键:* ?等通配获取 name
r_conn.keys(pattern='*')
# 为某个 key_name 设置超时时间
r_conn.expire(key_name,time)
# 查看 key_name的过期时间
r_conn.ttl(key_name) # 单位:秒,数据类型:int
r_conn.pttl(key_name) # 单位:毫秒,数据类型:int

# 重命名键
r_conn.rename(key_name,new_key_name)
# 将 key_name 移到指定 db 下
r_conn.move(key_name,db)
# 随机获取一个 redis 的 key_name
r_conn.randomkey()

# 删除当前数据库中所有键
r_conn.flushdb()
# 删除所有数据库中的所有键
r_

字符串-string

  • 可以是字符串、整数或浮点数。
  • 最基本的数据类型,最大 512M
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
r.set(key_name, # 键
value, # 待传入的值
ex=None, # 过期时间,秒,过期时值会变成 None
px=None, # 过期时间,毫秒
nx=False, # 若为 True,只有 key_name 不存在时才 set(不存在时新建)
xx=Flase) # 若为 True,只有 key_name 存在时才 set(存在时修改)
# r.setnx(key_name,value) # key_name 不存在时才 set
# r.setex(name,time,value) # 设置值
# r.psetex(name, time_ms, value) # 毫秒级过期
get(key_name) # 获取值
getset(key_name,value) # 为指定的键设置新的值,并返回旧值


r.getrange('name', 0, 1) # 获取子序列 0 <= x <= 1
r.mset(key_value_dict) # 一次性设置多个键值对
r.mget('k1', 'k2', 'k3', 'k4')
r.incr(key_name,amount=1) # 自增,若键不存在,值为 amount
r.incrbyfloat(key_name,amount=1.0) # 自增,若键不存在,值为 amount

r.decr(key_name,amount=1) # 自减,若键不存在,值为 amount

r.append(key,value) # 在指定的字符串后追加值,不存在则创建
r.setrange(key_name,offset,value) # 用 value 覆写 key 存储的字符串值,从偏移量 offset 开始(新值太长时,则向后添加),返回值的长度

r.strlen(key_name) # 返回字符串的长度,key 不存在时为 0

列表 -list

  • 双向链表,对两端操作性能高,下标索引不佳
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 增
r.lpush('students','chen') # 左侧添加
r.lpush('students','chen','zhang','li') # 左侧添加,['li','zhang','chen']

lpushx()
rpushx()
# 删
r.blpop() # 左侧删除
r.brpop() # 右侧删除
# 改

# 查

lpush(key_name,value) # 左侧添加
lrange(key_name,start,end)
lindex(key_name,index)

r.lpush('oo', 11) # 保存顺序为: 33,22,11
r.lpushx('oo', 00) # 在 name 对应的 list 中添加元素,只有 name 已经存在时,值添加到列表的最左边
print(r.llen('oo')) # name 对应的 list 元素的个数

r.linsert('oo', 'before', 11, 99) # 在 11 之前插入值 99
r.lset('oo', 1, 88) # 对 name 对应的 list 中的某一个索引位置重新赋值
print(r.lrange('oo', 0, -1)) # 在 name 对应的列表分片获取数据

r.lrem('oo', 88, num=1) # 在 name 对应的 list 中删除指定的值.num=0,删除列表中所有的指定值;num=2,从前到后,删除 2 个;num=-2,从后向前,删除 2 个
print(r.lrange('oo', 0, -1))

print(r.lpop('oo')) # 在 name 对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
print(r.lindex('oo', 0)) # 在 name 对应的列表中根据索引获取列表元素
r.lpush('l1', 11) # index 为 0
r.rpush('l1', 22)
r.rpush('l1', 33)
r.rpush('l1', 44)
r.rpush('l1', 55) # index 为 4
r.ltrim('l1', 1, 3) # 在 name 对应的列表中移除没有在[start-end]索引之间的值
print(r.lrange('l1', 0, -1))

r.rpoplpush('l1', 'l1') # 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边;src 要取数据的列表的 name, dst 要添加数据的列表的 name
print(r.lrange('l1', 0, -1))

r.brpoplpush('l1', 'l1', timeout=3) # # timeout,当 src 对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
print(r.lrange('l1', 0, -1))

print(r.blpop('l1', 3)) # 从列表头部取出第一个元素,返回该元素值并从列表删除(l 代表 left,左边)
print(r.lrange('l1', 0, -1))

哈希 -hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
r.hexists('chen','age') # 检测 hash 指定键是否存在

r.hset('chen','age',18) # hset(name, key, value),name 对应的 hash 中设置一个键值对(不存在,则创建;否则,修改)
r.hget('chen','age') # 获取指定键
r.hdel('chen','age') # 删除的指定键

r.hkeys('chen') # 返回 hash 所有键,list
r.hvals('chen') # 返回 hash 的所有键的值,list

r.hlen('chen') # 返加 hash 的键的个数
r.hgetall('chen') # 获取所有键值对,返回 dict

r.hincrby('chen','age',amount=1) # 为 hash 的指定键对应的值加上指定的整数,不存在时先存为 0,再加
r.hincrbyfloat('chen','weight',amount=1.0)
hsetnx('chen','age') # 不存在时赋值,存在时失败

r.hexists(name,key) # name 对应的 hash 里是否存在该 key

# DeprecationWarning: Redis.hmset() is deprecated. Use Redis.hset() instead.
r.hmset('n2', {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}) # hmset(name, mapping),在 name 对应的 hash 中批量设置键值对
print(r.hmget('n2', 'k2'))
print(r.hgetall('n2')) # 获取 name 对应 hash 的所有键值
print(r.hlen('n2')) # 获取 name 对应的 hash 中键值对的个数
print(r.hkeys('n2')) # 获取 name 对应的 hash 中所有的 key 的值
print(r.hvals('n2')) # 获取 name 对应的 hash 中所有的 value 的值
print(r.hexists('n2', 'k4')) # 检查 name 对应的 hash 是否存在当前传入的 key
r.hdel('n2', 'k3') # 将 name 对应的 hash 中指定 key 的键值对删除
r.hset('n3', 'k1', 1)
r.hincrby('n3', 'k1', amount=1) # hincrby(name, key, amount=1),自增 name 对应的 hash 中的指定 key 的 value 的值,不存在则创建 key=amount
print(r.hgetall('n3'))

集合 -set

  • 添加,删除,查找的复杂度都是 O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 增
r.sadd('set_name', 'var1', 'var2', 'var3')

# 删
r.srem('set_name1','var1')
r.spop('set_name1') # 随机删除一个元素

# 改
r.smove('set_name1','set_name2','var1') # 将集合 1 中的成员 var1 移至集合 2

# 查
r.scard('set_name') # 集合的元素个数
r.smembers('set_name') # 获取集合的所有元素
r.srandmember('set_name',nums) # 从集合中随机获取 nums 个元素

# 检测是否在集合中
r.sismember('set_name','var_name') # 元素是否在集合中

# 交集
r.sinter('set_name1','set_name2') # 返回给定集合的交集
r.sinterstore('set_name3','set_name1','set_name2') # 将 set_name1 与 set_name2 的效集存放到 set_name3
# 并集
r.sunion('set_name1','set_uname2')
r.sunionstore('set_name3','set_name1','set_name2')

# 差集
r.sdiff('set_name1','set_name2') # 在第一个集合中且不在第二个集合中的元素集合
r.sdiffstore('set_name3','set_name1','set_name2') # 获取在第一个集合中且不在其他集合中的元素集合并添加到第一个集合中

有序集合 -zset

在集合的基础上,为每个元素排序,排序按另一个值来比较,即每个元素有两个值:值与分数,分数用来排序

  • 相比于常规的集合,有序集合的每个成员关联了一个评分(score),该评分被用来按照从最低分到最高分的方式排序集合中的成员,集合的成员是唯一的,但是评分可以是重复的
    • 因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 增
r.zadd(key_name,"a1",5,"a2",6,"a3",3)
r.zadd(key_name,b1=2,b2=10)
# 删
r.zrem('z1', '66') # 删除 name 对应的有序集合中值是 values 的成员
r.zremrangebyrank('z1', 0, 1) # 根据排行范围删除
r.zremrangebyscore('z1', 4.5, 5.5) # 根据分数范围删除

# 改
r.zincrby(key_name, '11', amount=5) # 自增 name 对应的有序集合的 name 对应的分数

# 查
r_conn.zcard(key_name) # 获取 zset 数量
r_conn.zcount(key_name,min,max) # 分数在[min,max]之间的个数
r.zrank('z1', 33) # 获取某个值在 name 对应的有序集合中的排行(从 0 开始)
r.zscore('z1', 11)) # 获取 name 对应有序集合中 value 对应的分数
r.zrange('z1', 0, -1, desc=False, withscores=True) # # 值 11 被排序到最后;此处表示按元素的值升序排列

# 交集
获取两个有序集合的交集并放入 dest 集合,如果遇到相同值不同分数,则按照 aggregate 进行操作
aggregate 的值为: SUM MIN MAX
r.zinterstore('zset_name2', ('zset_name', 'zset_name1'), aggregate='Sum')

消息队列应用

  • 基于 rpush/lpop 或 lpush/rpop 实现简单队列
  • 基于 blpop 或 brpop 实现阻塞读取队列
  • 发布/订阅:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 发布者
import redis
client = redis.StrictRedis()
client.publish("stu", "tom")

# 订阅者
import time
import redis

client = redis.StrictRedis()
p = client.pubsub()
p.subscribe("stu")
while True:
msg = p.get_message()
if not msg:
time.sleep(1)
continue
print(msg)

# 监听模式
```python
import redis
client = redis.StrictRedis()
p = client.pubsub()
p.subscribe("stu")
for msg in p.listen():
print(msg)
  • stream:redis5.0 后的,强大的支持多播的可持久化的消息队列

mongodb

mongodb:备用的非关系数据库(首选 redis)

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pymongo
from pymongo import MongoClient
import json
import datetime
# client= MongoClient('mongodb://localhost:27017')
client = MongoClient('localhost', 27017)
client = MongoClient('mongodb://username:password@localhost:27017/dbname')
db = client.chener #
person = db.person #
person_info = [{'name': 'chen', 'birth': '1111'},
{'name': 'gao', '学号': '2016206'}]
result = person.insert_many(person_info)
print(result.inserted_ids) # 批量插入有 instered_ids
person_id = person.insert_one({'name': 'chen2'}).inserted_id
print(person_id)
# aa=person.insert_one(post).inserted_id
print(person.find_one())
insert_one()
insert_many()
result1 = person.delete_one({'name': 'gao'})
print(result1)
result2 = person.delete_many({'name': 'chen2'})
print(result2)

#
res = person.update_one({'name': 'chen'}, {'$set': {'birth': '20221111'}})
print(res.matched_count)
person.update_many({'name': 'chen'}, {'$set': {'birth': '1111'}})

#
print(person.find())
print(person.find({'sex': 'boy'}).sort('name'))

# 聚合统计可视化 o

TinyDB

TinyDB:一个微型的、面向文档型数据库

ZODB

ZODB:一个 Python 原生对象数据库

  • 一个键值和对象图数据库。
给博主来一杯卡布奇诺