java基础-redis缓存篇
Redis的数据类型
Redis总共支持5种数据类型,分别是:
类型 | 说明,命令 |
---|---|
String | set ,get |
Hash | HMSET myhash field1 "Hello" field2 "World" HGET myhash field1 |
List | lpush lrange |
Set | sadd smembers |
(ZSet)Sorted Set | zadd ZRANGEBYSCORE runoob 0 1000 |
AOF和RDB区别
RDB: redis database,在某一个时间点将Redis存储的数据快照一份存储在磁盘上,生成的文件可以随时备份.RDB恢复比AOF的方式更高效.对于数据完整性不是很敏感的可以用该方式.
AOF:append only file ,将Redis所有的写指令存储下来,这样Redis重启的时候按照指令执行一遍就可以了.RDB和AOF方式可以同时使用,以AOF的优先,因为一般AOF的数据恢复更全.但是更慢.
appendonly yes
即可打开AOF功能
默认每隔一秒会fsync一次到文件中
提供了redis-check-aof
工具用来日志文件恢复
提供了AOF文件重写的方式来减小文件大小.
AOF文件和RDB文件一样可以随时备份,不影响完整性.
AOF rewrite过程
- Redis(fork)一个“重写子进程”,读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中
- 与此同时,主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样就保证了原有的AOF文件是完整的.防止重写子进程出意外.
- 当" 重写子进程"完成重写任务后就会向主进程发信号,主进程就会将内存缓存区中的数据追加到新的文件中.
- 当追加结束后,Redis就会用新的AOF文件替换掉原有的文件,之后新的指令就写到新的AOF文件中.
缓存雪崩
- 什么是缓存雪崩
由于各种原因导致缓存服务宕机了.此时大量请求发到后端,后端因负载有限很快也宕机了,导致整个服务不可用或很慢. - 解决方案
- 保证缓存层高可用,用Redis Sentinel 和Redis Cluster
- 限流,当缓存服务失效时用hystrix限流
- 充分测试,了解后端的抗压能力以及会出现的问题并做出一些预案
缓存击穿
- 什么是缓存击穿
当用户请求了缓存中没有缓存的数据,该请求会一直到数据库上,数据库上也没有,如果有大量的这种请求,相当于缓存失效了,没有起到保护数据库的作用,数据库会很快挂掉. - 解决方案
- 缓存空值,但是这个会浪费大量的存储空间,最好对这种值设置一个相对短的过期时间
- 用布隆过滤器,把数据库有的值缓存起来,没有的值直接返回错误信息.适合更新不频繁的数据.
一致性Hash
-
第一种 传统的 对key值的hashcode和 节点 求模 ;这种会数据不均衡,当有一台死了会导致大部分数据都命中不了.而不只是1/n (n = 节点数)
-
第二种
image.png -
第三种
image.png -
Redis 独创的Hash slot
一个 Redis 集群包含 16384 个哈希槽(hash slot),
数据库中的每个键都属于这 16384 个哈希槽的其中一个,
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽,
其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和
这样的方式使得用户可以很容易地向集群中添加或者删除节点
总结:环形Hash空间可以解决一致性问题,有效防止一个节点宕机导致rehash使得大部分数据都失效的问题;添加虚拟节点方法可以解决数据不平衡问题.参考
缓存一致性
写:先写数据库再淘汰缓存,如果计算缓存值要很长时间,为了避免缓存失效后第一次访问很卡可以主动更新缓存,做到缓存永不过期的效果,对于热点缓存失效问题也可以用这个方法解决.
读:先读缓存再读数据库
缓存命中率
命中率 = 命中数 / 总请求数
缓存一般都是用在读多写少的场景,如果读少写多,用缓存意义不大.命中率会很低.
通常情况下,缓存的粒度越小,命中率会越高;
可以通过缓存预加载(预热)、增加存储容量、调整缓存粒度、更新缓存等手段来提高命中率
缓存无底洞
随着节点数的增加,读取缓存的时间因为要加上节点间通讯时间而更慢.表现为新增了很多服务器性能不但没有提升反而下降了.
- 方案:
- 尽量让需要访问的数据在一个节点上.用hash_tag实现
- 对于批量操作,客户端计算出每个节点的key子列表,之后对每个节点执行mget或者pipeline操作(这一步可以弄成多线程并行)
Redis的使用,怎么保证高可用。
用主从,Redis Sentinel和Redis Cluster,Redis Cluster引入了master和slave节点,每个master节点会对应两个对应的slave节点,这样在整个集群中,任意两个节点宕机都不会导致数据不可用.当master节点退出后,集群会自动选择一个slave节点为新的master节点.
redis和Memcached的区别
Redis | Memcached | |
---|---|---|
支持的数据结构 | 哈希、列表、集合、有序集合 | 纯kev-value |
持久化支持 | 有 | 无 |
高可用支持 | redis天然支持集群功能,可以实现主动复制,读写分离。官方也提供了sentinel集群管理工具,能够实现主从服务监控,故障自动转移,这一切,对于客户端都是透明的,无需程序改动,也无需人工介入 | 需要二次开发 |
存储value容量 | 最大512M | 最大1M |
内存分配 | 临时申请空间,可能导致碎片 | 预分配内存池的方式管理内存,能够省去内存分配时间 |
虚拟内存使用 | 有自己的VM机制,理论上能够存储比物理内存更多的数据,当数据超量时,会引发swap,把冷数据刷到磁盘上 | 所有的数据存储在物理内存里 |
网络模型 | 非阻塞IO复用模型,提供一些非KV存储之外的排序,聚合功能,在执行这些功能时,复杂的CPU计算,会阻塞整个IO调度 | 非阻塞IO复用模型 |
水平扩展的支持 | 暂无 | 暂无 |
多线程 | Redis是单线程 | Memcached支持多线程,CPU利用方面Memcache优于Redis |
过期策略 | 有专门线程,清除缓存数据 | 懒淘汰机制:每次往缓存放入数据的时候,都会存一个时间,在读取的时候要和设置的时间做TTL比较来判断是否过期 |
单机QPS | 约10W | 约60W |
源代码可读性 | 代码清爽简洁 | 考虑了太多的扩展性,多系统的兼容性,代码不清爽 |
适用场景 | 复杂数据结构、有持久化、高可用需求、value存储内容较大 | 纯KV,数据量非常大,并发量非常大的业务 |
- 查看内存使用情况
info memory
Redis Sentinel选举
主观下线(subjectively down):Sentinel 会定时心跳检查,如果超时未回复,Sentinel会标记该节点为 S_DOWN
客观下线(objectively down):多个Sentinel认为master挂了,就会标记为 O_DOWN,开启-
failover过程
先有一台标记为 主观下线,然后这台Sentinel会发消息和其他Sentinel确认master是否失效了,如果回复超过法定人数(quorum)就会开启选举.过程如下图:
image.png
is-master-down-by-addr命令,格式如下IS-MASTER-DOWN-BY-ADDR <master_ip> <master_port> <sentinel:current-epoch> <sentinel:runid>
-
开始选举Leader
首先发现master节点的节点会在等待failover_start_time
后开始向其他节点发送is-master-down-by-addr
命令,主要关注<sentinel:current-epoch> <sentinel:runid>
,并给自己投一票;其他节点根据 current-epoch 和自己的 current-epoch 比较,比自己大就选对方,最后会选出一个Leader -
开始选举新的master
- 过滤掉一些节点.选出备选节点
-
选出master
过程如图:
Redis选举.png
缓存设计原则
原则 |
---|
只应将热数据放到缓存中 |
缓存过期时间应尽量分散,以避免集中过期 |
缓存key应具备可读性 |
避免不同业务出现同名key |
可对key进行适当的缩写以节省内存空间 |
选择合适的数据结构 |
避免使用耗时长的命令,如keys* |
一个key对应的数据不应过大 |
避免缓存穿透 |
进行适当的缓存预热 |
数据淘汰策略
淘汰策略 | 说明 |
---|---|
volatile-lru | remove the key with an expire set using an LRU algorithm 从已设置过期时间的key中挑选最少使用的数据淘汰 |
allkeys-lru | remove any key accordingly to the LRU algorithm 从所有的数据集中挑选最近最少使用的数据淘汰 |
volatile-random | remove a random key with an expire set 从已设置过期时间的数据集中任意挑选数据淘汰 |
allkeys->random | remove a random key, any key 在整个数据集中随机挑选key淘汰 |
volatile-ttl | remove the key with the nearest expire time (minor TTL) 从已设置过期时间的数据集中挑选将要过期的数据淘汰 |
noeviction | don't expire at all, just return an error on write operations 禁止淘汰数据,如果写满了直接报错 |