Redis介绍
- 特点及优点
1、开源的,使用C编写,基于内存且支持持久化
2、高性能的Key-Value的NoSQL数据库
3、支持数据类型丰富,字符串strings,散列hashes,列表lists,集合sets,有序集合sorted sets 等等
4、支持多种编程语言(C C++ Python Java PHP ... )
5、单进程单线程
- 与其他数据库对比
1、MySQL : 关系型数据库,表格,基于磁盘,慢
2、MongoDB:键值对文档型数据库,值为类似JSON文档,数据结构相对单一
3、Redis的诞生是为了解决什么问题??# 解决硬盘IO带来的性能瓶颈
- 应用场景
1,缓存
2,并发计数
3,排行榜
4,生产者消费者模型
...
- redis版本
1、最新版本:5.0
2、常用版本:2.4、2.6、2.8、3.0(里程碑)、3.2、3.4、4.0(教学环境版本)、5.0
- Redis附加功能
1、持久化将内存中数据保存到磁盘中,保证数据安全,方便进行数据备份和恢复
2、过期键功能为键设置一个过期时间,让它在指定时间内自动删除<节省内存空间># 音乐播放器,日播放排名,过期自动删除
3、事务功能原子的执行多个操作
4、主从复制
5、Sentinel哨兵
安装
- Ubuntu
# 安装
sudo apt-get install redis-server
# 服务端启动
sudo /etc/init.d/redis-server status | start | stop | restart
# 客户端连接
redis-cli -h IP地址 -p 6379 -a 密码
配置文件详解
- 配置文件所在路径
/etc/redis/redis.conf
mysql的配置文件在哪里? : /etc/mysql/mysql.conf.d/mysqld.cnf
- 设置连接密码
1、requirepass 密码
2、重启服务sudo /etc/init.d/redis-server restart
3、客户端连接redis-cli -h 127.0.0.1 -p 6379 -a 123456127.0.0.1:6379>ping
- 允许远程连接
1、注释掉本地IP地址绑定69行: # bind 127.0.0.1 ::1
2、关闭保护模式(把yes改为no)88行: protected-mode no
3、重启服务sudo /etc/init.d/redis-server restart
- 通用命令 适用于所有数据类型
# 切换库(number的值在0-15之间,db0 ~ db15)
select number
# 查看键
keys 表达式 # keys *
# 数据类型
type key
# 键是否存在
exists key
# 删除键
del key
# 键重命名
rename key newkey
# 清除当前库中所有数据(慎用)
flushdb
# 清除所有库中所有数据(慎用)
flushall
数据类型
字符串类型(string)
- 特点
1、字符串、数字,都会转为字符串来存储
2、以二进制的方式存储在内存中
- 字符串常用命令-必须掌握
# 1. 设置一个key-value
set key value
# 2. 获取key的值
get key
# 3. key不存在时再进行设置(nx)
set key value nx # not exists
# 4. 设置过期时间(ex)
set key value ex seconds# 5. 同时设置多个key-value
mset key1 value1 key2 value2 key3 value3
# 6. 同时获取多个key-value
mget key1 key2 key3
- 字符串常用命令
# 1.获取长度
strlen key
# 2.获取指定范围切片内容 [包含start stop]
getrange key start stop
# 3.从索引值开始,value替换原内容
setrange key index value
- 数值操作
# 整数操作
incrby key 步长
decrby key 步长
incr key : +1操作
decr key : -1操作
# 应用场景: 抖音上有人关注你了,是不是可以用INCR呢,如果取消关注了是不是可以用DECR
# 浮点数操作: 自动先转为数字类型,然后再进行相加减,不能使用append
incrbyfloat key step
- string命令汇总
# 字符串操作
1、set key value
2、set key value nx
3、get key
3、mset key1 value1 key2 value2
4、mget key1 key2 key3
5、set key value nx ex seconds
6、strlen key
# 返回旧值并设置新值(如果键不存在,就创建并赋值)
7、getset key value
# 数字操作
7、incrby key 步长
8、decrby key 步长
9、incr key
10、decr key
11、incrbyfloat key number#(可为正数或负数)# 设置过期时间的两种方式
# 方式一
1、set key value ex 3
# 方式二
1、set key value
2、expire key 5 # 秒
3、pexpire key 5 # 毫秒
# 查看存活时间
ttl key
# 删除过期
persist key
- string数据类型注意
# key命名规范
可采用 - wang:email
# key命名原则
1、key值不宜过长,消耗内存,且在数据中查找这类键值的计算成本高
2、不宜过短,可读性较差
# 值
1、一个字符串类型的值最多能存储512M内容
- 业务场景
- 缓存
- 将mysql中的数据存储到redis字符串类型中
- 并发计数 - 点赞/秒杀
- 说明:通过redis单进程单线程的特点,由redis负责计数,并发问题转为串行问题
- 带有效期的验证码 - 短信验证码
- 借助过期时间,存放验证码;到期后,自动消亡
- 缓存
万个思考之后,清除所有库
### **列表数据类型(List)**- **特点**```python
1、元素是字符串类型
2、列表头尾增删快,中间增删慢,增删元素是常态
3、元素可重复
4、最多可包含2^32 -1个元素
5、索引同python列表
- 列表常用命令
# 增
1、从列表头部压入元素LPUSH key value1 value2 返回:list长度
2、从列表尾部压入元素RPUSH key value1 value2返回:list长度
3、从列表src尾部弹出1个元素,压入到列表dst的头部RPOPLPUSH src dst返回:被弹出的元素
4、在列表指定元素后/前插入元素LINSERT key after|before value newvalue返回:1,如果命令执行成功,返回列表的长度2,如果没有找到 pivot ,返回 -13,如果 key 不存在或为空列表,返回 0 # 查
5、查看列表中元素LRANGE key start stop# 查看列表中所有元素: LRANGE key 0 -1
6、获取列表长度LLEN key# 删
7、从列表头部弹出1个元素LPOP key
8、从列表尾部弹出1个元素RPOP key
9、列表头部,阻塞弹出,列表为空时阻塞BLPOP key timeout
10、列表尾部,阻塞弹出,列表为空时阻塞BRPOP key timeout# 关于BLPOP 和 BRPOP1、如果弹出的列表不存在或者为空,就会阻塞2、超时时间设置为0,就是永久阻塞,直到有数据可以弹出3、如果多个客户端阻塞再同一个列表上,使用First In First Service原则,先到先服务
11、删除指定元素LREM key count valuecount>0:表示从头部开始向表尾搜索,移除与value相等的元素,数量为countcount<0:表示从尾部开始向表头搜索,移除与value相等的元素,数量为countcount=0:移除表中所有与value相等的值返回:被移除元素的数量12、保留指定范围内的元素LTRIM key start stop返回:ok样例:LTRIM mylist1 0 2 # 只保留前3条# 应用场景: 保存微博评论最后500条LTRIM weibo:comments 0 499# 改
13、将列表 key 下标为 index 的元素的值设置为 valueLSET key index newvalue
python交互redis
- 模块(redis)
Ubuntu
sudo pip3 install redis
- 使用流程
import redis
# 创建数据库连接对象
r = redis.Redis(host='127.0.0.1',port=6379,db=0,password='123456')
- 代码示例
import redis# 1 创建redis数据库连接对象
r = redis.Redis(password='123456')
# 2 使用,很多命令的返回值是字节串,需要用字符串
# 表示时,调用decode方法.
# 3.1 通用命令...
print(r.keys('*'))
print(r.exists('name'))
# 3.2 字符串类型的操作
r.set('uname', 'aid2102', 60)
print(r.get('uname').decode())
r.mset({'a': 100, 'b': 200, 'c': 300})
print(r.mget(['a', 'b', 'c']))
# 3.3 列表类型的操作
r.lpush('pylk1',100,200,300)
print(r.lrange('pylk1',0,-1))
list案例: 一个进程负责生产任务,一个进程负责消费任务
进程1: 生产者,product.py
import redis
# 1 创建redis数据库连接对象
r = redis.Redis(password='123456')
# 2.向任务队列中添加任务,使用列表类型
# 任务格式: 任务类别_发送者_接收者_内容
task = 'sendMail_aid2102_mzg_helloworld'
r.lpush('lst:tasks',task)
进程2: 消费者,consumer.py
import redisr = redis.Redis(password='123456')while True:task = r.brpop('lst:tasks',3)if task:print(task)# (b'lst:tasks', b'sendMail_aid2102_mzg_helloworld')# 处理任务task_data = task[1].decode()print(task_data)# sendMail_aid2102_mzg_helloworldlst_data = task_data.split('_')if lst_data[0] == 'sendMail':print('执行发送邮件的任务...')else:print('-no task!-')
位图操作bitmap
定义
1、位图不是真正的数据类型,它是定义在字符串类型中
2、一个字符串类型的值最多能存储512M字节的内容,位上限:2^32
# 1MB = 1024KB
# 1KB = 1024Byte(字节)
# 1Byte = 8bit(位)
强势点
可以实时的进行统计,极其节省空间。官方在模拟1亿2千8百万用户的模拟环境下,在一台MacBookPro上,典型的统计如“日用户数”的时间消耗小于50ms, 占用16MB内存
SETBIT 命令
-
说明:设置某位置上的二进制值
-
语法:SETBIT key offset value
-
参数:offset - 偏移量 从0开始
value - 0或者1
-
示例:
# 默认扩展位以0填充
127.0.0.1:6379> SET mykey ab
OK
127.0.0.1:6379> GET mykey
"ab"
127.0.0.1:6379> SETBIT mykey 0 1
(integer) 0
127.0.0.1:6379> GET mykey
"\xe1b"
127.0.0.1:6379> SETBIT mykey 17 1
"\xe1b@"
如果要设置的位偏移量过大默认会按照最小字节对内容进行扩展 使用0000 0000 填充然后进行操作
GETBIT 命令
-
说明:获取某一位上的值
-
语法:GETBIT key offset
-
示例:
127.0.0.1:6379> GETBIT mykey 3
(integer) 0
127.0.0.1:6379> GETBIT mykey 0
(integer) 1
127.0.0.1:6379>
BITCOUNT 命令
- 说明:统计键所对应的值中有多少个 1
- 语法:BITCOUNT key start end
- 参数:start/end 代表的是 字节索引
- 示例:
127.0.0.1:6379> SET mykey1 ab
OK
127.0.0.1:6379[4]> BITCOUNT mykey
(integer) 6
127.0.0.1:6379[4]> BITCOUNT mykey 0 0
(integer) 3
应用场景案例
# 网站用户的上线次数统计(寻找活跃用户)用户名为key,上线的天作为offset,上线设置为1
# 示例用户名为 user1:login 的用户,今年第1天上线,第30天上线SETBIT user1:login 0 1 SETBIT user1:login 29 1BITCOUNT user1:login
Hash散列数据类型
- 定义
1、由field和关联的value组成的键值对
2、field和value是字符串类型
3、一个hash中最多包含2^32-1个键值对
- 优点
1、节约内存空间 - 特定条件下 【1,字段小于512个,2:value不能超过64字节】
2、可按需获取字段的值
- 缺点(不适合hash情况)
1,使用过期键功能:键过期功能只能对键进行过期操作,而不能对散列的字段进行过期操作
2,存储消耗大于字符串结构
- 基本命令操作
# 1、设置单个字段
HSET key field value
HSETNX key field value
# 2、设置多个字段
HMSET key field value field value
# 3、返回字段个数
HLEN key
# 4、判断字段是否存在(不存在返回0)
HEXISTS key field
# 5、返回字段值
HGET key field
# 6、返回多个字段值
HMGET key field filed
# 7、返回所有的键值对
HGETALL key
# 8、返回所有字段名
HKEYS key
# 9、返回所有值
HVALS key
# 10、删除指定字段
HDEL key field
# 11、在字段对应值上进行整数增量运算
HINCRBY key field increment
# 12、在字段对应值上进行浮点数增量运算
HINCRBYFLOAT key field increment
python操作hash
# 1、更新一条数据的属性,没有则新建
hset(name, key, value)
# 2、读取这条数据的指定属性, 返回字符串类型
hget(name, key)
# 3、批量更新数据(没有则新建)属性,参数为字典
hmset(name, mapping)
# 4、批量读取数据(没有则新建)属性
hmget(name, keys)
# 5、获取这条数据的所有属性和对应的值,返回字典类型
hgetall(name)
# 6、获取这条数据的所有属性名,返回列表类型
hkeys(name)
# 7、删除这条数据的指定属性
hdel(name, *keys)
应用场景:用户维度数据统计
用户维度统计统计数包括:关注数、粉丝数、喜欢商品数、发帖数用户为key,不同维度为field,value为统计数比如关注了5人HSET user:10000 fans 5HINCRBY user:10000 fans 1
python操作hash
import redis# 创建redis数据库的连接对象
r = redis.Redis(password='123456')# 操作hash
r.hset('pyhk1', 'username', 'aid2102')
r.hmset('pyhk1', {'age': 18, 'major': 'python'})
print(r.hget('pyhk1', 'username').decode())
print(r.hmget('pyhk1', ['username', 'age']))
print(r.hgetall('pyhk1'))
# 字典推导式
data = {k.decode(): v.decode() for k, v in r.hgetall('pyhk1').items()}
print(data)
r.hdel('pyhk1','age')
print(r.hgetall('pyhk1'))
# 删除键
r.delete('pyhk1')
集合数据类型(set)
- 特点
1、无序、去重
2、元素是字符串类型
3、最多包含2^32-1个元素
- 基本命令
# 1、增加一个或者多个元素,自动去重;返回值为成功插入到集合的元素个数
SADD key member1 member2
# 2、查看集合中所有元素
SMEMBERS key
# 3、删除一个或者多个元素,元素不存在自动忽略
SREM key member1 member2
# 4、元素是否存在
SISMEMBER key member
# 5、随机返回集合中指定个数的元素,默认为1个
SRANDMEMBER key [count]
# 6、弹出成员
SPOP key [count]
# 7、返回集合中元素的个数,不会遍历整个集合,只是存储在键当中了
SCARD key
# 8、把元素从源集合移动到目标集合
SMOVE source destination member# 9、差集(number1 1 2 3 number2 1 2 4 结果为3)
SDIFF key1 key2
# 10、差集保存到另一个集合中
SDIFFSTORE destination key1 key2# 11、交集
SINTER key1 key2
SINTERSTORE destination key1 key2# 11、并集
SUNION key1 key2
SUNIONSTORE destination key1 key2
案例: 新浪微博的共同关注
# 需求: 当用户访问另一个用户的时候,会显示出两个用户共同关注过哪些相同的用户
# 设计: 将每个用户关注的用户放在集合中,求交集即可
# 实现:user001 = {'peiqi','qiaozhi','danni'}user002 = {'peiqi','qiaozhi','lingyang'}user001和user002的共同关注为:SINTER user001 user002结果为: {'peiqi','qiaozhi'}
python操作set
import redisr = redis.Redis(password='123456')
'''武将: 张飞 许褚 赵云 马超 周瑜文臣: 诸葛亮 周瑜 司马懿结果: 1.纯武将 2.纯文臣 3.文武双全 4.文臣武将
'''
# set集合类型的操作
r.sadd('武将', '张飞', '许褚', '赵云', '马超', '周瑜')
r.sadd('文臣', '诸葛亮', '周瑜', '司马懿')
data1 = r.sdiff('武将', '文臣')
result = []
for item in data1:result.append(item.decode())
print('纯武将:', result)data2 = r.sdiff('文臣', '武将')
result = []
for item in data2:result.append(item.decode())
print('纯文臣:', result)data3 = r.sinter('文臣', '武将')
result = []
for item in data3:result.append(item.decode())
print('文武双全:', result)data4 = r.sunion('文臣', '武将')
result = []
for item in data4:result.append(item.decode())
print('文臣武将:', result)
有序集合sortedset
- 特点
1、有序、去重
2、元素是字符串类型
3、每个元素都关联着一个浮点数分值(score),并按照分值从小到大的顺序排列集合中的元素(分值可以相同)
4、最多包含2^32-1元素
-
示例
一个保存了水果价格的有序集合
分值 | 2.0 | 4.0 | 6.0 | 8.0 | 10.0 |
---|---|---|---|---|---|
元素 | 西瓜 | 葡萄 | 芒果 | 香蕉 | 苹果 |
一个保存了员工薪水的有序集合
分值 | 6000 | 8000 | 10000 | 12000 | |
---|---|---|---|---|---|
元素 | lucy | tom | jim | jack |
一个保存了正在阅读某些技术书的人数
分值 | 300 | 400 | 555 | 666 | 777 |
---|---|---|---|---|---|
元素 | 核心编程 | 阿凡提 | 本拉登 | 阿姆斯特朗 | 比尔盖茨 |
- 有序集合常用命令
# 在有序集合中添加一个成员 返回值为 成功插入到集合中的元素个数
zadd key score member
# 查看指定区间元素(升序)
zrange key start stop [withscores]
# 查看指定区间元素(降序)
zrevrange key start stop [withscores]
# 查看指定元素的分值
zscore key member# 返回指定区间元素
# offset : 跳过多少个元素
# count : 返回几个
# 小括号 : 开区间 zrangebyscore fruits (2.0 8.0
zrangebyscore key min max [withscores] [limit offset count]
# 每页显示10个成员,显示第5页的成员信息:
# limit 40 10
# MySQL: 每页显示10条记录,显示第5页的记录
# limit 40,10
# limit 2,3 显示: 第3 4 5条记录# 删除成员
zrem key member
# 增加或者减少分值
zincrby key increment member
# 返回元素排名
zrank key member
# 返回元素逆序排名
zrevrank key member
# 删除指定区间内的元素
zremrangebyscore key min max
# 返回集合中元素个数
zcard key
# 返回指定范围中元素的个数
zcount key min max
zcount salary 6000 8000
zcount salary (6000 8000# 6000<salary<=8000
zcount salary (6000 (8000#6000<salary<8000
# 并集
zunionstore destination numkeys key [weights 权重值] [AGGREGATE SUM|MIN|MAX]
# zunionstore salary3 2 salary salary2 weights 1 0.5 AGGREGATE MAX
# 2代表集合数量,weights之后 权重1给salary,权重0.5给salary2集合,算完权重之后执行聚合AGGREGATE# 交集:和并集类似,只取相同的元素
zinterstore destination numkeys key1 key2 weights weight AGGREGATE SUM(默认)|MIN|MAX
python操作sorted set
import redisr = redis.Redis(password='123456')# 有序集合类型的操作
r.zadd('pyzk1', {'tedu': 100, 'tedu2': 200})
print(r.zrange('pyzk1', 0, -1, withscores=True))
r.zadd('pyzk2', {'tedu2': 200, 'tedu3': 200})
# 并集运算
r.zunionstore('pyzk3',['pyzk1','pyzk2'],aggregate='sum')
print(r.zrange('pyzk3', 0, -1, withscores=True))
# 并集运算(带权重)
r.zunionstore('pyzk4',{'pyzk1':0.8,'pyzk2':0.2},aggregate='sum')
print(r.zrange('pyzk4', 0, -1, withscores=True))
五大数据类型及应用场景
类型 | 特点 | 使用场景 |
---|---|---|
string | 简单key-value类型,value可为字符串和数字 | 常规计数(微博数, 粉丝数等功能) |
hash | 是一个string类型的field和value的映射表,hash特别适合用于存储对象 | 存储部分可能需要变更的数据(比如用户信息) |
list | 有序可重复列表 | 消息队列等 |
set | 无序不可重复列表 | 存储并计算关系(如微博,关注人或粉丝存放在集合,可通过交集、并集、差集等操作实现如共同关注、共同喜好等功能) |
sorted set | 每个元素带有分值的集合 | 各种排行榜 |
事务
特点
1. 单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
2. 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制
事务命令
1、MULTI # 开启事务 mysql begin
2、命令1 # 执行命令
3、命令2 ... ...
4、EXEC # 提交到数据库执行 mysql commit
4、DISCARD # 取消事务 mysql 'rollback'
使用步骤
# 开启事务
127.0.0.1:6379> MULTI
OK
# 命令1入队列
127.0.0.1:6379> INCR n1
QUEUED
# 命令2入队列
127.0.0.1:6379> INCR n2
QUEUED
# 提交到数据库执行
127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 1
事务中命令错误处理
# 1、命令语法错误,命令入队失败,直接自动discard退出这个事务这个在命令在执行调用之前会发生错误。例如,这个命令可能有语法错误(错误的参数数量,错误的命令名)处理方案:语法错误则自动执行discard案例:
127.0.0.1:6379[7]> MULTI
OK
127.0.0.1:6379[7]> get a
QUEUED
127.0.0.1:6379[7]> getsss a
(error) ERR unknown command 'getsss'
127.0.0.1:6379[7]>
127.0.0.1:6379[7]>
127.0.0.1:6379[7]> EXEC
(error) EXECABORT Transaction discarded because of previous errors.# 2、命令语法没错,但类型操作有误,则事务执行调用之后失败,无法进行事务回滚我们执行了一个由于错误的value的key操作(例如对着String类型的value施行了List命令操作) 处理方案:发生在EXEC之后的是没有特殊方式去处理的:即使某些命令在事务中失败,其他命令都将会被执行。案例
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set num 10
QUEUED
127.0.0.1:6379> LPOP num
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get num
"10"
127.0.0.1:6379>
思考为什么redis不支持回滚?
pipeline 流水线
定义:批量执行redis命令,减少通信io
注意:此为客户端技术
示例
import redis
# 创建连接池并连接到redis
pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
r = redis.Redis(connection_pool=pool)pipe = r.pipeline()
pipe.set('fans',50)
pipe.incr('fans')
pipe.incrby('fans',100)
pipe.execute()
性能对比
# 创建连接池并连接到redis
pool = redis.ConnectionPool(host = '127.0.0.1',db=0,port=6379)
r = redis.Redis(connection_pool=pool)def withpipeline(r):p = r.pipeline()for i in range(1000):key = 'test1' + str(i)value = i+1p.set(key, value)p.execute()def withoutpipeline(r):for i in range(1000):key = 'test2' + str(i)value = i+1r.set(key, value)
python 操作 redis事务
with r.pipeline(transaction=true) as pipepipe.multi()pipe.incr("books")pipe.incr("books")values = pipe.execute()
watch - 乐观锁
作用: 事务过程中,可对指定key进行监听,命令提交时,若被监听key对应的值未被修改时,事务方可提交成功,否则失败
> watch books
OK
> multi
OK
> incr books
QUEUED
> exec # 事务执行失败
(nil)watch之后,再开一个终端进入redis
> incr books # 修改book值
(integer) 1
python操作watch
#同时对一个账户进行操作, 当前余额 * 2