1. Redis是什么?
Remote Dictionary Server
被称为数据结构服务器。
完全开源、非关系型、高性能的key-value存储系统。
支持5中数据类型:存储string、list、set、zset和hash
2.Redis的特点
redis是单线程的,操作是安全的;
redis支持数据的持久化,可将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用;
redis支持key-value,并提供list、set、zset、hash等数据结构的存储;
redis支持数据的备份,即master-slave模式的数据备份;
redis单个value的最大限制1GB;
3.五种数据类型介绍及使用场景
应用场景:
会话缓存;
消息队列,比如支付;
活动排行榜或计数;
发布,订阅消息(消息通知);
商品列表,评论列表等;
1) String
key-value,可以包含任何数据,一个键最大能存储512MB。
比如图片或者序列化的对象。
set:设置存储在指定键中的值;
get:获取存储在指定键中的值;
del:删除存储在指定键中的值;
使用场景:
incr 自增 ;eg:生成id;
decr 减少;eg:库存;
计数器缓存;
缓存–过期时间设置,模拟session;
使用案例:
2) List
redis列表,按照插入顺序可以添加一个元素到列表的头部或尾部。
rpush:将给定值推入列表的右端;
lrange:获取列表在指定范围上的所有值;
lindex:获取列表在指定范围上的单个元素;
使用场景:
多任务调度队列;
利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好;
微博发表文章,使关注的人都能看到(使用列表做一个消息队列);
生产者消费者模型(多线程);
3)Set
string类型的无序集合。集合是通过哈希表实现的,增删查复杂度都是O(1)
sadd:将给定元素添加到集合;
smembers:返回集合包含的所有元素;
sismember:检查指定元素是否存在于集合中;
使用场景:
微博关注数。
利用交集、并集、差集等操作,可以计算共同爱好,全部的喜好,自己独有的喜好等功能。
4)hash
键值对集合。是string类型的field和value的映射表,适用于存储对象。
hset:散列里面关联起指定的键值对;
hget:获取指定散列键的值;
hgetall:散列包含的所有键值对
hdel:如果给定键存在于散列中,移出这个键;
使用场景:
购物车
单点登录,用这种数据结构存储用户信息,一cookie作为key,设置30分钟为缓存过期时间,
能很好地模拟类似session效果。
5)zset
集合,不允许重复的成员。每个元素都会关联一个double类型的分数。
redis通过分数来为集合中成员进行从小到大的排序。
zset的成员是唯一的,但分数却可以重复。
zadd:将一个带有给定分值的成员添加到有序集合里面;
zrange:根据元素在有序排列中所处的位置,从有序集合里面获取多个元素;
zrangebyscore:获取有序集合在给定分值范围内的所有元素;
zrem:如果指定成员存在于有序集合中,那么移出这个成员
使用场景
排行榜,取Top N操作;
延时任务
范围查找
3.持久化
两种方式:
1)rdb快照:
默认redis是会以快照的形式将数据持久化到磁盘(一个二进制文件,dump.rdb,这个文件名字可以指定),
在配置文件(redis.conf)中的格式是:save
N M表示在N秒之内,redis至少发生M次修改则redis抓快照到磁盘。
保存 900 1:900秒内如果超过1个key被修改,则启动快照保存;
保存300 10:300秒内如果超过10个key被修改,则启动快照保存;
保存60 10000:60秒内如果超过10000个重点被修改,则启动快照保存;
2)aof仅附加文件
使用aof持久时,服务将每个收到的写命令通过写函数追加到文件中(appendonly.aof)
aof持久化存储方式参数说明:
appendonly yes:开启aof持久化存储方式;
appendfsync always:收到写命令后就立即写入磁盘,效率最差,效果最好;
appendfsync everysec:每秒写入磁盘一次,效率与效果居中;
appendfsync no:完全依赖操作系统,效果最佳,效果没法保证;
4.redis性能、并发
性能:
在碰到需要执行耗时特别久,且结果不频繁变动的sql,特别适合将运行结果放入魂村,
这样后面的请求就去缓存中读取,使得请求能够迅速响应。
并发:
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。
这个时候,需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。
5.优点
1)性能极高。
redis能读的速度是110000次/s,写的速度是81000次/s;
2)丰富的数据类型。
redis支持二进制案例的strings、lists、hashes、sets及ordered sets数据类型操作;
3)原子。
redis的所有操作都是原子性的,意思是要么成功要么失败完全不执行。
单个操作都是原子性的,多个操作也支持事务,即原子性,通过Multi和Exec指令包起来。
4)丰富的特性。
redis支持publish、subscribe,通知,key过期等特性。
6.缺点
1)缓存和数据库双写一致性问题
2)缓存雪崩问题
3)缓存击穿问题
4)缓存并发竞争问题
7.单线程的redis为什么这么快?
redis是单线程工作模型
1)纯内存操作
2)单线程操作,避免了频繁的上下文切换
3)采用非阻塞的I/o多路复用机制
8.redis的过期策略及内存淘汰机制
eg:redis只能存5G数据,可是写了10G,那会删除5G的数据。怎么删除?
你的数据设置了过期时间,但是时间到了,内存占用率还是比较高,原因是什么?
--redis:采用的是定期删除+惰性删除策略。
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。
虽然内存即使释放,但是十分消耗CPU资源。
在大并发情况下,CPU要将时间应用在处理请求,而不是删除key,因此没有用这一策略。
定期删除+惰性删除是如何工作的?
定期删除,redis默认每个100ms检查,是否有过期的key,有则删除。
需要说明的是,redis不是每个100ms将所有key检查一次,而是随机抽取检查。
因此,如果只采用定期删除策略,会导致很多key到期时间没有删除。
所以,惰性删除是在获取某个key的时候,redis会检查一遍,这个key如果设置了过期时间,那么是否过期了?如果过期了,此时就会删除。
采用定期删除+惰性删除就没有其他问题了吗?
如果定期删除没有删除key,然后你也没及时请求key,也就是说惰性删除也没生效。
这样,redis的内存会越来越高,那么就应该采用内存淘汰机制。
9.redis和数据库双写一致性的问题
一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。
数据库和缓存双写,就必然会存在不一致的问题。
如果对数据有强一致性要求,不能放缓存。
我们所做的一切,只能保证最终一致性。另外,从根本来上说,只能说降低不一致发生的概率,无法完全避免。
因此,有强一致性的要求的数据,不能放缓存。
首先,采用正确更新策略,先更新数据库,再删除缓存,其次,因为可能存在删除缓存失败的问题,提供了一个补偿措施即可
如消息队列。
10.如何应对缓存穿透和缓存雪崩问题
缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
解决方案:
1)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试;
2)采用异步更新策略,无论key是否取到值,都直接返回。
value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需做环迅预热。
3)提供一个能迅速判断请求是否有效的拦截机制
缓存雪崩:即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
解决方案:
1)给缓存的失效时间,加上一个随机值,避免集体失效;
2)使用互斥锁,但是该方案吞吐量明显下降了;
3)双缓存。
11.如何解决redis的并发竞争key问题
即:同时有多个子系统去set一个key,需要注意什么?
1)如果对这个key操作,不要求顺序
这种情况,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
2)如果对这个key操作,要求顺序
假设有一个key1,系统A需要将key1设置为value1,系统B需要将key1设置为valueB,系统C将key1设置为valueC,
期望按照key1的value值按照valueA-->valueB-->valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个
时间戳,
系统A key1 {valueA 3:00}
系统B key1 {valueB 3:05}
系统C key1 {valueC 3:10}
那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05},接下来系统A抢到锁,发现自己的valueA的时间戳
早于缓存时间戳,那就不做set操作。
其他方法,比如利用队列,将set方法变成串行访问也可以。