1. 快速批量添加(使用管道)
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组装的命令个数不能太多,不然数据量过大客户端阻塞的时间可能过久,同时服务端此时也被迫回复一个队列答复,占用很多内存
  1. 批量删除【好像不能选库】
redis-cli -h 192.168.10.20 -a redis  keys "k*" | xargs redis-cli -h 192.168.10.20 -a redis del
  1. 如何禁用危险命令误删误用

通过配置设置禁用这些命令,redis.conf在SECURITY这一项中,使用会报错command not found…

rename-command flushdb ""
rename-command flushall ""
rename-command keys ""
  1. 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
  • 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
  1. 缓存问题

缓存预热:@PostConstruct初始化数据

缓存问题

产生原因

解决方案

缓存更新方式

数据变更、缓存时效性

同步更新、失效更新、异步更新、定时更新

缓存不一致

同步更新失败、异步更新

增加重试、补偿任务、最终一致

缓存雪崩

服务挂掉、大量key同时失效

快速失败熔断、主从模式、集群模式

缓存穿透

恶意攻击,redis和mysql都不存在的数据

空对象缓存、bloomfilter过滤器

缓存击穿

热点key失效,全查mysql

互斥更新、随机退避、差异失效时间

  1. 布隆过滤器Bloom Filter

由一个初值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素

  • 使用场景:
  • 缓存击穿问题,提前把缓存的key存入过滤器
  • 黑名单校验,识别垃圾邮件
  • 白名单校验,识别合法用户
  • 优点:高效地插入和查询,内存占用bit空间少
  • 缺点:
    - 不能删除元素(会导致误判率增加,存的数据会共享同一位置,存在误删)
    - 存在误判,不能精准过滤(有是有可能有,无是一定无)

guava提供的Bloom Filter将数据存入内存,用redis的bitmap也可以实现,用在分布式环境

  1. 数据库和缓存双写一致性

策略

高并发多线程条件下

问题

现象

解决方案

先删除redis缓存,再更新mysql


缓存删除成功但数据库更新失败

Java程序从数据库中读到旧值

再次更新数据库,重试


缓存删除成功但数据库更新中…有并发读请求

并发请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值

延迟双删

先更新mysql,再删除redis缓存


数据库更新成功,但缓存删除失败

Java程序从redis中读到旧值

再次删除缓存,重试


数据库更新成功但缓存删除中…有并发读请求

并发请求从缓存读到旧值

等待redis删除完成,这段时间有数据不一致,短暂存在

在大多数业务场景下,建议优先使用先更新数据库,再删除缓存的方案。理由如下:
1 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql。
2.如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
使用canal 8. 双检加锁策略 多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它。
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。
后面的线程进来发现已经有缓存了,就直接走缓存。
9. 分布式锁
具备的条件:
- 独占性:任何时刻只能有且仅有一个线程持有
- 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况;高并发请求下,依旧性能好使
- 防死锁:必须有超时控制机制或者撤销操作,有兜底终止跳出方案
- 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放
- 重入性:同一个节点的同一个线程如果获得锁之后,也可以再次获取这个锁
code

  1. 十大数据类型
    | 类型 | type key |
    |–|–|
    | string | string |
    | list | list |
    | hash | hash |
    | set | set |
    | zset | zset |
    | bitmap | string |
    | hyperloglog | string |
    | geo | zset |
    | stream | stream |
    | bitfield | 看具体key的value |