Redis学习笔记
一.Redis介绍
非关系数据库,解决硬盘IO带来的性能瓶颈
1.特点
- 1.开源的,使用C编写,基于内存且支持持久化
- 2.支持数据类型丰富,字符串strings、散列hashes、列表lists、集合sets、有序集合sorted sets等
- 3.支持多种编程语言(C、C++、Python、Java、PHP)
- 4.单进程、单线程
2.功能
-
1.持久化
将内存中数据保存到磁盘中,保证数据安全,方便进行数据备份和恢复
-
2.过期键功能
-为键设置一个过期时间,让它在指定时间内自动删除
-<节省内存空间> 音乐播放器、日播放排名、过期自动删除
-
3.事务功能
-弱事务型的数据库,只是具备简单的事务功能
-
4.主从复制
-如何让redis保持高可用状态,官方提供主从搭建方案
-
5.Sentinel哨兵
-在搭配了基础的主从结构后,哨兵可做到自动故障转移
3.应用场景
-
1.缓存
作为缓存存储介质;查询优先走redis,没有则走mysql;可有效的降低mysql查询压力
-
2.并发计数
由于redis本身是单进程单线程的,可以有效解决并发请求技术场景,例如微博点赞
-
3.排行榜
各大实时排行榜 - 如电商/游戏中的排行
-
4.生产者消费者模型
从当生产者消费者模型的中间层;生产者可将任务分发给redis,消费者从redis中获取任务
4.安装&连接(Ubuntu)
- 安装:sudo apt-get install redis-server (Ubuntu默认开机自启)
- 服务端操作:sudo /etc/init.d/redis-server status|start|stop|restart
- 客户端连接:redis-cli -h ip地址 -p 6379 -a 密码 ,立刻执行 ping
- 测试命令:redis-benchmark -q -n 10000
二.redis配置及基础命令
1.配置文件
-
所在路径 /etc/redis/redis.conf
-
修改配置文件前备份
-sudo cp /etc/redis/redis.conf /etc/redis/redis_bak.conf 备份配置文件
-sudo chown redis:redis /etc/redis/redis_bak.conf 更改用户组为redis
添加密码
- requirepass 密码
- 重启服务 sudo /etc/init.d/redis-server restart
- 客户端连接 redis-cli -h 127.0.0.1 -p 6379 -a 123456 ,执行ping
- 进入后重新认证密码 AUTH 123456
重启redis失败,大概率为配置文件有误
- 解决方案1: 终端执行 sudo redis-server /etc/redis/redis.conf 启动,按照报错修改
- 解决方案2:还原备份后的配置文件
- sudo mv /etc/redis/redis.conf /etc/redis/redis_error.conf
- sudo mv /etc/redis/redis_bak.conf /etc/redis/redis.conf
远程连接
-
1.注释 本地IP地址绑定
-69行:# bind 127.0.0.1 ::1
-
2.关闭保护模式(把yes 改为 no)
-88行: protected-mode no
-
3.重启服务
-sudo /etc/init.d/redis-server restart
2.基础通用命令
可执行文件
redis-server # 服务端
redis-cli # 客户端
redis-benchmark # 性能测试工具
redis-check-aof # aof修复工具
redis-check-dump # rdb修复工具
redis-sentinel # sentinel服务端
无关于具体的数据类型
OBJECT encoding key # 查看 key的底层数据类型
select number # 切换数据库(redis默认16个数据库,编号为0-15,默认进入redis为 db0)
info # 查看redis服务的整体情况
keys 表达式 # 查找所有符合给定模式的 key,正式模式避免执行,由于redis单进程单线程,key很多时,当前命令可能阻塞redis
key * 匹配数据库中所有 key
key h?llo ?匹配一个任意字符
key h*llo *匹配任意长度字符或空
type key # 返回当前键的数据类型
esists key # 返回当前键是否存在,1:存在 0:不存在
del key # 删除key
rename key newkey # 重命名当前key的名称
flushdb # 清除当前所在数据库数据
flushall # 清除所有数据库数据
三.字符串
- 1.字符串、数字,都会转为字符串来存储
- 2.以二进制的方式存储在内存中
注意:
- key命名规范-可采用 - wang:email
- key命名原则:
- 1.key值不宜过长,消耗内存,且在数据中查找这类键值的计算成本高
- 2.不宜过短,可读性较差
- 3.一个字符串类型的值最多能存储512M内容
1.常用命令
set key value nx ex # 设置一个字符串的key
# 其中 nx -> not exist 代表 当key不存在,才存储这个 key
# ex -> expire 过期时间,单位s
get key # 获取key的值
strlen key # 获取key存储值的长度
getrange key start stop # 取指定范围切片内容【含start stop】
setrange key index value # 从索引值开始,用value替换原内容;返回最新长度
mset key1 value1 key2 value2 key3 value3 # 批量存储
mget key1 key2 key3 # 批量获取key的值
2.数值操作
incrby key step # 将 key 增加指定步长,如10 ,-10
decrby key step # 将 key 减少指定步长
incr key # +1
decr key # -1
incrbyfloat key step
3.过期操作及机制
1.过期时间设置
# 手动设置 key 过期时间
set key value ex 60 # 方案1:使用set 的ex参数
set key value # 方案2:使用 expire 通用命令
expire key 60 # 秒
pexpire key 60 # 毫秒
ttl key # 查看过期时间 ttl key - 通用命令
""" 返回值:
-1:表示key没有过期时间
>0: 表示key的剩余存活时间
-2:表示key不存在
"""
persist key # 删除过期时间,变为 永不过期
# 返回值:1代表删除成功 0代表key没有过期时间或不存在
2.过期回收机制
- redis数据库中有一个特殊的容器负责存储带有过期时间的key及其过期时间,即”过期字典“
- 针对过期字典中的key,redis结合惰性删除和定期删除以及最大内存检查机制,有效删除过期数据
1.惰性删除(被动)
当调用 key时,检查是否过期,如果过期则删除
2.定期删除(主动)
主动定期扫描过期字典中的数据,检查是否过期
DB_NUMBERS = 16 # 数据库数量
KEY_NUMBERS = 20 # 每次检查key的数量
current_db = 0 # 记录当前检查的数据库
# 每一百毫秒 调用该函数
def activeExpireCycle():
for i in range(DB_NUMBERS):
if current_db == DB_NUMBERS:
current_db = 0
#获取当前数据库
redisDB = server.db[current_db]
first_start = True
del_key_num = 0
current_db += 1
while first_start or del_key_num > KEY_NUMBERS/4 :
first_start = False
for j in range(KEY_NUMBERS):
_key = redisDB.randomExpireKey()
if is_expire(_key):
# 过期则直接删除
delete_key(_key)
del_key_num += 1
if time_is_limit():
# 若执行时间太长 默认25毫秒
return
3.最大内存检查
- 最后一道保险 - maxmemory 配置选项
- 一旦内存量超过 最大限制,redis会在执行命令时触发 内存淘汰
- (需要手动在 redis配置文件中 激活 maxmemory配置项
主流淘汰机制如下
- volatile-lru:从已设置过期时间的内存数据集中挑选最近最少使用的数据 淘汰
- volatile-ttl:从以设置过期时间的内存数据集中挑选即将过期的数据 淘汰
- volatile-random:从已设置过期时间的内存数据集中任意挑选数据 淘汰
- allkeys-lru:从内存数据集中挑选最近最少使用的数据 淘汰
- allkeys-random:从数据集中任意挑选数据 淘汰
- no-enviction:禁入大多写命令,允许删除等出操作
4.应用场景
-
缓存
-将mysql中的数据存储到redis字符串类型中
-
并发计数 - 点赞/秒杀
-通过redis单进程单线程的特点,由redis负责计数,并发问题转为串行问题
-
带有效期的验证码
-借助过期时间,存放验证码;到期后,自动消亡
四.列表
- 元素是字符串类型,元素可重复,索引同python列表
- 列表头尾增删快,中间增删慢,增删元素是常态
- 最多可包含2^32-1个元素
1.常用命令
# 1.增加数据
lpush key value1 value2 # 从列表头部压入元素,返回list长度
rpush key value1 value2 # 从列表尾部压入元素,返回list长度
rpoplpush s1 t1 # 从列表s1尾部弹出1个元素,压入t1的头部,返回,s1弹出的元素 (s1右弹,t1左压)
linsert key after|before value newvalue # 在列表指定元素后/前 插入元素
"""返回:
1.成功返回列表的长度
2.未找到指定元素,返回-1
3.key不存在或为空列表,返回 0
"""
# 2.查看
lrange key start stop # 查看列表中元素
llen key # 获取列表长度
# 3.删除
lpop key # 从列表头部弹出一个元素(左弹)
rpop key # 从列表尾部弹出一个元素(右弹)
blpop key timeout # 列表头部,列表空时阻塞timeout后弹出
brpop key timeout # 列表尾部,列表空时阻塞timeout后弹出
"""关于阻塞弹出
1.如果弹出的列表不存在 或为空,就会阻塞,否则直接弹出
2.timeout设置为0,即永久阻塞,直到有数据可以弹出
3.如果多个客户端阻塞再同一个列表上,使用 先到先服务 原则
"""
lrem key count value # 删除指定元素
"""返回删除元素的数量
count>0:从头部开始向表尾搜索,删除与value相等的元素,数量count
count<0:从尾部开始向表头搜索,删除与value相等的元素,数量count
count=0:删除表中所有与value相等的值
"""
ltrim key start stop # 保留指定范围内的元素
# ltrim weibo:comments 0 499 保存微博评论最后500条
# 4.更新数据
lset key Index newvalue # 设置list指定索引的值
2.应用场景
- 1.存储微博评论,做切割,只保留最新的xx个
- 2.生产者消费者模型,做中间层,存放生产者的任务
五.pyredis操作redis
- 检查当前Ubuntu是否安装 - sudo pip3 freeze|grep -i ‘redis’
- 安装Python的redis模块 - sudo pip3 install redis
redis连接及 执行操作
# 1.建立连接对象
import redis
r = redis.Redis(host='127.0.0.1',port=6379,db=0,password='123456')
# 2.连接对象.redis命令,大部分命令和 redis终端中使用相同
# 基础命令
key_list = r.keys('*')
print(key_list) # [b'ce', b'spi', b'li', b'l1']
print(r.type('l1'))
# list 命令
r.lpush('pyl1','a','v','w','z')
print(r.lrange('pyl1',0,-1))
r.linsert('pyl1','before','v','g')
print(r.lrange('pyl1',0,-1))
# string 命令
r.set('uname','ppp',ex=60)
print(r.get('uname'))
r.mset({'k1':'v1','k2':'v2'}) # 批量插入,使用字典
print(r.mget('k1','k2','k3','k6'))
六.位图
- 位图不是真正的数据类型,它是定义在字符串类型中
- 一个字符串类型中的值最多能存储512M字节的内容,即位上限:2^32
1.常用命令
setbit key offset value # 设置某位置上的二进制值,返回修改之前 位 的值
""" 参数:
1)offset - 偏移量从0开始
2)value - 0或1
3) key不存在时: 初始化最小字节数的 bit位,默认补0
4)key存在时: 满足长度要求,直接修改当前bit位
不满足长度要求,在原有value上初始化最小字节数的bit
"""
getbit key offset # 获取某一位上的值
bitcount key start end # 统计键所对应的值中有多少个 1 ,注意是字节索引(8位)
bitcount key # 统计key中所有1的数量
2.pyredis操作
import redis
r = redis.Redis(host='127.0.0.1',port=6379,db=0,password='123456')
r.setbit('pybit1',4,1)
print(r.getbit('pybit1',3))
print(r.getbit('pybit1',4))
print(r.bitcount('pybit1'))
3.应用场景
- 记录网站上用户的上线频率 - 用户A、B上线天数
- 性能:网站运行10年,每个用户占用空间 10*365(bit),即456字节,bitcount的处理速度和get、incre操作一样快
七.哈希
-
由字符串类型的field和关联的value组成的键值对,每个hash最多含2^32-1个键值对
-
优点:
1.节约内存空间-特定条件【1.字段小于512个,2.value不能超过64字节】
2.可按需获取字段的值
-
缺点:
1.使用过期键功能:只能对键进行过期操作,不能对散列的字段进行过期操作
2.存储消耗大于字符串结构
1.常用命令
hset key field value # 设置单个字段
hsetnx key field value # key中不存在 field时设置成功
hmset key field1 value1 field2 value2 # 设置多个字段
hlen key # 返回字段个数
hexists key field # 判断字段是否存在(不存在返回0)
hget key field # 返回字段值
hmget key field1 field2 # 返回多个字段值
hgetall # 返回所有的键值对
hkeys key # 返回所有字段名
hvals key # 返回所有值
hdel key field # 删除指定字段
hincrby key field step # 在字段值进行整数增量运算
hincrbyfloat key field step # 在字段值进行浮点数增量运算
2.pyredis操作
import redis
r = redis.Redis('127.0.0.1',port=6379,db=0,password='123456')
r.hmset('pyh2',{'name':'zs','age':30})
print(r.hgetall('pyh2'))
3.应用场景
1.用户维度数据统计
- 原理:基于hash压缩特点,和字段可计数
- 统计:关注数、粉丝数、喜欢商品数、发帖数
- 用户为key,不同维度为 field ,value为统计数
- 如关注了5人 hset user:10000 fans 5 | hincrby user:10000 fans 1
2.缓存 - redis + mysql + hash组合使用
-
原理:hash可以按需取出字段数据,也比较适合做缓存
-
示例:用户想要查询个人信息
1.到redis缓存中查询个人信息
2.redis中查询不到,到mysql查询,并缓存到redis
3.再次查询个人信息
八.集合
- 无序、去重,元素是字符串类型
- 最多包含2^32-1个元素
- 当全是整型且在512位内,类型为intset(数组),其余是hashset(哈希)
1.常用命令
sadd key member1 member2 # 增加 一个或多个元素,自动去重,返回成功总数
smembers key # 查看集合中所有元素
srem key member1 member2 # 删除一个或多个元素,不存在自动忽略
sismember key member # 元素是否存在
srandmember key count # 随机返回集合中指定个数的元素,默认为1
spop key count # 随机弹出指定个数的元素(删除)
scard key # 返回集合中元素的个数
smove source destination member # 把元素从source移动到 destination集合
sdiff key1 key2 # 差集(key1-key2:在key1中,但不在key2中)
sdiffstore destination key1 key2 # 差集保存在destination中
sinter key1 key2 # 交集
sinterstore destination key1 key2 # 交集保存
sunion key1 key2 # 并集
sunionstore key1 key2 # 并集保存
2.pyredis操作
import redis
r = redis.Redis(host='127.0.0.1',port=6379,db=0,password='123456')
r.sadd('pys1','a','b','c','d')
print(r.smembers('pys1'))
r.sadd('pys2','a','f','c','z')
print(r.sinter('pys1','pys2'))
3.应用场景
- 社交类平台,共同好友 - 交集
- 纯随机类抽奖(等概率,无法做权重)
- 防止元素重复
- 黑/白名单(中间件)
九.有序集合
- 有序、去重,元素是字符串类型
- 最多包含2^32-1个元素
- 每个元素都关联着一个浮点数分值(score),并按照分值从小到大的顺序排列集合中的元素(分值可以相同)
1.常用命令
zadd key score member # 在有序集合中添加成员,返回成功插入的元素个数
zrange key start stop [withscores] # 查看指定区间元素【升序】,分值
zrevrange key start stop [withscores] # 查看指定区间元素【降序】,分值
zscore key member # 查看指定元素的分值
# 返回指定区间元素
zrangebyscore key min max [withscores] [limit offset count]
""" min/max :最小值/最大值区间,默认闭区间(>= 或 <=)
(min (max ,开启 开区间 即(> 或 <)
其中:limit offset count 和 mysql一样
offset:跳过多少个元素
count:返回几个
"""
zrem key member # 删除成员
zincrby key step member # 增加或减少分值
zrank key member # 查看指定元素的排名【升序】
zrevrank key member # 查看指定元素的排名【降序】
zremrangebyscore key min max # 删除指定区间内的元素 默认闭区间,加(开区间
zcard key # 返回集合中元素个数
zcount key min max # 返回指定范围中元素的个数
zunionstore destination numkeys key1 key2 [weights 权重值按key的顺序] [aggregate sum|min|max] # 并集保存至destination集合
"""如: zunionstore s3 2 s1 s2 weights 1 0.5 aggregate max
2 代表集合数量,weights后 权重1给s1,权重0.5给s2,算完权重再执行聚合
"""
zinterstore destination numkeys key1 key2 [weights 权重值按key的顺序] [aggregate sum|min|max] # 交集保存至destination集合
2.pyredis操作
import redis
r = redis.Redis(host='127.0.0.1',port=6379,db=0,password='123456')
# 有序集合
r.zadd('pyz1',{'tom':6000,'jim':8000,'jack':12000})
print(r.zrange('pyz1',0,-1,withscores=True))
# [(b'tom', 6000.0), (b'jim', 8000.0), (b'jack', 12000.0)]
# zcount 中 开区间需要转为 字符串,如 "(8000"
print(r.zcount('pyz1',"(8000",10000))
r.zadd('pyz2',{'tom':4000,'jim':6000})
# 并集 或 交集 中 numkeys不用,直接用括号将 操作的集合括起来
r.zinterstore('pyz3',('pyz1','pyz2'),aggregate='max')
print(r.zrange('pyz3',0,-1,withscores=True))
# 加权重需要以字典形式,如下所示
r.zinterstore('pyz4',{'pyz1':0.5,'pyz2':1},aggregate='max')
print(r.zrange('pyz4',0,-1,withscores=True))
3.应用场景
- 各种排行榜
十.事务
事务是逻辑上对数据的一组操作,这组操作要么一次全部成功,或者这组操作全部失败;是不可分割的一个工作单位
1.事务四大特性(ACID)
原子性(Atomicity)
事务中所有操作是不可再分割的原子单位。全部成功或全部失败
一致性(Consistency)
是事务对数据完整性约束的遵循,含主键约束、外键约束或用户自定义约束
隔离性(Isolation)
事务与事务之间互不打扰
持久性(Durability)
一个事物一旦成功提交,对数据库的改变必须是永久的,即便是数据库发生故障也不应该会对其产生任何影响
2.redis的事务
- 弱事务型的数据库,并不具备ACID的全部特性
- 具备隔离性:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
- 不保证原子性:redis的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制
3.事务命令
multi # 开启事务,在命令执行或取消前,会被放在redis的队列空间内,统一执行
命令1 # 命令1入队列
命令2 # 命令2入队列
exec # 提交到数据库执行
discard # 取消事务 ,和exec命令二选一,要么执行,要么取消,没有先后顺序
4.事务特殊情况
命令语法错误
命令入队失败,直接自动discard退出这个事务
- 这个命令在执行调用之前会发生错误,如这个命令可能有语法错误(错误的参数数量,错误的命令名)
- 处理方案:语法错误则自动执行discard
类型操作错误
命令语法正确,但类型操作有误,则事务执行调用之后失败,无法进行事务回滚
- 当我们执行了一个由于错误的value的key操作会出现(如对String类型的value施行了List命令操作
- 处理方案:发生在exec之后的是没有特殊方式去处理的,即使某些命令在事务中失败,其他命令都将会被执行
# 第一条命令成功执行,第二条命令失败
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set test 100
QUEUED
127.0.0.1:6379> lpop test
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 test
"100"
127.0.0.1:6379>
5.pyredis事务
pipeline流水线
-
是一种客户端技术,能够批量执行redis命令,减少通信IO
-
**原理:**效仿redsi的事务,客户端将多个命令打包,一次通信发给redis,可明显降低redis服务的请求数
-
**注意:**如果一组命令中,一个命令需要上一个命令的执行结果才可以执行,则无法使用该技术
import redis
# 创建连接池并连接到redis
pool = redis.ConnectionPool(host='127.0.0.1',db=0,port=6379,password='123456')
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline()
pipe.set('fans',50)
pipe.incr('fans')
pipe.incrby('fans',100)
pipe.execute()
乐观锁-watch
- 事务过程中,可对指定key进行监听,命令提交时,若被监听key对应的值未被修改,事务方可提交成功,否则失败,返回nul
- 解决资源竞争的一种方式
# 监听 fans后,另一终端执行 incr fans 和 incrby fans -1
127.0.0.1:6379> watch fans
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> incr fans
QUEUED
127.0.0.1:6379> exec
(nil)
#watch监听后 即使 fans的值没有变化,但监听到fans被修改,故该事务依旧失败
python操作事务
- 含流水线技术及乐观锁
import time
import redis
pool = redis.ConnectionPool(host='127.0.0.1', db=0, port=6379, password='123456')
r = redis.Redis(connection_pool=pool)
def double_account(user_id):
key = 'account_%s' % (user_id)
# 流水线技术,事务打开
with r.pipeline(transaction=True) as pipe:
while True:
# python中,加入乐观锁后,事务失败后会报错,需要异常处理
try:
# 加上乐观锁,监听key
pipe.watch(key)
value = int(r.get(key))
value *= 2
pipe.multi()
pipe.set(key, value)
pipe.execute()
break
except redis.WatchError:
print('----key changed')
continue
return int(r.get(key))
if __name__ == '__main__':
# account_yuyupeng
print(double_account('yuyupeng'))
十一.数据持久化
- redis将数据从掉电易失的内存放到永久存储的设备上,故所有的数据都在内存上,因此必须要持久化
- redis提供两种持久化方案:1.RDB默认开启 2.AOF
1.RDB
- 保存真实的数据(全量保存),即快照
- 将服务器包含的所有数据库数据以二进制文件的形式保存到硬盘里面
- 默认文件名:/var/lib/redis/dump.rdb
- 文件名及目录可在配置文件中修改【/etc/redis/redis.conf】
263行:dir /var/lib/redis # 表示rdb文件存放路径
253行:dbfilename dump.rdb # 文件名
触发rdb持久化
1.redis终端中使用SAVE 或 BGSAVE 命令
- SAVE比BGSAVE快,因为创建子进程,消耗额外的内存
- 可以通过查看日志文件来查看Redis的持久化过程
- logfile位置查看:vim /var/log/redis/redis-server.log
SAVE特点:
127.0.0.1:6379> SAVE
OK
- 执行SAVE命令过程中,Redis服务器将被阻塞,无法处理客户端发送的命令请求,在SAVE命令执行完毕后,服务器才会重新开始处理客户端发送的命令请求
- 如果RDB文件已经存在,那么服务器将自动使用新的RDB文件代替旧的RDB文件
BGSAVE特点:
127.0.0.1:6379> BGSAVE
Background saving started
# 可以在日志中看到保存记录,
# 终端执行即可查看 vim /var/log/redis/redis-server.log
- 客户端发送 BGSAVE 给服务器
- 客户端马上返回 Background saving started 给客户端
- 服务器 fork() 子进程做这件事情
- 服务器继续提供服务
- 子进程创建完RDB文件后再告知Redis服务器
2.设置配置文件
- 只要三个条件的任意一个被满足时,服务器就会自动执行BGSAVE
- 每次创建RDB文件之后,服务器为实现自动持久化而设置的时间计数器和次数计数器就会被清零,并重新开始计数,所以多个保存条件的效果不会叠加
# redis 配置文件默认
218行:save 900 1
219行:save 300 10
220行:save 60 10000
# 表示如果距离上一次创建RDB文件过去了60秒,并且服务器的所有数据库总共已经执行了不小于10000此修改,那么自动执行BGSAVE命令
3.Redis关闭
- redis在正常关闭时,也会执行保存RDB操作
- 但是异常关闭(如断电),无法自动触发RDB操作
特殊说明
- 创建RDB文件需要将服务器所有的数据库的数据都保存起来,这是一个非常消耗资源和时间的操作,所以服务器需要隔一段时间才创建一个新的RDB文件,也就是说,创建RDB文件不能执行的过于频繁,否则会严重影响服务器的性能
- 可能丢失数据
2.AOF
- 存储的是命令,而不是真实数据
- 默认不开启,开启方式(修改配置文件)
vim /etc/redis/redis.conf
699行:appendonly yes # 把 no 改为 yes
703行:appendfilename "appendonly.aof"
- 重启服务 sudo /etc/init.d/redis-server restart
执行原理
- 每当有修改数据库的命令被执行时
- 因为AOF文件里面存储了服务器执行过的所有数据库修改的命令,所以给定一个AOF,服务器只要重新执行一遍AOF文件里面包含的所有命令,就能还原数据库
- 用户可以根据需要对AOF持久化进行调整,让Redis在遭遇意外停机时不丢失任何数据,或者只丢失一秒钟的数据,这比RDB持久化丢失的数据要少得多
特殊说明
- 1.AOF持久化:当一条命令真正的被写入到硬盘里面时,这条命令才不会因为停机而意外丢失
- 2.AOF持久化在遭遇停机时丢失命令的数量,取决于命令被写入到硬盘的时间
- 3.越早将命令写入到硬盘,发生意外停机时丢失的数据就越少,反之亦然
持久化相关配置
# 打开配置文件:/etc/redis/redis.conf
728行:appendfsync always
# 服务器每写入一条命令,就将缓冲区里的命令写入硬盘,服务器就算意外停机,也不会丢失任何已经成功执行的命令数据
729行:appendfsync everysec # (默认)
# 服务器每一秒将缓冲区里的命令写入硬盘,服务器及时遭遇意外停机,最多只丢失1秒的数据
730行:appendfsync no
# 服务器不主动将命令写入硬盘,由操作系统决定何时将缓冲区里的命令写入硬盘,丢失命令数量不确定
AOF重写
为了让AOF文件的大小控制在合理范围,避免胡乱增长,减少大量冗余命令,通过AOF****重写功能,服务器可以产生一个新的AOF文件
- 新的AOF文件记录的数据库数据 和 原由的AOF文件记录的数据库数据完全一样
- 新的AOF文件会使用尽可能少的命令来记录数据库数据,因此AOF文件会小很多
- AOF重写期间,服务器不会被阻塞,可以正常处理客户端发送的命令请求
AOF重写触发
1.客户端向服务器发送 BGREWRITEAOF 命令
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
2.修改配置文件让服务器自动执行 BGREWRITEAOF 命令
auto-aof-rewrite-peicentage 100
auto-aof-rewrite-min-size 64mb
# 只有当 AOF文件的增量大于100%时才进行重写,即原文件的2倍
""" 如: 第一次重写新增:64M
第二次重新新增:128M
第三次重写新增:256M
"""
3.AOF VS RDB
- 当dump.rdb、appendonly.aof同时存在,恢复时优先找appendonly.aof
- 当redis存储真正数据,每一条都不能丢失,要用always ;有的做缓存,有的保存真数据,可以开启多个redis服务,不同业务使用不同的持久化
AOF持久化 | RDB持久化 |
---|---|
增量备份,一次保存一个修改数据库的命令 | 全量备份,一次保存整个数据库 |
保存间隔默认为 1s | 保存的间隔较长 |
数据还原速度一般,冗余命令多,还原速度慢 | 数据还原速度快 |
无论时平时还是进行AOF重写时,都不会阻塞服务器 | 执行SAVE命令会阻塞服务器,但手动或自动触发的BDSAVE不会阻塞 |
十二.高可用
通过设计减少系统不能提供服务的时间
-
消除基础架构中的单点故障
-
redis单进程单线程模式,如果redis进程挂掉,相关依赖的服务就难以正常服务
-
redis高可用方案:主从搭建 + 哨兵
1.主从复制
- 一个Redis服务可以有多个该服务的复制品,这个Redis服务成为master,其他复制品成为slaves
- master会一直将自己的数据更新同步给slaves,保持主从同步
- 只有master可以执行写命令,slaves只能执行读命令
作用:分担了读的压力(高并发);提高可用性
原理:从服务器执行客户端发送的读命令,客户端可以连接slaves执行读请求,来降低master的读压力
Linux命令行
命令:redis-server --port <port> --slaveof<mast-ip> <master-port> --masterauth <master password>
# 从服务端 开启一个redis服务(slave)
redis-server --port 6400 --slaveof 127.0.0.1 6379 --masterauth 123456
# 从客户端
redis-cli -p 6400
127.0.0.1:6400> keys *
# 复制了原6379端口的redis中数据
127.0.0.1:6400> set mykey 456
(error) READONLY You can't write against a read only replica.
127.0.0.1:6400>
# 从服务器 只能读数据,不能写数据
Redis命令行
slaveof IP PORT # 成为谁的从
slaveof no one # 自己成为主
配置文件
info replication # 查看主从复制状态:
# 每个redis服务,都有1个和他对应的配置文件
# 两个redis服务
6379 -> /etc/redis/redis.conf
6400 -> /home/pp/redis_6400.conf
# 创建并修改配置文件
vim redis_6400.conf
slaveof 127.0.01 6379
masterauth 123456 # 若主服务器有密码,则需要配置该项
port 6400
daemonize yes # 守护进程配置项,默认为 no
# 启动redis服务
redis-server redis_6400.conf
# 客户端连接测试
redis-cli -p 6400
127.0.0.1:6400> keys *
127.0.0.1:6400> hset lis uname zhangsan
(error) READONLY You can't write against a read only replica.
127.0.0.1:6400>
2.哨兵
- Sentinel会不断检查Master和Slaves是否正常
- 每一个Sentinel可以监控任意多个Master和该Master下的Slaves
原理:哨兵进程定期与redis主从进行通信,当哨兵认为redis主 ‘阵亡‘后【通信无返回】,自动将切换工作完成
安装&使用
- 1.安装redis-sentinel
sudo /etc/init.d/redis-sentinel stop|start|restart|status # 验证是否已安装
sudo apt install redis-sentinel # 安装
- 2.新建配置文件sentinel.conf
port 26379
sentinel monitor pp 127.0.0.1 6379 1
# 哨兵 监听 名称 监听IP 端口号 投票数
- 3.启动sentinel
redis-sentinel sentinel.conf # 方式一
redis-server sentinel.conf --sentinel # 方式二
- 4.将master的redis服务终止,查看从是否会提升为主
sudo /etc/init.d/redis-server stop
# 两个从分别6400、6500,发现提升6500为master,其他两个为从
# 在6500上设置新值,6400查看
127.0.0.1:6500> set user zhangsan
OK
127.0.0.1:6400> get user
"zhangsan"
配置文件
# sentinel监听端口,默认是 26379,可以修改
port 26379
# 告诉sentinel去监听地址为ip:port的一个master,master-name可自定义,quorum是一个数字,指明当有多少个sentinel认为一个master失效时,master才算真正失效
sentinel monitor <master-name> <ip> <redis-port> <quorum>
# 如果master有密码,则需要添加该配置,注意所有主从密码都要保持一致,否则失效
sentinel auth-pass <master-name> <password>
# master多久失联才认为是不可用了,默认是30s,单位ms
sentinel down-after-milliseconds <master-name> <milliseconds>
python操作
from redis.sentinel import Sentinel
# 生成哨兵连接
sentinel = Sentinel([('localhost',26379)],socket_timeout=0.1)
# 初始化master连接,若有密码需要加上 password ='123456'
master = sentinel.master_for('pp',socket_timeout=0.1,db=0,password='123456')
slave = sentinel.slave_for('pp',socket_timeout=0.1,db=0,password='123456')
# 使用redis相关命令
master.set('mymaster','yes')
print(slave.get('mymaster'))