缓存设计
1.缓存穿透 > 查询一个根本不存在的数据,缓存层没有,每次都要到存储层区查询,失去了缓存保护后端存储的意义
- 解决方案: 缓存空对象并设置超时时间
2. 缓存失效>一大批缓存同一时间失效导致大量请求穿透缓存直达数据库,造成数据库压力过大甚至挂掉
- 解决方案: 将一次缓存过期时间设置为同一时间内不同时间,加个几秒的随机数
3. 缓存雪崩> redis服务宕机或超大并发导致缓存层无法支撑,全部打到存储层导致存储层调用过高存储层也宕机了
- 使用高可用架构 如哨兵或集群
- 使用后端限流如Hysytix组件
- 提前做好预案
4. 热点key重建优化>热点key失效的瞬间有大量的请求区访问后端重建缓存,导致后端负载过大
- 使用互斥锁 只允许一个线程重建缓存,其他线程等待从缓存中取值
开发规范与性能优化
- key名设计
- 可读性与可管理性,以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
- 简洁性: 保证语义的前提下,控制key的长度
- 不要包含特殊字符 如 空格、换行、单双引号
- value设计
- 拒绝bigkey
- String一般超过10KB就是bigkey其他数据结构元素不要超过5000个
- 非字符串的bigkey,不要使用del删除应使用hscan等渐进式删除
- bigkey过期删除应配置异步删除(lazyfree-lazyexpire yes)
- 网络拥塞 : 客户端每秒1000,一个bigkey为1m 对于一个千兆网肯定不行,对于单机多实例一个bigkey对其他实例也会影响
- 优化bigkey
- 拆分
- 如果无法拆分取数据时应使用hmget 避免使用hgetall
- 选择适合的数据类型
- 控制key的生命周期
- 命令使用
- 关注要取出数据的量 有些情况下可以使用hscan,scan等避免使用hgetall
- 禁用命令,禁止使用keys flushall, flushdb
- redis多数据库较弱,多业务使用多数据库还是单线程的
- 使用批量命令提升效率如mget,mset ,pipeline管道
- redis事务建议使用lua脚本
客户端使用
- 避免多个应用使用同一redis
- 连接池的配置
- maxTotal 连接池最大数量
-
- 要求高并发 1个命令1ms 要求5000QPS 理论上50 实际可以翻倍,无法保证连接释放就可以用
- redis的应用数量*maxTotal应小于redis配置文件中maxClients
-
- maxIdle与minIdle 最大存活数与最小存活数 一般maxIdle会是QPS理论值
- 当连接池中的连接大于minIdle小于maxIdle,连接池会维持minIdle
- 当连接池连接数量大于maxIdle 连接池会维护maxIdle
- 连接预热 对于预知高并发,可以使用jedis.ping模拟连接然后释放让连接池保持在minIdle
- 高并发下建议客户端添加熔断功能(hystrix)
- 设置合理的密码
- redis对于过期键的清除策略
- 被动删除,当读写一个过期的key时,redis会删除这个过期的key
- 主动删除,redis会定期删除一批过期的key
- 当已用内存超过设置的内存值时会触发主动清理策略
- volatile-lru 根据最近使用情况删除过期的key,直到腾出空间
- allkeys-lru:根据最近使用情况删除所有的key,直到腾出空间
- allkeys-random:随机删除所有键,直到腾出空间
- volatile-random: 随机删除所有过期的键,直到腾出空间
- volatile-ttl 根据key过期时间删除,若没有使用noeviction策略
- noeviction 返回oom
- maxTotal 连接池最大数量
布隆过滤器
简单理解 把key根据多个hash算法进行计算然后取模落到位数组上数组上元素值为1 取值时进行相同操作若计算后的几个落点至少有1个位1 就可能有此元素,若为0则肯定没有
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
//创建过滤器
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 1000000, 0.001);
//放值
bloomFilter.put(UUID.randomUUID().toString().replace("-", ""));
//取值
boolean contain = bloomFilter.mightContain("jamin" + i)