redis
2022-04-10 17:19:48 0 举报
AI智能生成
redis相关知识
作者其他创作
大纲/内容
本地测试:每秒11万次的set 和get
SET: 111482.72 requests per secondLPUSH: 118906.06 requests per second
每秒执行11万的Lua脚本
测试命令:
纯内存结构 key -value结构
没有创建线程、销毁线程带来的消耗
避免了上下文切换导致的 CPU 消耗
避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等
单线程
延伸一下什么叫多路复用selector、poll、epoll
异步非阻塞IO
多路复用
这么快的原因:
Redis为啥这么快
不同的业务使用不同的redis集群
协议使用redis的不同db。
1、业务隔离
格式:业务标识:系统名称:模块名称:关键词简写
比如:保险:用户管理:用户申请:手机号
Redis Key:bx:um:reg:mobile
2、良好的Redis Key的设计
redis如何防止key冲突
主从复制
选举出一个 Sentinel 节点来完成自动故障转移
sentinel节点为奇数个(2n+1)
哨兵模式集群(解决主节点挂掉不可写的问题,没有主节点)
3主3从
slot = crc16(key) mod NUMER_SLOTS
Redis 集群中有 16384 个散列槽,为了计算给定密钥的散列槽,Redis 对 key 采用 CRC16 算法
Hash 槽(slot)
集群模式
aof
rdb
数据持久化
redis如何保证高可用
SET resource_name unique_value NX PX 30000
获取锁(unique_value可以是UUID等)
释放锁(lua脚本中,一定要比较value,防止误解锁)
锁实现
set命令要用set key value px milliseconds nx;
value要具有唯一性;
释放锁时要验证value值,不能误解锁;
要点
在Redis的master节点上拿到了锁;
但是这个加锁的key还没有同步到slave节点;
master故障,发生故障转移,slave节点升级为master节点;
导致锁丢失。
问题:锁丢失场景
Redis分布式锁实现
使用N个完全独立、没有主从关系的Redis master节点以保证他们大多数情况下都不会同时宕机,N一般为奇数。一个客户端需要做如下操作来获取锁:
获取当前时间(单位是毫秒)。
轮流用相同的key和随机值在N个节点上请求锁,在这一步里,客户端在每个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。比如如果锁自动释放时间是10秒钟,那每个节点锁请求的超时时间可能是5-50毫秒的范围,这个可以防止一个客户端在某个宕掉的master节点上阻塞过长时间,如果一个master节点不可用了,我们应该尽快尝试下一个master节点。
客户端计算第二步中获取锁所花的时间,只有当客户端在大多数master节点上成功获取了锁((N/2) +1),而且总共消耗的时间不超过锁释放时间,这个锁就认为是获取成功了。
如果锁获取成功了,那现在锁自动释放时间就是最初的锁释放时间减去之前获取锁所消耗的时间。
如果锁获取失败了,不管是因为获取成功的锁不超过一半(N/2+1)还是因为总消耗时间超过了锁释放时间,客户端都会到每个master节点上释放锁,即便是那些他认为没有获取成功的锁。
核心原理
就是利用多个的主节点,在超过半数以上的主节点获取锁成功,才算成功;否则算失败,回滚–删除之前在所有节点上获取的锁。
总结
Redlock实现
解决
redis分布式锁存在宕机导致锁丢失,怎么解决
所谓的bigkey就是存储本身的key值空间太大,或者hash,list,set等存储中value值过多。
问题
1、单个简单的key存储的value很大
2、hash, set,zset,list 中存储过多的元素
3、一个集群存储了上亿的key
场景
1.读写bigkey会导致超时严重,甚至阻塞服务。
2.大key相关的删除或者自动过期时,会出现qps突降或者突升的情况,极端情况下,会造成主从复制异常,Redis服务阻塞无法响应请求
产生问题
可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;
对象需要每次都整存整取
该对象每次只需要存取部分数据
单个简单的key存储的value很大
可以对存储元素按一定规则进行分类,分散存储到多个redis实例中。
hash, set,zset,list 中存储过多的元素
转Hash结构存储,即原先是直接使用Redis String 的结构存储,现在将多个key存储在一个Hash结构中
一个集群存储了上亿的key
不使用redis,使用其他存储,比如mongodb
解决方法
redis-cli --bigkeys
redis 可使用redis-cli的“--bigkeys”选项查找大Key
如何找到大key
大key
热key问题就是突然有几十万的请求去访问redis上的某个特定key,那么这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis服务器直接宕机。
介绍
用户消费的数据远大于生产的数据(热卖商品、热点新闻、热点评论、明星直播)
请求分片集中,超过单 Server 的性能极限。
• 流量集中,达到物理网卡上限。
• 请求过多,缓存分片服务被打垮。
• DB 击穿,引起业务雪崩。
发现热key以后,可以把热key数据加载到系统JVM并设置合适的缓存过期时间,针对热key的请求就会直接分散到各业务服务器上,防止所有请求同时访问同一台redis。
增加二级缓存
服务端缓存:即将热点数据缓存至服务端的内存中
可以把热点key的数据备份到所有redis的集群节点中,可以通过在热点key后面拼接集群节点编号,然后将这些备份key分散到所有集群节点中,客户端访问热点key的时候也在热点key后面随机拼接集群节点编号,将热点key的请求分散到不同集群节点上。
备份热点key
config set maxmemory-policy volatile-lfu
config set maxmemory-policy allkeys-lfu
必须配合maxmemory-policy的属性
./redis-cli --hotkeys
使用
redis-cli –hotkeys
凭借业务经验,进行预估哪些是热key
在客户端进行收集。比如在redis客户端执行redis命令之前,加入一行代码进行命令数据收集,,然后通过网络将收集的命令发送出去,确定是对客户端代码有入侵。
如何找到热key
热key
redis 大key,热key怎么解决
在传输数据时,保证二进制数据的信息安全,也就是不被篡改、破译等,如果被攻击,能够及时检测出来
概念
C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。
c字符串
SDS(simple dynamid string )的 API都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制、过滤、或者假设,数据在写入时是什么样的,它被读 取时就是什么样。
通过使用二进制安全的SDS,而不是C字符串,使得Redis不仅可以保存文本数据,还可以保存任意格式的二进制数据。
SDS使用len属性的值而不是空字符来判断字符串是否结束
sds
redis二进制安全怎么实现
mget
mset
string
hmset
hmget
hash
命令
redis.clients.jedis.Jedis#mget
代码
批量get/set(multi get/set)
客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。服务端处理命令,并将结果返回给客户端。
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
pipeline就是把一组命令进行打包,然后一次性通过网络发送到Redis。同时将执行的结果批量的返回回来. 使用管道发送命令时,服务器将被迫回复一个队列答复,占用很多内存。所以,如果你需要发送大量的命令,最好是把他们按照合理数量分批次的处理,例如10K的命令,读回复,然后再发送另一个10k的命令,等等。
redis.clients.jedis.MultiKeyPipelineBase
父类
redis.clients.jedis.Pipeline
yum install nc
安装nc命令
(printf \"PING\\PING\\PING\\\") | nc localhost 6379
nc打包多个命令
管道(pipelining)
事务(transaction)
在Redis中,管道是通过RESP,即redis协议来实现的,它允许在一个消息包中按照指定格式传递多个命令。而事务是通过命令实现的,因此管道和事务之间并不冲突,事务可以承载与管道之上。在某些场景,需要在一次请求处理中发起多次事务的场景下,通过引入管道,可以获得略高于单独执行多次事务的性能,但是两者的差距非常小,小到可以忽略。
基于事务的管道(transaction in pipelining)
redis批量命令
主要利用redis的setnx命令进行,setnx:\"set if not exists\"就是如果不存在则成功设置缓存同时返回1,否则返回0。
入库
出库
占用
释放
warehouseCode+itemCode
key
分布式锁
库存
仓库/货主/覆盖区域
字典
地区
基础数据
缓存
redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景
限时业务的运用
redis在项目中是怎么用的
当我们往数据库写数据的时候我们去更新缓存,包括先更新缓存再更新数据库和先更新数据库再更新缓存。
如果你是一个写数据库场景比较多,而读数据场景比较少的业务需求,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。
效率问题
同时有请求A和请求B进行更新操作,那么会出现 (1)线程A更新了数据库 (2)线程B更新了数据库 (3)线程B更新了缓存 (4)线程A更新了缓存。这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。
线程安全性问题
缺点
写时更新
当我们往数据库写数据的时候我们直接删除缓存,然后其他请求读数据的时候更新缓存。包括先删除缓存再更新数据和先更新数据库再删除缓存。
1、缓存刚好失效 线程 A 查询数据库,得一个旧值
2、线程 B 将新值写入数据库
3、线程 B 删除缓存
4、线程 A 将查到的旧值写入缓存
需要线程A读操作必需在B线程写操作前进入数据库操作,而又要晚于B写操作更新缓存,所有的这些条件都具备的概率基本并不大
出现概率极低
设置缓存的过期时间,这样可以达到最终一致性
问题一、如果在高并发的场景下,会出现数据库与缓存数据不一致
将需要删除的 key 发送到消息队列中 自己消费消息,获得需要删除的 key 不断重试删除操作,直到成功
第一步操作数据库成功,第二步删除缓存失败,会导致数据库里是新数据,而缓存里是旧数据
第一步操作数据库就失败了,第二步更新缓存不会执行,不会出现数据不一致。
问题二、如果删除缓存失败或更新数据库失败了会怎样?
在高并发下相对出现数据不一致问题概率很低,但在原子性被破坏时(删除缓存失败或更新数据库失败)会出现数据一致性问题
解决方案就是要给KEY设置过期时间
先更新数据库在删除缓存
1、线程 A 删除了缓存 线程 B 查询,发现缓存已不存在
2、线程 B 去数据库查询得到旧值
3、线程 B 将旧值写入缓存
4、线程 A 将新值写入数据库
第一步删除缓存成功,第二步更新数据库失败,数据库和缓存的数据还是一致的。
第一步删除缓存就失败了,第二步更新数据库不会去执行,数据库和缓存的数据还是一致的。
并不会导致数据不一致问题
在高并发下相对更容易出现数据不一致问题,但在原子性被破坏时(删除缓存失败或更新数据库失败)并不会出现数据一致性问题
先删除缓存再更新数据
分类
写时删除,读时更新
1、先淘汰缓存
2、再写数据库
要造成脏缓存,就需要在缓存被删除后,数据库被更新前有请求读取到了旧数据并更新了缓存,那么这边睡眠一秒后再次删除缓存就可以把这个短暂的时间间隔内产生的脏缓存再次删除掉。
3、休眠1秒/写数据成功之后
4、再次淘汰缓存.
延时双删策略
启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。
原理图
阿里canal方案
其原理和阿里的Canal一致
LinkedIn的DataBus
其他解决方案
缓存与数据库的一致性(20210511)
面试
redis.conf 参数配置:# maxmemory <bytes>
64 位系统不限制内存,32 位系统最多使 用 3GB 内存
动态修改:config set maxmemory 2GB
Redis 的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来 决定清理掉哪些数据,以保证新数据的存入
Least Recently Used:最近最少使用
在带有过期时间的键,中删除最近最少使用的
volatile-lru
在所有带有过期时间的Key中,删除最近最少使用的
allkeys-lru
Redis优化过:
LRU
Least Frequently Used,最不常用(最近使用频次最少的),4.0 版本新增
在带有过期时间的键中删除最不常用的。
volatile-lfu
在所有的键中选择最不常用的,不管数据有没有设置超时属性。
allkeys-lfu
LFU
随机删除
在带有过期时间的键中随机删除
volatile-random
随机删除所有键,直到腾出足够内存为止。
allkeys-random
Rondom
默认策略,不会删除任何数据,达到内存最大后,返回错OOM,只响应读操作
noeviction
根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到 noeviction 策略
volatile-ttl
config set maxmemory-policy volatile-lru
动态修改淘汰策略
默认推荐使用:建议使用 volatile-lru,在保证正常服务的情况下,优先删除最近最少使用的 key。
继承LinkedHashMap并重写removeEldestEntry方法
LinkedHashMap是有序的,且默认为插入顺序,先进入在前,后进去的在后面,
思考如何基于一个数据结构实现LRU算法:
淘汰策略
每个设置过期时间的 key 都需要创建一个定时器,到过期时间就会立即清除
效率很高,但是key很多的时候 也创建了大量的定时器,对内存很友好,占用大量的 CPU 资源去处理过期的 数据
定时过期
只有当访问一个 key 时,才会判断该 key 是否已过期,过期则清除
该策略可以最 大化地节省 CPU 资源,却对内存非常不友好,极端情况可能出现大量的过期 key 没有再 次被访问,从而不会被清除,占用大量内存
惰性过期(被动淘汰)
每隔一定的时间,会扫描一定数量的数据库的 expires 字典中一定数量的 key,并清 除其中已过期的 key。该策略是前两者的一个折中方案
定期过期
过期策略
内存回收
指查询一个根本不存在的数据,缓存层和存储层都不会命中
缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端的意义
自身业务代码或者数据出现问题;
一些恶意攻击、爬虫等造成大量空命中
原因
1、空值做了缓存,意味着缓存层中存了更多的键,需要更多内存空间。比较有效的方法是针对这类数据设置一个较短的过期时间。
2、缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。
缓存空对象
是一种空间利用率较高的概率型数据结构,用来测试一个元素是否在集合中。但是存在一定可能,导致结果误判。即元素不在集合中,查询结果却返回元素在集合中。
当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不存在;如果都是1,则被检元素很可能存在
Bloom Filter跟单哈希函数Bit-Map不同之处在于:Bloom Filter使用了k个哈希函数,每个字符串跟k个bit对应。从而降低了冲突的概率。
原理
二进制组成的数组,占用内存极少,并且插入和查询速度都足够快
优点
随着数据的增加,误判率会增加
无法判断数据一定存在
无法删除数据
布隆过滤器
缓存穿透(缓存和数据库都不存在)
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
设置热点数据永远不过期。
在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
加互斥锁
解决方案
缓存击穿(某个热点key失效)
Redis中大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,此时数据库层的负载压力会骤增
在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,以防止大量的key在同一时刻过期;
对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;
延长热点key的过期时间或者设置永不过期
缓存雪崩(大量key同时过期)
缓存三大问题
在集群模式下这个配置是不起作用的,集群客户端是不支持多数据库db的,只有一个数据库默认是SELECT 0;
redis在单机模式下redis.conf配置文件中默认的数据库数量是16个
集群slave从节点默认是不支持读写操作的,但是在执行过readonly命令后可以执行读操作
ERR SELECT is not allowed in cluster mode
详细
实体entity未被序列化
配置序列化
发生异常错误信息(redis缓存处理序列化时)
使用set方法错误,使用4个参数的set方法,带过期时间
无法读取json Could not read JSON: Invalid UTF-32 character 0x5b21636f
WRONGTYPE Operation against a key holding the wrong kind of value tells that another object by the same key is stored but with different type.
有重复的key
换个key即可
redisssion加锁失败 Operation against a key holding the wrong kind of value
常见问题
1、安装
name 名称
host 主机
port端口
auth 密码
connetc to redis server
连接
2、使用
get AUTH:STATIC:TENANT:CODE:P001
ctr+T
3、打开控制台
子主题
4、页面上参数
效果
5、打开0数据库
redis desktop manager
登录
查找key
更新key
复制key
RedisClientWin10.rar
redis-client中国红
客户端连接工具
Cacheable
清除
CacheEvict
更新
CachePut
常用注解
aop
实现原理
注解
RedisTemplate/StringRedisTemplate
spring data redis(20210305)
jedis
java连接客户端
redis客户端
普通实现
lua脚本实现
哨兵模式
集群
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能
简介
http://www.lua.org/download.html
下载地址
解压
make all test
编译
linux安装
lua -v
查看版本信息
测试hello world
脚本式编程
交互式编程
实现分布式锁
lua脚本(20210305)
在小数据量情况下使用没什么问题,数据量大会导致 Redis 锁住及 CPU 飙升,在生产环境建议禁用或者重命名!
keys *
删除Redis中当前所在数据库中的所有记录,并且该命令是原子性的,不会终止执行,一旦执行,将不会执行失败
flushdb
删除Redis中所有数据库中的所有记录,并且该命令是原子性的,不会终止执行,一旦执行,将不会执行失败。
flushall
客户端可修改 Redis 配置
config
相关命令
rename-command ,修改即可完成命令禁用
命令禁用
重命名命令
禁止危险命令
requirepass 123456
requirepass password
配置文件配置redis.conf
config set requirepass 123456
使用命令配置
密码
安全
http://doc.redisfans.com/script/eval.html#lua-redis
参考
lua脚本
sript
用于指定键名参数的个数
numkeys
在脚本中所用到的那些 Redis 键(key),为一个数组,可通过KEYS[1] , KEYS[2]访问
key [key ...]
通过全局变量 ARGV 数组访问,可通过ARGV[1] 、 ARGV[2]访问
arg [arg ...]
EVAL script numkeys key [key ...] arg [arg ...]
语法
redis.call()
redis.pcall()
执行函数
示例
在 Lua 数据类型和 Redis 数据类型之间转换
eval
1)EVAL 命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)。
2)Redis 有一个内部的缓存机制,因此它不会每次都重新编译脚本,不过在很多场合,付出无谓的带宽来传送脚本主体并不是最佳选择。
3)为了减少带宽的消耗, Redis 实现了EVALSHA 命令,它的作用和 EVAL 一样,都用于对脚本求值,但它接受的第一个参数不是脚本,而是脚本的 SHA1 摘要
如果有给定的 SHA1 校验和所指定的脚本,那么执行这个脚本
没有,那么它返回一个特殊的错误,提醒用户使用 EVAL 代替 EVALSHA
返回结果
测试
EVALSHA
SCRIPT EXISTS script [script ...]
判断脚本是否存在
SCRIPT FLUSH
刷新脚本缓存
SCRIPT KILL
杀死正在执行的脚本
SCRIPT LOAD script
加载脚本
其他脚本命令
script脚本命令
http://doc.redisfans.com/key/scan.html
SCAN 命令是一个基于游标的迭代器(cursor based iterator): SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。
SCAN cursor [MATCH pattern] [COUNT count]
用于进行下一次迭代的新游标
返回值1
包含了所有被迭代的元素
返回值2
返回值
scan curor
不加参数
进行模式匹配,返回符合条件的元素
match选项
每次迭代中应该从数据集里返回多少元素
COUNT 参数的默认值为 10
count选项
以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0
完整遍历
scan
TTL key
当 key 不存在,或者 key 没有设置剩余生存时间时,命令都返回 -1
ttl
查找所有符合给定模式pattern(正则表达式)的 key 。
时间复杂度为O(N),N为数据库里面key的数量。
速度极快,在一百万的key数据库中查询时间大约是40毫秒。
KEYS pattern
keys
Key(键)
CONFIG GET parameter
用于取得运行中的 Redis 服务器的配置参数(configuration parameters)
CONFIG SET parameter value
动态地调整 Redis 服务器的配置(configuration)而无须重启
CONFIG REWRITE
对启动 Redis 服务器时所指定的 redis.conf 文件进行改写,即保存到redis.conf文件中
CONFIG RESETSTAT
重置 INFO 命令中的某些统计数据
Server(服务器)
命令(20210306)
默认不开启
AOF 采用日志的形式来记录每个写操作,并追加到文件中。开启后,执行更改 Redis 数据的命令时,就会把命令写入到 AOF 文件中。 Redis 重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复 工作
# 开关appendonly no
appendfilename "appendonly.aof"
redis.conf
不是的,是先进入了系统的缓存区,硬盘缓存区
表示每秒执行一次 fsync,可能会导致丢失这 1s 数据。通常选择 everysec , 兼顾安全性和效率。
everysec
表示每次写入都执行 fsync,以保证数据同步到磁盘,效率很低;
always
表示不执行 fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
no
appendfsync
数据都是实时持久化到磁盘吗?
如 set abc 666,执行 1000 次,结果都是 abc=666。
以使用命令 bgrewriteaof 来重写
默认值为 100。aof 自动重写配置,当目前 aof 文件大小超过上一次重写的 aof 文件大小的 百分之多少进行重写,即当 aof 文件增长到一定大小的时候,Redis 能够调用 bgrewriteaof 对日志文件进行重写。当前 AOF 文件大小是上次日志重写得到 AOF 文件大小的二倍(设 置为 100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
默认 64M。设置允许重写的最小 aof 文件大小,避免了达到约定百分比但尺寸仍然很小的 情况还要重写。
auto-aof-rewrite-min-size 64mb
当 AOF 文件的大小超过所设定的阈值 时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集
文件越来越大,怎么办?
no-appendfsync-on-rewrite
aof 文件可能在尾部是不完整的,当 redis 启动的时候,aof 文件的数据被载入内存。重启 可能发生在 redis 所在的主机操作系统宕机后,尤其在 ext4 文件系统没有加上 data=ordered 选项,出现这种现象。redis 宕机或者异常终止不会造成尾部不完整现象,可以选择让 redis 退出,或者导入尽可能多的数据。
如果选择的是 yes,当截断的 aof 文件被导入的时候, 会自动发布一个 log 给客户端然后 load。如果是 no,用户必须手动 redis-check-aof 修复 AOF 文件才可以。默认值为 yes。
aof-load-truncated
重写过程中,AOF 文件被更改了怎么办?
带来的问题:
AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步 一次,Redis 最多也就丢失 1 秒的数据而已。
对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大(RDB 存的是数据快照
优点:
AOF
默认的持久化策略,生成dump.rdb
save 900 1 # 900 秒内至少有一个 key 被修改(包括添加)不使用时 直接注释
配置保存文件路径,是否开启压缩
配置规则触发 redis.conf
save 在生成快照的时候会阻塞当前 Redis 服务器, Redis 不能处理其他命令。如果 内存中的数据比较多,会造成 Redis 长时间的阻塞,生产环境不建议使用
save
执行 bgsave 时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请 求
Redis 进程执行 fork 操作创建子进程(copy-on-write),RDB 持久化 过程由子进程负责,完成后自动结束
bgsave
手动触发
shutdown 触发,保证服务器正常关闭
flushall,RDB 文件是空的,没什么意义
练习操作命令
1、RDB 是一个非常紧凑(compact)的文件,它保存了 redis 在某个时间点上的数据 集。这种文件非常适合用于进行备份和灾难恢复
2、生成 RDB 文件的时候,redis 主进程会 fork()一个子进程来处理所有保存工作,主 进程不需要进行任何磁盘 IO 操作
3、RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
优势:
RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要 执行 fork 操作创建子进程,频繁执行成本过高
在一定间隔时间做一次备份,所以如果 redis 意外 down 掉的话,就会丢失最后 一次快照之后的所有修改(数据有丢失)
劣势:
RDB 触发
RDB快照
如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要 比 AOF 恢复的速度要快
AOF和RDB的选择
持久化方式
set name kklt
get name
字符串
每个键值对都会有一个 dictEntry
dictEntry的结构
分支主题
value存在redisObject中
int,存储 8 个字节的长整型(long,2^63-1)。
raw,存储大于 44 个字节的字符串(3.2 版本之前是 39 字节)。
SDS是Redis 中字符串的实现。 内部编码:
key 存在SDS中
SDS的相关问题 在笔记中查看
底层实现原理:hashtable
SET key value [EX seconds] [PX milliseconds] [NX|XX]
set
当 key 不存在时,返回 nil ,否则,返回 key 的值。
如果 key 不是字符串类型,那么返回一个错误。
GET key
get
String
HSET website google www.google.com baidu www.baidu.com
HGET website google
哈希
当存储 hash 数据类型时, 我们把它叫做内层的哈希
ziplist:OBJ_ENCODING_ZIPLIST(压缩列表)
hashtable:OBJ_ENCODING_HT(哈希表)
实现原理:hashtable
具体结构查看笔记,它是C语言的结构,大概了解即可
包含键值对的无序散列表。value 只能是字符串,不能嵌套其他类型
hash的结构是:key field value
Hash
SADD bbs aa bb cc
SMEMBERS bbs
集合
intset 或 hashtable 存储 set。如果元素都是整数类型,就用 inset 存储。 如果不是整数类型,就用 hashtable(数组+链表的存来储结构)
如果元素个数超过 512 个,也会用 hashtable 存储。
实现原理:
sorted set,有序的 set,每个元素有个 score。 score 相同时,按照 key 的 ASCII 码排序
ZADD page_rank 10 google.com
同时满足以下条件时使用 ziplist 编码: 元素数量小于 128 个 所有 member 的长度都小于 64 字节
超过阈值之后,使用 skiplist+dict 存储。
有序集合
zset
LPUSH mylist a b c
查看mylist中的所有数据
LRANGE mylist 0 -1
列表
实现结构:quicklist
List
Hyperloglog
Geo
Streams
数据类型
对象缓存,全页缓存。
热点数据缓存
STRING 类型 setnx 方法,只有不存在时才能添加成功,返回 true。
分布式 Session
INT 类型,INCRBY,利用原子性
全局 ID
文章的阅读量,微博点赞数,允许一定的延迟,先写入 Redis 再定时同步到 数据库
INT 类型,INCR 方法
计数器
以访问者的 IP 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false。
限流
String 类型的 BITCOUNT(1.6.6 的 bitmap 数据结构介绍)。
位统计
代表的意思是: student:1:sno 为key GP16666 为value
利用分层:mset student:1:sno GP16666 student:1:sname 沐风 student:1:company 腾讯
缺点:key 太长,占用的空间太多
问题:String类型如何存一个对象
仓库
货主
基础信息的存储
mcsp:stc:inv:stock:OZIW005:21051030Z00143:1
仓库+商品+库存类型
库存值
mcsp:stc:inv:stocklock:ZNKOW0102:21061110000905:1
锁定值
账面库存
mcsp:stc:inv:sales:IQHTW0001:21063210000025:SELF
仓库+商品+分区类型
mcsp:stc:inv:saleslock:MCPRW0001:31022210004823:SELF
销售库存
库存存储
String类型
这里的INT类型是指 set key value 的value是int set age 12
把所有相关的值聚集到一个 key 中,节省内存空间
只使用一个 key,减少 key 冲突
当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗
存储字符串,Hash 与 String 的主要区别?
举例:存储对象 key Filed value qsTeacher age 15
1、Field 不能单独设置过期时间
2、没有 bit 操作
3、需要考虑数据量分布的问题(value 值非常大的时候,无法分布到多个节点)
不适合的场景:
购物车
String 可以做的事情,Hash 都可以做。
hash:
存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色
消息队列
用户消息时间线 timeline
场景:
随机获取元素 spop myset
点赞、签到、打卡
商品标签
sdiff set1 set2
差集
sinter set1 set2
交集
sunion set1 set2
并集
商品筛选
smembers forbidden_list
黑名单列表
Set
排行榜
应用场景
redis
0 条评论
下一页
为你推荐
查看更多