这篇文章主要是从几个方面来对redis做了一个小总结,整理的话,因为是偏向面试,所以侧重理论的内容会比较多一点
使用Redis会有哪些缺点
1、缓存和数据库双写一致性问题
一致性问题时分布式架构当中比较常见的问题,还可以进一步分为强一致性和最终一致性。对于数据库和缓存分别进行操作,势必会造成缓存和数据库数据不一致问题。如果有个强一致性的要求,那么就需要把数据先保存至数据库,再删除缓存,如果删除缓存失败,我们提供一个解决策略,比如消息队列。
2、缓存雪崩问题
缓存雪崩是指在某一时刻大量的数据同时失效,而这时候又有大量请求进来,直接去查询数据库,导致数据库连接异常
解决办法:
a)给缓存的失效时间设置成一个随机值,避免集体失效
b)使用互斥锁,对于失效的数据,在查询数据库之前先尝试获取到锁,然后再操作数据库,这个办法降低了效率。
3、缓存穿透问题
缓存穿透就是大量的访问缓存当中不存在的数据,导致所有的请求都直接压在了数据库上,对数据库造成了一定的压力。
解决办法:
a)利用互斥锁,缓存失效的时候,先去请求锁,如果获取锁成功,再请求至数据库。如果没有得到锁,先休眠一段时间,再尝试去获取锁。
b)使用布隆过滤器,其内部维护所有有效的key, 当一个不存在的key请求时,就会将其过滤掉,从而不会访问到数据库。
4、缓存的并发竞争问题
如果在同一时刻多个子系统对同一个缓存当中的key就行修改,需要如何处理,有些给出的方案是采用redis的事务,但是不是特别合适,如果是分布式的缓存,那么采用事务显然不能解决问题的。
给出以下两种解决方案a: 采用分布式锁,谁抢到谁修改 b:增加一个时间戳,这样每个线程在抢到锁之后,判断当前自己的时间戳是否小于数据库当中的时间戳,如果小于不更新,大于再更新,这样保证了操作的顺序性。
单线程的redis为什么这么快
1、操作内存
2、单线程操作,避免了上下文切换
3、采用非阻塞的IO多路复用
简单介绍下redis的数据类型,及每种数据类型的使用场景
1、String类型:最常规的数据类型,可以进行get、set操作,value也可以是数字。
2、hash类型:类似于数据库当中的HashMap,在进行单点登录时,可以将用户信息保存在hash当中,设置失效时间,可以模拟出session的效果
3、list类型:可以做简单的消息队列,可以使用lrange命令,做基于redis的分页功能,效果比较好。
4、set类型:存储一堆不重复的数据,可以做全局去重,为什么不使用java当中的set,因为我们开发场景采用的是分布式集群,此外还可以使用set的取交集,并集,差集等功能。
5、sorted set类型:增加了一个score权重,用来进行排序,可以进行排行榜操作,topN,同时也可以用来做延时任务,还有范围查找。
Redis的过期策略及内存淘汰机制
redis采用的是定期删除+惰性删除策略,
为什么不采用定时删除策略
如果用一个定时器来监控key, 如果过期就删除,是十分耗费CPU资源的,在大并发资源下,CPU需要将时间应用在处理请求,而不是删除key,因此没有采用。
定期删除加惰性删除是如何工作的
定期删除,是每100ms检查,是否有过期的key,如果有就删除。并不是每100ms就检查一次所有的key,而是随机检查。因此,如果只采用定期删除,就会导致很多key过期了没有被删除。
这个时候就需要用到惰性删除,也就是在每次获取key的时候,redis会检查下,这个key是否过期,如果过期就删除。
采用定期删除和惰性删除也是有一定问题的,如果定期删除没有将key删除,同时一段时间内又没有请求key,这样这个key就会一直存在内存中,内存就会越来越高。这个时候就要采用内存淘汰机制。
在redis当中的redis.conf中增加配置
# maxmemory-policy volatile-lru
1)noeviction : 内存不足时,新加入的操作会报错。不建议使用
2)allkeys-lru: 内存不足时,移除最近最少使用的key, 推荐使用
3)volatile-lru: 当内存不足时,在设置了过期的键当中,选择最近最少使用的key, 这种一般是把redis即当缓存又当数据库时采用,不推荐
4)volatile-random : 内存不足时,在设置的过期键当中,随机选择一个删除。不推荐
5)volatile-ttl: 内存不足时,在设置了过期时间的键当中,选择最早更新的key进行移除。不推荐
如果没有设置expire的key,那么volatile-lru, volatile-ttl 和volatile-random基本和noevication一致。