目录
什么是Redis
1、Redis简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
2、Redis优势
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
3、Redis和其他的key-value存储有什么不同
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
缓存雪崩
大面积故障或大量缓存失效,然同时访问数据库。
解决:对不同类型数据,缓存不同周期,尽可能的分散缓存过期时间
其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
缓存穿透
查询一个数据库一定不存在的数据,会导致每次查询数据库
解决:查询为空,可以设置一个默认值或空值到缓存,并给一个过期时间。
缓存击穿
是指一个key非常的热门,承受着大量的并发和集中访问,一旦这个key失效的瞬间,那么大量的访问就直接访问数据库,给数据库服务里面带来大量压力,就像在一个屏罩上击穿了一个洞。
当然大多数情况下很难对数据库造成压垮性的压力。
解决:像电商的主打商品或者爆款,可以设置让缓存永远不过期。
缓存预热
系统上线后,将相关的缓存数据和热门数据直接加载到缓存系统。
处理方案:
- 写个缓存的h5页面,后台人员刷新就可以了
- 数据量少的,可以在项目启动时就加载到缓存中
- 定时任务来刷新缓存
key名称规范
缓存key名称规范,尽量全局配置。hash key的个数不要太多,可以将同类型的放到散列表中。
分布式锁
获取锁和释放锁的实例代码,来自项目中实际中的使用类。
最大内存设置
若内存占满后,有各样的问题。
设置后,根据配置的数据淘汰策略来淘汰数据,释放空间; 如没有设置,redis会对所有写请求返回错误,但读请求仍然可以正常执行
数据淘汰机制
- volatile-lru:从已设置过期时间的数据集中挑选使用最近最少的数据淘汰
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集中任意随机挑选数据淘汰
- allkeys-lru: 从数据集中挑选最近最少使用的数据淘汰
- allkeys-random: 从数据集中任意随机挑选数据淘汰
- no-enviction(驱逐):禁止淘汰数据
缓存注意事项
- 缓存的过期时间需要合理
- 缓存的命中率,应释放哪些数据
- 缓存的穿透和雪崩问题的处理
- 禁止使用redisTemplate.keys() 这个方法,严重影响性能
- 将key的失效时间错开,以及长耗时命令要避免(redis单线程)
- Redis尽量使用池的方式,可以减少TCP连接次数
缓存更新策略
保证数据的一致性,使用缓存更新策略
方法A:
步骤:1、删除缓存 ; 2、更新db; 3、下一次读操作没有命中缓存时,更新缓存
问题:如果另一个读任务发生在步骤2之前,那么缓存的内容就是“更新db”之前的“脏数据”
方法B:
步骤:1、更新db,2、更新缓存
问题:如果发生并发更新数据,程序不能保证“更新缓存”的先后顺序,存在“脏数据”的可能
方法C:
步骤:1、更新db,2、删除缓存,3、下一次读操作没有命中缓存时,更新缓存
问题:如果有一个读任务在步骤1“更新db”之前,没有命中缓存去db读取了“老数据”,在步骤2之后将“老数据”放到了缓存中,那么缓存中就是“脏数据”
思考:方法C的策略在实际场景中发生的概率要比前两个要低很多,但是要完全杜绝这种问题,可以采用分布式事务-2PC/3PC or Paxos,但分布式事务会增加系统的复杂度,降低系统的可用性,一般采用方法C。
关于2PC,3PC,Paxos的内容讲解:https://blog.csdn.net/xmzyjr123/article/details/86763021
集群分片 or 主从复制
内容较多下次更新
pipline的方式批量处理
Redis是一个cs模式的Tcp服务,类似于Http请求。
使用Pipeline在对Redis批量读写的时候,性能上有非常大的提升。
Redis性能调优
尽管Redis是一个非常快速的内存数据存储媒介,也并不代表Redis不会产生性能问题。
针对Redis的性能优化,主要从下面几个层面入手:
- 最初的也是最重要的,确保redis没有执行耗时长的命令
- 使用pipelining(批处理) 将连续执行的命令组合执行
- 操作系统的Transparent huge pages 必须关闭:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
- 使用物理机直接部署Redis,如果是虚拟机,天然就有延迟
- 考虑引入读写分离机制
- 检查数据持久化策略