济南市网站建设,网页ui设计的排版,苏州模板网站专业设计,运动网站开发的需求分析文章目录 Redis概述Redis基本数据类型Redis与MySQL的区别以及使用场景如何保持双写一致性#xff08;缓存一致性#xff09;1. 延迟双删2. 分布式锁#xff08;强一致性时使用#xff09;3. 中间件 Redis持久化机制RDB#xff08;redis database#xff09;AOF#xff0… 文章目录 Redis概述Redis基本数据类型Redis与MySQL的区别以及使用场景如何保持双写一致性缓存一致性1. 延迟双删2. 分布式锁强一致性时使用3. 中间件 Redis持久化机制RDBredis databaseAOFappend only file命令重复的解决办法 穿透、击穿、雪崩Redis事务key过期策略数据淘汰策略Redis分布式锁Redis集群的方案主要有几种主从复制主从集群哨兵模式Redis集群哨兵模式下的脑裂问题 分片集群 Redis是单线程的为什么还是那么快解释I/O多路 复用模型 Redis为什么使用跳表而不是用B树? Redis概述 Redis是一个基于C语言开发的开源数据库与传统数据库不同的是Redis的数据是存储在内存中的因此读写速度非常快广泛应用于缓存消息队列等方向。Redis 提供了多种数据类型来支持不同的业务场景比如– 除此之外Redis 还支持事务 、持久化、Lua 脚本、多种集群方案主从复制模式、哨兵模式、切片机群模式、内存淘汰机制、过期删除机制等等。 Redis基本数据类型 5种基本数据结构 String(字符串)List(列表)Set(集合)Hash(散列)Zset(有序集合) 3种特殊数据结构 HyperLogLogs基数统计、Bitmap 位存储、Geospatial (地理位置) Redis与MySQL的区别以及使用场景 Redis和MySQL的区别 MySQL属于关系型数据库使用表格存储数据Redis属于非关系型数据库使用键值对存储数据MySQL将数据持久化存储在硬盘上Redis数据存储在内存中因此具有非常高的读写性能MySQL作为关系型数据库拥有强大的SQL查询功能能够进行复杂关系查询MySQL提供了严格的事务支持能确保数据的一致性和完整性MySQL还支持索引用于提高查询性能Redis中支持多种数据结构每种数据结构都有相应的操作命令方便进行存储和操作Redis支持分布式架构可以通过分片来将数据分布在多个节点上提高系统的可扩展性和容错性Redis的发布/订阅功能可以用来实现消息队列用于在不同的应用程序或服务之间传递消息 Redis和MySQL两者的使用场景 Redis是一个内存数据结构存储常用于需要高速读写操作、缓存或作为消息代理的场景。主要用于实时分析、排行榜、发布/订阅消息系统、实时通信 MySQL是一个关系数据库管理系统适用于需要存储大量数据、复杂查询和事务处理的场景。主要用于电子商务网站、客户关系管理系统、内容管理系统 如何保持双写一致性缓存一致性
要回答这种问题首先是结合业务进行回答的有的业务的一致性要求比较高有些业务的一致性是允许延迟一致性的。双写一致性的概念就是修改数据库中的数据相应缓存中的数据也要被修改。缓存和数据库的数据要保持一致。
1. 延迟双删
redis缓存的一般流程就是读操作的一般流程就是上图写操作的话一般是使用延迟双删删除缓存—修改数据库–延时–删除缓存 一般设计两个问题1. 就是为什么先删除缓存在修改数据库反转是否可以2. 是为什么要删两次无论是先删除缓存还是先修改数据库都补课避免的会产生脏数据先删除缓存是为了降低脏数据的出现使用双删是防止脏数据比如两个线程线程a在删除缓存之后线程b进行读操作这时候线程b拿到的就是修改之前的数据这时候线程a进行修改数据库和删除缓存操作之后线程b将之前拿到的数据返回并存入redis此时就产生了脏数据。为什么要使用延时删除一般情况下DB是主从模式的是读写分离的使用延时是主节点要将数据同步到从节点极大控制了脏数据的风险但延时不容易控制也会有产生脏数据的风险。
2. 分布式锁强一致性时使用
在进行写操作的时候不允许其他线程只有当一个线程操作完成之后其他线程才能进行读写但是效率低。但是一般放入缓存中的数据都是读多写少加锁可以使用读写锁也就是共享锁和排他锁。 共享锁读锁readLock共享读操作。排他锁写锁writeLock阻塞其他线程进行读写操作。
3. 中间件
在实际业务开发中出现短暂的不一致是主流的可以使用异步通知保证数据的一致性。第一种是基于MQ的数据一致性 异步通知保证数据一致性最终的一致性保证取决于MQ的可靠性。 第二种是基于阿里的中间件Canal的异步通知基于MySQL主从同步来实现利用Canal这个中间件不需要修改业务代码只需要伪装一个MySQL的从节点Canal通过读取binglog数据来更新缓存。
Redis持久化机制
RDBredis database
Redis数据备份文件也就是数据快照就是将内存中的所有数据都记录在磁盘中当Redis故障重启后从磁盘中读取快照文件恢复数据。我们可以手动使用save或者bgsave来触发RDB机制也可以在redis.conf配置文件中去配置自动触发机制每隔多少秒触发一次bgsave。
执行原理bgsave开始时会fork主进程得到子进程子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。 但是redis主进程是没有办法直接操作物理内存的操作系统给每一个进程分配了一个虚拟内存主进程可以操作虚拟内存操作系统创建了一个页表用于维护虚拟地址和物理地址之间的映射关系主进程去操作虚拟内存虚拟内存基于页表关联到真正物理内存存储数据的地址这样就能实现对物理内存数据的读写操作内部主进程fork克隆一个子进程子进程将主进程的页表数据进行拷贝子进程拥有了和主进程相同的映射关系那么子进程在操作自己的虚拟内存时因为映射关系和主进程一样最终能读到相同的物理内存区域这样就实现了子进程和主进程内存空间的共享然后子进程将读到的数据写入到rdb文件中替换旧的rdb文件。 此时如果主进程对数据进行操作而子进程在读数据这时候可能会出现脏数据这个情况下fork的时候采用了一种copy-on-write的技术就是在fork的时候物理内存中的数据是read-only的就是只读模式用户进程只能读不能写此时如果主进程有写操作过来会先去拷贝一份数据在拷贝的数据中进行写操作主进程的读操作也是在拷贝的数据中。 RDB创建快照会阻塞线程吗 在Redis中创建了两个命令来生成RDB快照文件 save 同步保存操作会阻塞 Redis 主线程 bgsave fork 出一个子进程子进程执行不会阻塞 Redis 主线程默认选项 AOFappend only file AOF日志持久化它会将所有写操作追加到一个日志文件中以日志的形式来记录每个写操作这个日志文件可以用来重建数据库只许追加文件不许改写文件Redis启动之初就会读取该文件重新构建数据也就是Redis重启的话就是根据日志文件的内容将写指令从前往后执行一次以完成数据的恢复工作AOF持久化机制的优点是可靠性高缺点是文件比较大恢复速度比RDB慢。 命令重复的解决办法 穿透、击穿、雪崩 缓存穿透key对应的数据在Redis中并不存在每次的请求key从缓存获取不到请求就会传到数据库数据库也没有当请求量达到一定程度就回压垮数据库。 解决方法 1. 将这个空对象设置到缓存里面下次请求的话直接从缓存里面拿这种情况一般将空对象设置一个较短过期时间2. 对参数进行校验不合法参数进行拦截 缓存击穿某个key对应数据库中存在但是Redis缓存在某个时间节点过期此时有大量请求发送过来发现缓存过期就会从后端数据库加载到缓存这时候大量并发可能会将数据库压垮。 解决方法 1. 热点数据设置永不过期 2. 加锁当多个线程去查询数据库的这条数据时我们可以在第一条查询语句加互斥锁这样当其他线程拿不到锁就进行等待当第一个线程查询到数据时然后将数据返回到Reids缓存起来后面线程进来发现有缓存直接从缓存处拿取数据 缓存雪崩高并发情况下大量的缓存失效或者缓存层出现故障于是所有的请求都会到达数据库数据库的调用量暴增造成数据库宕机 解决办法 1. 随机设置key失效时间避免大量的key集体失效 2. 若是集群部署可将热点数据均分在不同的Redis库中可能避免key全部失效 3. 不设置过期时间 4. 跑定时任务在缓存失效前刷新新的缓存 总结雪崩就是大面积的key缓存失效穿透是Redis里不存在这个缓存key击穿是Redis某个热点key突然失效最终的受害者都是数据库对于Redis宕机请求数据全部去数据库这种情况我们可以有以下思路 事发前 实现Redis的高可用主从架构Sentinel哨兵尽量避免Redis挂掉这种情况 事发中 玩意Redis真的挂了我们可以设置本地缓存ehcache限流尽量避免数据库被压垮起码保证给服务正常运行 事发后 Redis持久化重启后从磁盘上加载数据快速恢复缓存数据 Redis事务 Redis 事务提供了一种将多个命令请求打包的功能。然后再按顺序执行打包的所有命令并且不会被中途打断。 Redis 事务在运行错误的情况下除了执行过程中出现错误的命令外其他命令都能正常执行。并且Redis 事务是不支持回滚roll back操作的。因此Redis 事务其实是不满足原子性的。 key过期策略 Redis中的key过期策略是用于设置key的生存时间并在时间到期后自动将过期的key删除。常见的有 定时删除当key的生存时间到期时Redis会自动删除该key。这种策略可以保证资源的及时释放但是在大量key同时过期时会阻塞服务器导致服务器暂时停止服务。 惰性/懒汉式删除只有当客户端访问一个已过期的key时Redis才会删除这个key。这种策略CPU开销小但是如果过期key不被访问那么这些key会一直存在于内存中占用内存空间。 定期删除Redis每隔一段时间会随机测试一些key删除其中已过期的key。这种策略是定时删除和惰性删除的折中方案通过调整删除操作执行的频率和时长可以在控制CPU开销和内存占用之间找到一个平衡。 数据淘汰策略 当Redis内存满了之后如果此时还有新的key要添加进Redis中这时候就会使用数据一种规则将内存中的数据删除掉这种数据的删除规则称为内存淘汰策略。 在Redis中支持8种不同的策略来选择要删除的key noeviction不淘汰任何key但是内存满的时候不允许写入新的数据默认是这种。volatile-ttl对设置了TTL的key比较key剩余的TTL的值越小的月线被淘汰。在所有数据中随机进行淘汰。在设置了TTL的数据中进行随机淘汰。对使用时间最晚的数据进行淘汰。在设置了TTL的数据中将使用时间最晚的数据进行淘汰。对使用频率最低的数据进行淘汰。在设置了TTL的数据中将使用频率最低的数据进行淘汰。 Redis分布式锁
首先使用本地锁是只能解决单个JVM中的线程安全问题的当使用反向代理进行负载均衡进行集群的情况下本地锁就不能解决线程安全问题这种情况下就要使用分布式锁。
Redis集群的方案主要有几种
主要有三种集群的方案主从复制主从集群、哨兵模式、分片集群。单节点的Redis并发能力是有限的需要进一步提高Redis的并发能力就需要搭建集群。
主从复制主从集群 要想提高单节点Redis的并发能力就要搭建主从集群实现读写分离主节点master负责写多个从节点slave负责操作但是当master节点写入数据时一般涉及到slave的数据的同步问题。主从同步原理 主从全量同步slave在执行replicaof命令建立连接向master发起数据同步请求master接收到请求之后会先判断是否第一次同步是第一次同步的话返回master的版本信息slave接收保存版本信息接着master执行bgsave生成RDB发送给slaveslave清空本地的RDB然后加载接收的RDBslave在接收RDB的过程中如果master有操作此时有一个repl_baklog日志文件记录的时RDB期间进行的所有命令之后将repl_baklog中的命令发送给slaveslave进行加载。在master判断的时候每一个master都有唯一的replidreplication Id用来判断从节点上的id是否一致。在repl_baklog记录同步时会有一个偏移量offset用来记录repl_baklog中的数据量当repl_baklog进行追加时offset的数据量也会增加一般用offset来判断master和salve中是否同步。主从增量同步主要是作用与slave重启或后期数据变化。首先当slave重启后会向master发送replid和offset然后master接收到之后通过replid判断是否第一次同步不是第一次同步就直接返回continue然后去repl_baklog获取offset后将数据发送给slave进行同步。
哨兵模式
主从同步有一个明显的缺点就是保证不了Redis的高可用比如主机节点宕机之后就丧失了redis的高可用。这个时候Redis提供了哨兵Sentinel机制来实现主从集群的自动故障修复。
哨兵其实也是由多个Redis节点组成的一个集群一般至少部署三台哨兵。哨兵一般实现三个功能监控、自动故障修复、通知。 监控Sentinel会不断检查master和slave是否正常工作。自动故障修复如果master故障会将一个slave提升为master当故障恢复后也以新的master为主。通知Sentinel充当Redis的服务发现来源当集群中发生故障转移时会将最新的信息推送给Redis的客户端客户端就会连接新的主节点极大的保证了Redis的高可用。 监控的原理实际就是每隔一秒向每个实例发送一个ping命令如果某个Sentinel发现某个实例未在规定的时间内响应就认为该实例主管下线。若超过指定数量quorum 最好超过哨兵数量的一半的Sentinel都认为该实例主观下线则该实例就是客观下线。如果判断这个实例客观下线了就要进行master的重新选主。重新选主的规则 如果一个从节点与主节点的断开时间超过了指定值则排除该节点。判断从节点的slave-priority值优先级越小优先级越高。如果优先级一样则判断offset值值越大优先级越高。最后判断slave节点运行id的大小越小优先级越高。
Redis集群哨兵模式下的脑裂问题
由于网络问题主节点master和哨兵都处于不同的分区哨兵只能去检测从节点这时候哨兵会在从节点中重新选主一个master这时候会出现两个主节点客户端的写入在原本的master这时候如果网络恢复原本的主节点会清除RDB重新加载新选主的master的RDB出现数据不一致问题。 解决办法在redis中又两个配置参数可以用来解决集群脑裂问题。min-replicas-to-write 1 表示最少的slave节点为1。min-replicas-max-log 5 表示数据复制和同步的延迟不能超过5秒。只有达到以上两个要求客户端才能成功向master中写入数据。
分片集群
主从和哨兵只能解决高可用高并发读的问题但是海量数据的存储和高并发写的问题并没有解决。
使用分片集群可以解决分片集群的特点主要有 一个分片集群中有多个master每个master保存不同的数据。每个master可以有多个slave节点。master之间通过ping检测彼此的健康状态。客户端请求可以访问集群任意节点最终都会被转发到正确节点。 在分片集群中的数据读写是在Redis分片集群中引入了hash槽Redis集群中有16348个hash槽每一个分片集群中会平均分配这个hash槽给master每一个key通过CRC16校验后对16348取模来决定放置与哪一个槽在读的时候也是如此先将key通过CRC16校验然后对16348取模来确定去哪一个槽中读取数据。
Redis是单线程的为什么还是那么快
首先Redis是基于内存进行操作的执行速度非常快其次Redis采用了单线程避免了不必要的上下文切换和竞争条件多线程还要考虑线程安全问题。最后Redis使用了I/O多路复用模型和非阻塞IO。
解释I/O多路 复用模型
Redis是基于内存的所以它的性能的瓶颈是网络延迟而不是执行速度I/O多路复用模型主要就是为了实现高效的网络请求。 在计算机操作系统中有用户空间和内核空间用户空间的权限较低不能直接调用系统资源而内核空间的权限高可以直接调用系统资源当用户空间需要使用系统资源时会先将用户数据写入内核空间然后内核空间使用系统资源进行处理Linux为了提高IO效率在用户空间和内核空间中都加入了缓冲区写数据时会先从用户缓冲区写入内核缓冲区然后内核缓冲区再写入设备中在读数据的时候设备会读取内核缓冲区的数据内核缓冲区从用户缓冲区中复制数据。 在上述操作中影响IO的主要有两个因素 用户缓冲区需要从内核缓冲区中获取数据如果内核缓冲区没有数据这时候就会一直等待在数据复制过程中也是一直等待这也是非常影响性能的 这时候有三种常见的I/O模型 BIO在获取数据recvfrom操作的时候如果内核缓冲区没有数据此时就处于阻塞状态当数据到达内核缓冲区的时候代表数据就绪此时需要将内核缓冲区的数据复制到用户缓冲区在这个过程中用户进程依然处于阻塞状态当复制结束后用户进程解除阻塞然后处理数据。 NIO非阻塞IO的recvfrom操作后会立即返回结果而不是阻塞进程当用户进程尝试从内核缓冲区读取数据时此时数据未到达内核缓冲区就返回异常给用户进程用户进程拿到error后再次尝试直到数据就绪然后复制数据。 这种方式的效率不是很高当用户进程在等待过程中虽然没有阻塞但是用户进程在等待数据过程中会不断询问内核有没有请求完成会导致CPU空转也会导致CPU使用率暴增。 IO多路复用利用单个线程监控多个Socket并在某个Socket可读/可写时得到通知从而避免无效等待充分利用了CPU资源。 流程用户进程调用select指定要监听的Socket集合内核监听对应的多个Socket任何一个Socket就绪就返回readable此过程中用户进程阻塞然后用户进程依次遍历找到就虚的Socket一次调用recvfrom读取数据内存缓冲区将数据拷贝到用户缓冲区用户进程处理数据。 在select监听过程中有提供select、poll、epoll来进行监听使用select和poll进行监听时都只能收到通知但是不知道那个Socket就绪之能通过遍历来确定在epoll中只要有就绪就会采用回调机制直接通知select然后直接将就虚的Socket写入用户缓冲区进行处理。 Redis的网络模型就是使用的IO多路复用模型结合事件处理器来应对多个Socket请求其中有连接应答处理器、命令回复处理器、命令请求处理器。在Redis6.0之后为了提高性能在命令回复处理器使用了多线程来处理回复事件在命令请求处理器中将命令转换使用了多线程增加了命令转换速度在命令执行时依旧使用的是单线程。
Redis为什么使用跳表而不是用B树?
主要是从内存占用、对范围查找的支持、实现难易程度这三方面总结的原因
从内存占用上来比较跳表比平衡树更灵活一些。 平衡树每个节点包含 2 个指针分别指向左右子树而跳表每个节点包含的指针数目平均为 1/(1-p)具体取决于参数 p 的大小。如果像 Redis里的实现一样取 p1/4那么平均每个节点包含 1.33 个指针比平衡树更有优势。 在做范围查找的时候跳表比平衡树操作要简单。 在平衡树上我们找到指定范围的小值之后还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造这里的中序遍历并不容易实现。而在跳表上进行范围查找就非常简单只需要在找到小值之后对第 1 层链表进行若干步的遍历就可以实现。 从算法实现难度上来比较跳表比平衡树要简单得多。 平衡树的插入和删除操作可能引发子树的调整逻辑复杂而跳表的插入和删除只需要修改相邻节点的指针操作简单又快速。