redis缓存设计-Redis(七)

上篇文章介绍了redisCluster。

redis集群-Redis(六)https://blog.csdn.net/ke1ying/article/details/131217674

  • 高并发缓存应对策略

缓存穿透

正常情况下,用户访问某条数据,第一次从数据库获取,后面会set进缓存,从缓存获取。缓存穿透指的是数据库没有这个值,当大量请求时,会经过缓存在数据库不断查询,数据库负担不断增加。这种情况下可能是业务代码异常,也可能是黑客利用不存在的key不断攻击数据库(所以redis不光可以提高性能,还可以利用减轻数据库压力防止黑客攻击)。那这种情况如何解决呢?

可以增加业务代码逻辑,当这个值不存在,则set空值到缓存,给这个空值一个过期时间。

还有一种方式使用bigMap布隆过滤器,在业务代码上先对数据进行一次过滤器过滤,对于不存在的数据,布隆过滤器可以先过滤掉。

缓存击穿

当系统中redis的key大量一起过期,导致同一时间高并发一起请求这些key,全部打到数据库服务器上,这时候导致数据库宕机怎么解决呢?

设置redis过期随机过期时间,并且提供一个过期时间基数。

缓存雪崩

整个redis服务器直接宕机导致不可用,这时候 必须通过我们前面说的集群保证redis高可用,以及预估高峰流量,做限流预案,用队列削峰,服务降级。比如用hystrix服务降级组件,kafka队列等。

  • 热点缓存key重建

当key是一个热点,比如双11某部手机大降价,高并发同时都访问这个key,于是全部一起访问到数据库,这时候怎么解决呢,用redission分布式锁来保证只有一个请求访问到数据库,其他的从缓存冲获取。(顺便一提redission分布式锁源码里通过lua脚本访问redis,保证事务和原子性)

  • 数据库缓存双写不一致设计

线程1:set数据库10,删除缓存

线程2:set数据库6,删除缓存

线程3:get数据库10,set缓存10

当线程3和线程2并行执行:

第一步:线程3获取10,线程2set数据库6,删除缓存

第二步:线程3set缓存10

这时候实际数据库存入的是6,但是缓存存入的是10。

导致了数据库和缓存不一致。这时候如何解决呢?

  1. 这种情况下肯定高并发导致的,如果并发量很小,或者是个人的数据压根不用考虑这种问题。
  2. 就算并发量很高,但是对数据库一致性要去不高,比如商品名称,设置一个过期时间,等读的时候更新就好。
  3. 如果不能容忍不一致,则使用分布式锁,保证排队,让并发变为串行。分布式读写锁,相当于读读或者读写的时候按顺序排队,读读的时候不需要排队。
  4. 引入阿里新的开源中间件canal来监听binlog日志来修改数据。

(延迟双删也是一种解决策略,在删除缓存的时候,sleep 50ms再删一次。但这种情况不推荐,这种小概率事件而让所有请求都停顿)

针对读多写少的情况,加入缓存提高性能,针对写多读少并且不能容忍不一致性的情况,就没必要使用缓存,可以直接操作数据库,没必要为了提高性能,而增加很多复杂的设计。

  • 键值设计
  1. key设计

1)以业务名(或者数据库名)为前缀,防止key冲突,冒号分割。

(微服务的情况下,加上服务名称)

  1. 统一在一个文件下管理,防止重复定义。
  2. 简洁性:在加前缀的情况下必须保证简介,牺牲可读性也是可以的。
  3. 特殊符号不可以:换行空格单双引号不可以出现。

  1. Value设计

Bigkey针对value的,绝对不能放大key,前面强调过很多次。在redis一个字符串最大512mb,哈希,set,zest,list可以存储大约40亿元素。实际情况下我们一般认为:

  1. 字符串:超过10kb就是bigkey。
  2. 非字符串:则以个数为单位,一般不要超过5000。
  3. 非字符串不要使用del进行删除,使用hscan,sscan,zscan方式渐进删除,同时要注意bigkey过期时间,因为过期会触发del,造成阻塞。

  1. bigkey危害

1)redis阻塞。

2)网络阻塞:Bigkey意味着产生网络流量较大,假设一个bigkey是1mb,客户端每秒访问1000,那么每秒产生1000mb流量,对于普通千兆网络服务器(按字节每秒128mb/s)直接宕机。

(千兆网卡为什么是128mb/s,因为需要除以8,按字节计算)

3)过期删除:在redis4.0版本设置lazyfree-lazy-expir yes,改为异步删除,如果没有配置,则bigkey过期会造成阻塞。

  1. bigkey产生

正常都是程序设计不当,

  1. 社交类,大v粉丝太多,比是bigkey。
  2. 统计类,按天存储某项功能用户集合,用户多也是bigkey。
  3. 缓存,当我们从数据库查询数据,序列化放入缓存是否放入很多不想干的数据。

如何优化呢?

可以拆分,通过哈希取模拆分更小模块,进行二次存储。

如果实在不可以避免,必须要bigkey,那么只可以在操作的时候不要hgetall,用hmget,删除的时候也要渐进式删除。

  1. 选择合适数据类型

Set user:1:name ky

Set user:1:age 20

可以改为哈希

Hmset user:1 name ky age 20

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JedisUtil是一个Java Redis缓存工具类,它封装了Jedis客户端的基本操作,使得使用Redis缓存更加简单方便。 以下是JedisUtil的示例代码: ``` import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisUtil { private static JedisPool jedisPool; static { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(1000); jedisPoolConfig.setMaxIdle(100); jedisPool = new JedisPool(jedisPoolConfig, "localhost", 6379); } public static void set(String key, String value) { try (Jedis jedis = jedisPool.getResource()) { jedis.set(key, value); } } public static String get(String key) { try (Jedis jedis = jedisPool.getResource()) { return jedis.get(key); } } public static void del(String key) { try (Jedis jedis = jedisPool.getResource()) { jedis.del(key); } } public static void expire(String key, int seconds) { try (Jedis jedis = jedisPool.getResource()) { jedis.expire(key, seconds); } } public static boolean exists(String key) { try (Jedis jedis = jedisPool.getResource()) { return jedis.exists(key); } } } ``` 在上面的代码中,我们使用了JedisPool来管理Jedis连接,它的作用是维护一定数量的Jedis连接,以便在需要时从池中获取连接,减少了创建和关闭连接的开销。 在使用JedisUtil时,我们只需要调用set、get、del、expire和exists等方法,就可以完成对Redis缓存的操作。 例如,要将一个键值对("name", "Tom")存入Redis中,可以使用以下代码: ``` JedisUtil.set("name", "Tom"); ``` 要获取键为"name"的值,可以使用以下代码: ``` String name = JedisUtil.get("name"); ``` 同时,JedisUtil还提供了删除、设置过期时间和判断键是否存在等方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后端从入门到精通

你的鼓励是我最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值