一、Radis是什么?
Redis是一款开源的、基于内存、单线程的高速缓存数据库。使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。我们在项目中经常使用redis作为缓存数据库使用,因为他基于内存所以处理速度非常快,又提供了完善的持久化机制。
数据库个数:16
是否单线程:是 (6.0后支持多线程)
默认端口号:6379
二、Radis有哪些优点和缺点?
优点
读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
支持数据持久化,支持AOF和RDB两种持久化方式。
支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
·
缺点
数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
三、Redis支持的数据类型有哪些?
- 字符串
- hash
- list
- set
- Zset
四、redis持久化的方式,怎么保证数据同步
redis是内存数据库,它把数据存储在内存中,这样在加快读取速度的同时也对数据安全性产生了新的问题,即当redis所在服务器发生宕机后,redis数据库里的所有数据将会全部丢失。为了解决这个问题,redis提供了持久化功能——RDB和AOF(Append Only File)。
RDB(Redis DataBase)是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis重启会通过加载dump.rdb文件来恢复数据。Redis会复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,来进行持久化。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
AOF(Append Only File),Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
五、如何保证数据库和redis数据的一致性
(1)查询
查询的时候,我们要先从redis里面查询,第一次查询肯定没有数据,这个时候我们从数据库查询,查询出来以后,将查询到的数据保存在redis里面,然后再将结果返回给前台。
(2)修改
修改数据的时候,我们一般会先修改数据库的数据,然后再修改redis的数据,这样会存在一个问题:如果数据库修改成功了,但是redis修改失败,这样用户再进行查询的时候,查询到的只能是修改之前的旧数据,而数据库存储的是新数据,这样就无法保证数据库和redis数据的一致性。
解决方案:我们可以在修改的时候,先删除redis里面对应的数据,删除成功后再进行数据库的修改操作。这样当用户查询该数据的时候,因为redis的数据已经删除,查询不到数据,所以会从数据库查询结果,查询到的数据也是修改后的最新数据,然后将这个最新数据再保存到redis当中。这样,以后用户查询该数据时,就会从redis当中获取最新的数据。
(3)删除
删除的时候也存在一致性的问题。如果是先删除数据库数据,再删除redis数据,如果redis数据删除失败,用户依然可以查到数据。这时候,我们也需要调整一下删除顺序,先删除redis中的数据,成功后再删除数据库数据。
(4)新增
增加的时候,要先增加数据库的数据,成功后再增加redis数据。如果先增加redis数据,再增加数据库数据的话,如果数据库数据增加失败,那么用户依然可以从redis里面查询到该数据,redis中存在,而数据库当中不存在,这就造成了数据一致性的问题。
六、redis缓存穿透,缓存击穿,缓存雪崩产生的原因和解决方案
(1)缓存穿透
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决方案:如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
(2)缓存击穿
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置。
(3)缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
解决方案:我们可以将缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
七、Redis为什么是单线程的?
- 代码更清晰,处理逻辑更简单;
- 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
- 不存在多线程切换而消耗CPU;
- 无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善;
八、Redis支持的 java 客户端都有哪些?
Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson。
九、Redis分布式锁有什么缺陷?
Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。
Redis容易产生的几个问题:
- 锁未被释放
- B锁被A锁释放了
- 数据库事务超时
- 锁过期了,业务还没执行完
- Redis主从复制的问题
十、Redis如何做内存优化?
缩短键值的长度
缩短值的长度才是关键,如果值是一个大的业务对象,可以将对象序列化成二进制数组;
首先应该在业务上进行精简,去掉不必要的属性,避免存储一些没用的数据;
其次是序列化的工具选择上,应该选择更高效的序列化工具来降低字节数组大小;
以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等
2、共享对象池
对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
3、字符串优化
4、编码优化
5、控制key的数量
十一、redis事务
redis对事务的支持是指可以一次执行多个命令,本质是一组命令的集合。一个事务中的
所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
涉及的命令:
multi : 开启一个事务
exec: 执行一个事务
discard:取消事务,如果被watch监控 取消watch
watch: 监控一个key 或 多个key是否发送变化
unwatch: 取消监控
在项目中,我们经常配合使用watch + multi + exec + discard 达到一个乐观锁的目的处理页面,
比如在秒杀项目中我们的预减库存操作就是利用这个方式,用watch监控一个商品库存,开启事务
尝试修改库存,如果在我们修改期间我们的这条数据被其它人修改过,那么这个事务就会提交不成功,
达到一个乐观锁的目的。 redis处理事务的机制稍弱,需要我们在代码中多加控制。
相关录音