Redis 数据库
Redis是一个基于 内存 的 key-value结构 数据库。
一、Redis基础
Redis是一个开源的内存中的键值对(key-value)数据结构存储系统,可以用作 数据库、缓存和消息中间件。Redis属于NoSql数据库。
1、Redis应用场景
①缓存;②任务队列;③消息队列;④分布式锁。
①字符串String;②哈希Hash;③列表List(常用于任务队列);④集合Set;⑤有序集合sorted Set。
Redis中所说的数据类型主要是针对值value数据类型的,键key的类型都是字符串类型。
具体参见链接 Redis中文网
4、Redis为什么速度快?
①Redis 基于内存,内存的访问速度是磁盘的上千倍;
②Redis基于Reactor模式设计开发了一套高效的事件处理模型,主要是 单线程 事件循环和 IO多路复用(非阻塞);
③Redis 内置了多种优化过后的数据结构实现 ,性能非常高。
Linux系统有三种方式实现IO多路复用的方式:select、poll和epoll。
共同点:
①都是 基于内存 的数据库,一般都用来当 做缓存使用。
②都有 过期策略。
③两者的性能都非常高。
区别:
①Redis支持更 丰富的数据类型(支持更复杂的应用场景)。Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储;Memcached 只支持最简单的 k/v 数据类型。
②Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memcached把数据全部存在内存之中。
③Redis有 灾难恢复机制。因为可以把缓存中的数据持久化到磁盘上。
④Redis在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached在服务器内存使用完之后,就会直接报异常。
⑤Memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是Redis目前是原生支持cluster模式的。
⑥Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路IO复用模型。(Redis 6.0引入了多线程IO)
⑦Redis支持发布订阅模型、Lua 脚本、事务等功能,而Memcached不支持。并且,Redis支持更多的编程语言。
⑧Memcached过期数据的删除策略只用了惰性删除,而Redis同时使用了惰性删除与定期删除。
6、Redis和MySQL的区别?
- MySQL是关系型数据库;而Redis是非关系型数据库。
- MySQL用于持久化存储数据到 硬盘,功能强大,但是速度缓慢;而Redis用于存储使用较为频繁的数据到 缓存 中,读取速度快。
- MySQL的数据存放在磁盘中;而Redis的数据存放在内存中。
- MySQL常用数据类型有数值、日期/时间、字符串;Redis常用数据类型有字符串、Hash、List、Set、Zset。
MySQL适合应用在web网站系统、数据库仓库、日志记录等。
Redis适合在会话缓存、发布/订阅模型等。
二、高可用
Redis保证高可用主要有三种方式:主从、哨兵、集群。
1、主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为 主节点(master),后者称为 从节点(slave)。且数据的复制是 单向 的,只能由主节点到从节点。Redis主从复制支持 主从同步 和 从从同步 两种,后者是Redis后续版本新增的功能,以减轻主节点的同步负担。
Q:主从复制主要的作用?
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复(实际上是一种服务的冗余)。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
2、Redis主从复制原理
- 保存主节点信息,包括主节点的ip和port。
- 主从建立连接,从节点发现新的主节点后,会尝试和主节点建立网络连接。
- 发送ping命令,看 主从端是否连接成功 。连接建立成功后从节点发送ping请求进行首次通信,主要是检测主从之间网络套接字是否可用、主节点当前是否可接受处理命令。
- 权限验证。如果主节点要求密码验证,从节点必须正确的密码才能通过验证。
- 同步数据。主从复制连接正常通信后,主节点会把持有的数据全部发送给从节点。
- 命令持续复制。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。
三、其他面试题
1、Redis的过期键的删除策略
Redis是 key-value数据库 ,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
- 定时过期: 设定Redis中缓存的key的过期时间。
- 惰性过期: 只有 当访问一个key时,才会判断该key是否已过期(访问时才判断) ,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期: 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key (隔一段时间扫描判断是否过期)。 该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
Redis中同时使用了惰性过期和定期过期两种过期策略。
2、Redis线程模型,单线程为什么快?
Redis基于Reactor模式(基于响应模式)开发了网络事件处理器,这个处理器叫做 文件事件处理器file event handler 。这个文件事件处理器,它是单线程的,所以Redis 才叫做单线程的模型,它 采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。 可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。
文件事件处理器的结构包含 4个部分: 多个Socket、IO多路复用程序、文件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)。
多个Socket可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个Socket,会将Socket放入一个队列中排队,每次从队列中取出一个Socket给事件分派器,事件分派器把Socket给对应的事件处理器。
然后一个Socket的事件处理完之后,IO多路复用程序才会将队列中的下一个Socket给事件分派器。文件事件分派器会根据每个Socket当前产生的事件,来选择对应的事件处理器来处理。
单线程快的原因:
- 纯内存操作
- 核心是基于非阻塞的IO多路复用机制
- 单线程反而避免了多线程的频繁上下文切换带来的性能问题
3、缓存雪崩、缓存穿透、缓存击穿
缓存雪崩 是指 缓存同一时间大面积的失效或缓存重启,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。
- 缓存预热
- 互斥锁
缓存穿透 是指 缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-nul;缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
- 采用布降过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap栏截植.从而避僻免了对底层存储系统的杳询压力。
缓存击穿 是指 缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
和缓存雪崩不同的是, 缓存击穿指并发查同—条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
4、简述Redis事务机制
事务四大特性: (ACID)原子性、一致性、持久性、隔离性
1、事务开始
MULTI命令的执行,标识着一个事务的开始。MULT命令 会 将客户端状态的flags属性中 打开REDIS_MULTT标识来完成的。在MULTI命令之前,可以执行watch命令。
2、命令入队
当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。
如果客户端发送的命令为 MULTI、EXEC(执行这些命令)、WATCH、DISCARD 中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回QUEUED回复。
- 如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。
- 如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的 flags属性关闭REDIS_MULTI标识,并且返回错误信息给客户端。如果正确,将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复。事务队列是按照FIFO(先进先出方式)的方式保存入队的命令。
3、事务执行
客户端发送EXEC命令,服务器执行EXEC命令逻辑。
- 如果客户端状态的flags属性不包含 REDIS MULTI标识 ,或者包含REDIS DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么就直接取消事务的执行。
- 否则客户端处于事务状态(flags有REDIS_MULTI标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
Redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。
Redis事务不支持检查那些程序员自己逻辑错误。例如对String类型的数据库键执行对HashMap类型的操作! - WATCH命令 是一个乐观锁,可以为Redis事务提供check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
- MULT命令 用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
- EXEC: 执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值nil 。
- 通过 调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。
- UNWATCH命令 可以取消watch对所有key的监控。
5、Redis集群方案
主从
1、哨兵模式:
sentinel,哨兵是redis集群中非常重要的一个组件,主要有以下功能:
- 集群监控: 负责 监控 redis master和slave进程 是否正常工作。
- 消息通知: 如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
- 故障转移: 如果master node挂掉了,会自动转移到slave node 上。
- 配置中心: 如果故障转移发生了,通知client客户端新的master地址。
哨兵用于实现redis集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
哨兵模式特点:
- 故障转移时,判断一个master node是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举。
- 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的。
- 哨兵通常需要3个实例,来保证自己的健壮性。
- 哨兵+redis 主从的部署架构,是不保证数据零丢失的,只能保证redis集群的高可用性。
- 对于哨兵+redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
2、Redis Cluster:: 是一种服务端Sharding技术,3.0版本开始正式提供。采用slot(槽)的概念,—共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。
方案说明
- 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384个槽位
- 每份数据分片会存储在多个互为主从的多节点上
- 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
- 同一分片多个节点间的数据不保持强一致性
- 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
- 扩容时需要需要把旧节点的数据迁移一部分到新节点
在redis cluster架构下,每个redis,要放开两个端口号,比如一个是6379,另外一个就是加1w的端口号,比如16379。16379端口号是用来进行节点间通信的,也就是cluster bus的通信,用来进行故障检测、配置更新、故障转移授权。clusterbus用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
优点
- 无中心架构,支持动态扩容,对业务透明
- 具备Sentinel的监控和自动Failover(故障转移)能力
- 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- 高性能,客户端直连redis服务,免去了proxy代理的损耗
缺点
- 运维也很复杂,数据迁移需要人工干预
- 只能使用O号数据库
- 不支持批量操作(pipeline管道操作)
- 分布式逻辑和存储模块耦合等
3、Redis Sharding: 是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。Java redis客户端驱动jedis,支持Redis Sharding功能,即ShardedJedis以及结合缓存池的ShardedJedisPool。
优点
- 优势在于非常简单,服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强。
缺点
- 由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。
- 客户端sharding不支持动态增删节点。 服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接不能共享 ,当应用规模增大时,资源浪费制约优化。
6、Redis主从复制的核心原理
master节点: 即可以读,也可以写。
slave节点: 只可以读,因此从节点的数据需要从主节点来,从主节点拷贝到从节点。
通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
全量复制:
- 主节点通过 bgsave命令fork子进程 进行RDB(全量数据的快照版本)持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的,最后会 生成一个dowp.rgb文件 ;
- 主节点通过网络 将RDB文件发送给从节点 ,对主从节点的带宽都会带来很大的消耗;
- 从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof,也会带来额外的消耗。
部分复制(增量复制):
- 复制偏移量: 执行复制的双方,主从节点,分别会 维护一个复制偏移量offset(如果主从节点复制的偏移量不一致,那么以主节点为准)。
- 复制积压缓冲区: 主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
- 服务器运行ID(runid): 每个Redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度:
- 如果从节点保存的runid与主节点现在的runid 相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底
能不能部分复制还要看offset和复制积压缓冲区的情况); - 如果从节点保存的runid与主节点现在的runid 不同,说明从节点在断线前同步的Redis节点并不是当前在主节点,只能
进行全量复制。
过程原理: