Python redis 使用介绍(通过源码查看怎么用)
redis 是一个 Key-Value 数据库,Value 支持 string(字符串),list(列表),set(集合),zset(有序集合),hash(哈希类型)等类型。
1、安装启动 redis
1.1 用brew安装
1.查看系统是否已经安装了Redis
brew info redis
这个命令会展示此系统下的redis信息,如果没有安装,会展示not install
2.输入命令安装Redis
brew install redis
可能需要等一会,系统下载完redis的包,会自动进行安装
3.启动redis
brew services start redis
这个命令会在后台启动redis服务,并且每一次登录系统,都会自动重启
如果你不想/不需要后台服务,你可以运行:
/usr/local/opt/redis/bin/redis-server /usr/local/etc/redis.conf
4.查看redis服务进程
可以通过下面命令查看redis是否正在运行
ps axu | grep redis
默认端口号为 6379,ctrl+D 退出
这个命令会读取redis的配置文件,并且在redis运行的过程中也会看到实时的日志打印。
2、redis 模块(Python)
redis 提供两个类 Redis 和 StrictRedis
- StrictRedis 用于实现大部分官方的命令
- Redis 是 StrictRedis 的子类,用于向后兼用旧版本。
redis 取出的结果默认是字节,可以设定 decode_responses=True 改成字符串。
2.1 连接池
redis-py 使用 connection pool 来管理对一个 redis server 的所有连接,避免每次建立、释放连接的开销。
默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数 Redis,这样就可以实现多个 Redis 实例共享一个连接池。
3、redis 基本命令 String
set(name, value, ex=None, px=None, nx=False, xx=False)
在 Redis 中设置值,默认,不存在则创建,存在则修改。
参数:
- ex - 过期时间(秒)
- px - 过期时间(毫秒)
- nx - 如果设置为True,则只有name不存在时,当前set操作才执行
- xx - 如果设置为True,则只有name存在时,当前set操作才执行
1.setnx(name, value) 设置值,只有name不存在时,执行设置操作(添加)
2.setex(name, time, value) 设置值
- time - 过期时间(数字秒 或 timedelta对象)
import redis import time pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.setex("fruit2", 5, "orange") print(r.get('fruit2')) time.sleep(5) print(r.get('fruit2')) # 5秒后,取值就从orange变成None # 输出结果 orange None
3.psetex(name, time_ms, value) 设置值
- time_ms - 过期时间(数字毫秒 或 timedelta对象)
4.mset(self, mapping) 批量设置值
r.mset({'k1': 'v1', 'k2': 'v2'}) print(r.mget("k1", "k2")) # 一次取出多个键对应的值 print(r.mget("k1")) # 输出结果 ['v1', 'v2'] ['v1']
5.mget(keys, *args) 批量获取
print(r.mget('k1', 'k2')) print(r.mget(['k1', 'k2'])) print(r.mget("fruit", "fruit1", "fruit2", "k1", "k2")) # 将目前redis缓存中的键对应的值批量取出来 # 输出结果 ['v1', 'v2'] ['v1', 'v2'] ['watermelon', None, 'orange', 'v1', 'v2']
6.getset(name, value) 设置新值并获取原来的值
print(r.getset("food", "barbecue")) # 设置的新值是barbecue 设置前的值是beef
7.getrange(key, start, end) 获取子序列(根据字节获取,非字符)
- start - 起始位置(字节)
- end - 结束位置(字节)
r.set("cn_name", "君惜大大") # 汉字 print(r.getrange("cn_name", 0, 2)) # 取索引号是0-2 前3位的字节 君 切片操作 (一个汉字3个字节 1个字母一个字节 每个字节8bit) print(r.getrange("cn_name", 0, -1)) # 取所有的字节 君惜大大 切片操作 r.set("en_name","junxi") # 字母 print(r.getrange("en_name", 0, 2)) # 取索引号是0-2 前3位的字节 jun print(r.getrange("en_name", 0, -1)) # 取所有的字节 junxi
8.setrange(name, offset, value) 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
- offset - 字符串的索引,字节(一个汉字三个字节)
- value - 要设置的值
r.setrange("en_name", 1, "ccc") print(r.get("en_name")) # jccci 原始值是junxi 从索引号是1开始替换成ccc 变成 jccci
9.strlen(name) 返回name对应值的字节长度(一个汉字3个字节)
print(r.strlen("foo")) # 4 'goo1'的长度是4
10.incr(self, name, amount=1) 自增 name 对应的值,当 name 不存在时,则创建 name=amount,否则,则自增。
- name - Redis的name
- amount - 自增数(必须是整数)
注:同 incrby
r.set("foo", 123) print(r.mget("foo", "foo1", "foo2", "k1", "k2")) r.incr("foo", amount=1) print(r.mget("foo", "foo1", "foo2", "k1", "k2"))
应用场景 – 页面点击数
假定对一系列页面需要记录点击次数。例如论坛的每个帖子都要记录点击次数,而点击次数比回帖的次数的多得多。如果使用关系数据库来存储点击,可能存在大量的行级锁争用。所以,点击数的增加使用redis的INCR命令最好不过了。
当redis服务器启动时,可以从关系数据库读入点击数的初始值(12306这个页面被访问了34634次)
r.set("visit:12306:totals", 34634) print(r.get("visit:12306:totals"))
每当有一个页面点击,则使用INCR增加点击数即可。
r.incr("visit:12306:totals") r.incr("visit:12306:totals")
页面载入的时候则可直接获取这个值
print(r.get("visit:12306:totals"))
11.decr(self, name, amount=1) 自减 name 对应的值,当 name 不存在时,则创建 name=amount,否则,则自减。
- name - Redis的name
- amount - 自减数(整数)
注:同 decrby
r.decr("foo4", amount=3) # 递减3 r.decr("foo1", amount=1) # 递减1 print(r.mget("foo1", "foo4")) # 输出结果 ['124', '-3']
12.append(key, value) 在redis name对应的值后面追加内容
- key - redis的name
- value - 要追加的字符串
r.append("name", "haha") # 在name对应的值junxi后面追加字符串haha print(r.mget("name"))
4、redis 基本命令 hash
1、单个增加–修改(单个取出)–没有就新增,有的话就修改
hset(name, key, value, mapping=None)
name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
- name - redis的name
- key - name对应的hash中的key
- value - name对应的hash中的value
- mapping - 接受一个由键/值对组成的字典
注:hsetnx(name, key, value) 当name对应的hash中不存在当前key时则创建(相当于添加)
import redis import time pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.hset("hash1", "k1", "v1") r.hset("hash1", "k2", "v2") print(r.hkeys("hash1")) # 取hash中所有的key print(r.hget("hash1", "k1")) # 单个取hash的key对应的值 print(r.hmget("hash1", "k1", "k2")) # 多个取hash的key对应的值 print(r.hsetnx("hash1", "k2", "v3")) # 只能新建 如果HSETNX创建了一个字段,返回1,否则返回0 print(r.hget("hash1", "k2")) # 输出结果 ['k1', 'k2'] v1 ['v1', 'v2'] 0 v2
2、批量增加(取出)
hmset(name, mapping)
在name对应的hash中批量设置键值对
- name - redis的name
- mapping - 字典
r.hmset("hash2", {"k2": "v2", "k3": "v3"}) hget(name,key)
在name对应的hash中获取根据key获取value
hmget(name, keys, *args)
在name对应的hash中获取多个key的值
- name - reids对应的name
- keys - 要获取key集合,如:[‘k1’, ‘k2’, ‘k3’]
- *args - 要获取的key,如:k1,k2,k3
print(r.hget("hash2", "k2")) # 单个取出"hash2"的key-k2对应的value print(r.hmget("hash2", "k2", "k3")) # 批量取出"hash2"的key-k2 k3对应的value --方式1 print(r.hmget("hash2", ["k2", "k3"])) # 批量取出"hash2"的key-k2 k3对应的value --方式2
3、取出所有的键值对
hgetall(name)
获取name对应hash的所有键值
print(r.hgetall("hash1")) # 输出结果 {'k1': 'v1', 'k2': 'v2'}
4、得到所有键值对的格式 hash长度
hlen(name)
获取name对应的hash中键值对的个数
print(r.hlen("hash1"))
5、得到所有的keys(类似字典的取所有keys)
hkeys(name)
获取name对应的hash中所有的key的值
print(r.hkeys("hash1"))
6、得到所有的value(类似字典的取所有value)
hvals(name)
获取name对应的hash中所有的value的值
print(r.hvals("hash1"))
7、判断成员是否存在(类似字典的in)
hexists(name, key)
检查 name 对应的 hash 是否存在当前传入的 key
print(r.hexists("hash1", "k4")) # False 不存在 print(r.hexists("hash1", "k1")) # True 存在
8、删除键值对
hdel(name,*keys)
将name对应的hash中指定key的键值对删除
print(r.hgetall("hash1")) r.hset("hash1", "k2", "v222") # 修改已有的key k2 r.hset("hash1", "k11", "v1") # 新增键值对 k11 r.hdel("hash1", "k1") # 删除一个键值对 print(r.hgetall("hash1"))
9、自增自减整数(将key对应的value–整数 自增1或者2,或者别的整数 负数就是自减)
hincrby(name, key, amount=1)
自增name对应的hash中的指定key的值,不存在则创建key=amount
参数:
- name - redis中的name
- key - hash对应的key
- amount - 自增数(整数)
r.hset("hash1", "k3", 123) r.hincrby("hash1", "k3", amount=-1) print(r.hgetall("hash1")) r.hincrby("hash1", "k4", amount=1) # 不存在的话,value默认就是1 print(r.hgetall("hash1"))
10、自增自减浮点数(将key对应的value–浮点数 自增1.0或者2.0,或者别的浮点数 负数就是自减)
hincrbyfloat(name, key, amount=1.0)
自增name对应的hash中的指定key的值,不存在则创建key=amount
参数:
- name - redis中的name
- key - hash对应的key
- amount,自增数(浮点数)
自增 name 对应的 hash 中的指定 key 的值,不存在则创建 key=amount。
r.hset("hash1", "k5", "1.0") r.hincrbyfloat("hash1", "k5", amount=-1.5) # 已经存在,递减-1.5 print(r.hgetall("hash1")) r.hincrbyfloat("hash1", "k6", amount=-1.0) # 不存在,value初始值是-1.0 每次递减1.0 print(r.hgetall("hash1")) # 输出结果 {'k2': 'v222', 'k11': 'v1', 'k3': '123', 'k4': '1', 'k5': '-0.5', 'k6': '-1'} {'k2': 'v222', 'k11': 'v1', 'k3': '123', 'k4': '1', 'k5': '-0.5', 'k6': '-2'}
11、取值查看–分片读取
hscan(name, cursor=0, match=None, count=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而防止内存被撑爆
- name - redis的name
- cursor - 游标(基于游标分批取获取数据)
- match - 匹配指定key,默认None 表示所有的key
- count - 每次分片最少获取个数,默认None表示采用Redis的默认分片个数
第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None) 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None) ... 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕 print(r.hscan("hash1")) # 输出结果 (0, {'k2': 'v222', 'k11': 'v1', 'k3': '123', 'k4': '1', 'k5': '-0.5', 'k6': '-5'})
12、hscan_iter(name, match=None, count=None)
利用yield封装hscan创建生成器,实现分批去redis中获取数据
- match - 匹配指定key,默认None 表示所有的key
- count - 每次分片最少获取个数,默认None表示采用Redis的默认分片个数
for item in r.hscan_iter('hash1'): print(item) print(r.hscan_iter("hash1")) # 生成器内存地址 # 输出结果 ('k2', 'v222') ('k11', 'v1') ('k3', '123') ('k4', '1') ('k5', '-0.5') ('k6', '-6') <generator object ScanCommands.hscan_iter at 0x7f94486cc270>
5、redis基本命令 list
1.增加(类似于list的append,只是这里是从左边新增加)–没有就新建
lpush(name,values)
在name对应的list中添加元素,每个新的元素都添加到列表的最左边
import redis import time pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.lpush("list1", 11, 22, 33) print(r.lrange('list1', 0, -1)) 保存顺序为: 33,22,11
rpush:增加(从右边增加)–没有就新建
r.rpush("list2", 44, 55, 66) # 在列表的右边,依次添加44,55,66 print(r.llen("list2")) # 列表长度 print(r.lrange("list2", 0, -1)) # 切片取出值,范围是索引号0到-1(最后一个元素)
2.往已经有的name的列表的左边添加元素,没有的话无法创建
lpushx(name,value)
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
r.lpushx("list10", 10) # 这里list10不存在 print(r.llen("list10")) # 0 print(r.lrange("list10", 0, -1)) # [] r.lpushx("list2", 77) # 这里"list2"之前已经存在,往列表最左边添加一个元素,一次只能添加一个 print(r.llen("list2")) # 列表长度 print(r.lrange("list2", 0, -1)) # 切片取出值,范围是索引号0到-1(最后一个元素
rpushx:往已经有的name的列表的右边添加元素,没有的话无法创建
r.rpushx("list2", 99) # 这里"foo_list1"之前已经存在,往列表最右边添加一个元素,一次只能添加一个 print(r.llen("list2")) # 列表长度 print(r.lrange("list2", 0, -1)) # 切片取出值,范围是索引号0到-1(最后一个元素)
3.新增(固定索引号位置插入元素)
linsert(name, where, refvalue, value))
在name对应的列表的某一个值前或后插入一个新值
- name - redis的name
- where - before/after
- refvalue - 标杆值,即:在它前后插入数据
- value - 要插入的数据
r.linsert("list2", "before", "11", "00") # 往列表中左边第一个出现的元素"11"前插入元素"00" print(r.lrange("list2", 0, -1)) # 切片取出值,范围是索引号0-最后一个元素