缓存+数据库读写的模式
- 读数据:先查缓存,不过不存在,查数据库,并更新缓存。
- 更新数据:先更新数据库,再删除缓存。
1 . 直接删除就行,不要更新缓存。缓存的数据可能是数据库取出后需要进行业务整合的,应该在查数据的时候再做此操作,提高更新性能。
还有可能此数据更新比较频繁,更新了10次,却只查了2次。那么如果你每次更新数据的时候都重新更新缓存,则非常浪费资源。hibernate的lazy加载有点类似,只要需要的时候才去加载。
2 . 顺序不应颠倒。在高并发下可能会发生缓存了旧数据的情况,导致数据不一致。 一个更新操作过来后,如果先删除缓存,在执行更新数据库逻辑时,另一个线程来查询,发现缓存没数据,去数据库取出了还未更新的旧数据,然后放置缓存,就发生了数据不一致问题。
大多情况下缓存key是以ID为基础设置的,那么黑客故意删除他的一条数据,并记住这个ID,或者使用任意方式查询一个不存在的ID,比如,自增ID,使用负数取查询等等。那么他使用这个ID去查数据,缓存为空,导致查数据库,还是空,缓存不更新,那么他每次请求都能穿透缓存进入数据库。导致所有流量全部打入数据库,导致数据库重压甚至奔溃。
当然整体的系统设计还会加上API网关进行限流,熔断,黑名单,参数合法验证等保护措施,不一致压垮,但必定还是会受到影响。
解决方案:
- BloomFilter,BitMap
- 对查询出来为空的数据也设置缓存。设置一个常量,标志是空数据,并设置过期时间,在未过期之前,不用担心被穿透的问题。这种方法非常简单,高效,但存在一棒子打死全部的问题,缓存的过期时间设置也是非常难判断,时间短导致穿透次数的增加,数据库压力的增加;时间长,可能会影响正常数据的查询。所以还是推荐上面的那种解决方案。
- 基本数据类型:字符串String、字典Hash、列表List、集合Set、有序集合SortedSet
- HyperLogLog、Geo、Pub/Sub
- Redis Module : BloomFilter, RedisSearch, Redis-ML, BitMap
当 Redis 用于缓存的内存不足时,需要怎么处理。就是根据淘汰策略决定的
- noeviction:新写入操作会报错。
- allkeys-lru:在键空间中,移除最近最少使用 (less recently used ,LRU) 的key。
- allkeys-random:在键空间中,随机移除某个key。
- allkeys-lfu : LFU (Least Frequently Used)最不常用的
- volatile-lfu :在设置了过期时间的键空间中,移除最不常用的
- volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:在设置了过期时间的键空间中,有更早过期时间(time to live,TTL) 的key优先移除。
默认是抛出异常 redis.cnf
maxmemory-policy noeviction
- RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存。(默认机制)
- AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里。
- 混合模式:since Redis4.0
- 分布式锁