- 快速批量添加(使用管道)
for((i=1;i<=100*10000;i++)); do echo "set k$i v$i" >> /tmp/redisTest.txt ;done;
cat /tmp/redisTest.txt | redis-cli -h 127.0.0.1 -p 6379 -a redis --pipe
管道(pipeline)可以一次性发送多条命令给服务端,服务端依次处理完完毕后,通过一条响应一次性将结果返回,通过减少客户端与redis的通信次数来实现降低往返延时时间。pipeline实现的原理是队列,先进先出特性就保证数据的顺序性。为了解决RTT往返回时,仅仅是将命令打包一次性发送,对整个Redis的执行不造成其它任何影响。
https://redis.io/docs/manual/pipelining/
- Pipeline与原生批量命令对比
- 原生批量命令是原子性(例如:mset, mget), pipeline是非原子性
- 原生批量命令一次只能执行一种命令,pipeline支持批量执行不同命令
- 原生批命令是服务端实现,而pipeline需要服务端与客户端共同完成
- Pipeline与事务对比
- 事务具有原子性,管道不具有原子性
- 管道一次性将多条命令发送到服务器,事务是一条一条的发,事务只有在接收到exec命令后才会执行,管道不会
- 执行事务时会阻塞其他命令的执行,而执行管道中的命令时不会
- 使用Pipeline注意事项
- pipeline缓冲的指令只是会依次执行,不保证原子性,如果执行中指令发生异常,将会继续执行后续的指令
- 使用pipeline组装的命令个数不能太多,不然数据量过大客户端阻塞的时间可能过久,同时服务端此时也被迫回复一个队列答复,占用很多内存
- 批量删除【好像不能选库】
redis-cli -h 192.168.10.20 -a redis keys "k*" | xargs redis-cli -h 192.168.10.20 -a redis del
- 如何禁用危险命令误删误用
通过配置设置禁用这些命令,redis.conf在SECURITY这一项中,使用会报错command not found…
rename-command flushdb ""
rename-command flushall ""
rename-command keys ""
- big key
string ≥10kb,list、hash、set、zset个数超过500个
- 危害
- 内存不均,集群迁移困难
- 超时删除,大key删除作梗
- 网络流量阻塞
- 找出
-i 0.1表示每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变长
好处:给出每种数据结构Top 1 bigkey,同时给出每种数据类型的键值个数+平均大小
不足:想查询大于10kb的所有key,–bigkeys参数就无能为力了,需要用到memory usage key来计算每个键值的字节数
redis-cli -h 127.0.0.1 -p 6379 -a redis --bigkeys -i 0.1
- 删除,code
- string:一般用del,如果过于庞大unlink
- hash:使用hscan每次获取少量field-value,再使用hdel删除每个field
- list:使用ltrim渐进式逐步删除,直到全部删除完成
- set:使用sscan每次获取部分元素,再使用srem命令删除每个元素
- zset:使用zscan每次获取部分元素,再使用ZREMRANGEBYRANK命令删除每个元素
- 调优:redis.conf配置文件LAZY FREEING
- lazyfree-lazy-server-del no改为yes
- replica-lazy-flush no改为yes
- lazyfree-lazy-user-del no改为yes
- 缓存问题
缓存预热:@PostConstruct初始化数据
缓存问题 | 产生原因 | 解决方案 |
缓存更新方式 | 数据变更、缓存时效性 | 同步更新、失效更新、异步更新、定时更新 |
缓存不一致 | 同步更新失败、异步更新 | 增加重试、补偿任务、最终一致 |
缓存雪崩 | 服务挂掉、大量key同时失效 | 快速失败熔断、主从模式、集群模式 |
缓存穿透 | 恶意攻击,redis和mysql都不存在的数据 | 空对象缓存、bloomfilter过滤器 |
缓存击穿 | 热点key失效,全查mysql | 互斥更新、随机退避、差异失效时间 |
- 布隆过滤器Bloom Filter
由一个初值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素
- 使用场景:
- 缓存击穿问题,提前把缓存的key存入过滤器
- 黑名单校验,识别垃圾邮件
- 白名单校验,识别合法用户
- 优点:高效地插入和查询,内存占用bit空间少
- 缺点:
- 不能删除元素(会导致误判率增加,存的数据会共享同一位置,存在误删)
- 存在误判,不能精准过滤(有是有可能有,无是一定无)
guava提供的Bloom Filter将数据存入内存,用redis的bitmap也可以实现,用在分布式环境
- 数据库和缓存双写一致性
策略 | 高并发多线程条件下 | 问题 | 现象 | 解决方案 |
先删除redis缓存,再更新mysql | 无 | 缓存删除成功但数据库更新失败 | Java程序从数据库中读到旧值 | 再次更新数据库,重试 |
有 | 缓存删除成功但数据库更新中…有并发读请求 | 并发请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值 | 延迟双删 | |
先更新mysql,再删除redis缓存 | 无 | 数据库更新成功,但缓存删除失败 | Java程序从redis中读到旧值 | 再次删除缓存,重试 |
有 | 数据库更新成功但缓存删除中…有并发读请求 | 并发请求从缓存读到旧值 | 等待redis删除完成,这段时间有数据不一致,短暂存在 |
在大多数业务场景下,建议优先使用先更新数据库,再删除缓存的方案。理由如下:
1 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql。
2.如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
使用canal 8. 双检加锁策略 多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。
后面的线程进来发现已经有缓存了,就直接走缓存。
9. 分布式锁
具备的条件:
- 独占性:任何时刻只能有且仅有一个线程持有
- 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况;高并发请求下,依旧性能好使
- 防死锁:必须有超时控制机制或者撤销操作,有兜底终止跳出方案
- 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放
- 重入性:同一个节点的同一个线程如果获得锁之后,也可以再次获取这个锁
code
- 十大数据类型
| 类型 | type key |
|–|–|
| string | string |
| list | list |
| hash | hash |
| set | set |
| zset | zset |
| bitmap | string |
| hyperloglog | string |
| geo | zset |
| stream | stream |
| bitfield | 看具体key的value |