同步操作将从 编程界的小学生/java-legendary 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
[TOC]
author:编程界的小学生
date:2021/02/21
C写的开源高性能非关系型键值对数据库。底层采取epoll读写速度非常快,大多用于缓存,也提供了事务、持久化、集群以及多种数据类型的功能。
优点:
缺点:
guava/map是基于jvm内存的本地缓存,生命周期会随着jvm的结束而停止,也不存在持久化的特性。Redis是分布式缓存,可以进行持久化。
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
rdb(默认) 和aof两种。
rdb优点:
rdb缺点:
aof优点:
aof缺点:
fork子进程+copyonwrite技术。
fork()是unix和linux这种操作系统的一个api,而不是Redis的api。fork()用于创建一个子进程,注意是子进程,不是子线程。fork()出来的进程共享其父类的内存数据。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见。比如:A进程fork()了一个子进程B,那么A进程就称之为主进程,这时候主进程A和子进程B所指向的内存空间是同一个,所以他们的数据一致。但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。这一点有点像守护线程的概念。Redis正是巧妙的运用了fork()这个牛逼的api来完成RDB的持久化操作。
Redis巧妙的运用了fork()。当bgsave执行时,Redis主进程会判断当前是否有fork()出来的子进程,若有则忽略,若没有则会fork()出一个子进程来执行rdb文件持久化的工作,子进程与Redis主进程共享同一份内存空间,所以子进程可以搞他的rdb文件持久化工作,主进程又能继续他的对外提供服务,二者互不影响。我们说了他们之后的修改内存数据对彼此不可见,但是明明指向的都是同一块内存空间,这是咋搞得?肯定不可能是fork()出来子进程后顺带复制了一份数据出来,如果是这样的话比如我有4g内存,那么其实最大有限空间是2g,我要给rdb留出一半空间来,扯淡一样!那他咋做的?采取了copyonwrite技术。
主进程fork()子进程之后,内核把主进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向主进程。这也就是共享了主进程的内存,当其中某个进程写内存时(这里肯定是主进程写,因为子进程只负责rdb文件持久化工作,不参与客户端的请求),CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。中断例程中,内核就会把触发的异常的页复制一份(这里仅仅复制异常页,也就是所修改的那个数据页,而不是内存中的全部数据),于是主子进程各自持有独立的一份。
数据修改之前的样子
数据修改之后的样子
其实就是更改数据的之前进行copy一份更改数据的数据页出来,比如主进程收到了set k 1
请求(之前k的值是2),然后这同时又有子进程在rdb持久化,那么主进程就会把k这个key的数据页拷贝一份,并且主进程中k这个指针指向新拷贝出来的数据页地址上,然后进行更改值为1的操作,这个主进程k元素地址引用的新拷贝出来的地址,而子进程引用的内存数据k还是修改之前的。
copyonwritefork()出来的子进程共享主进程的物理空间,当主子进程有内存写入操作时,read-only内存页发生中断,将触发的异常的内存页复制一份(其余的页还是共享主进程的)。
1.假设是全量复制,那么内存空间直接减半,浪费资源不说,数据量10g,全量复制这10g的时间也够长的。这谁顶得住?
2.如果不全量复制,会是怎样?相当于我一边复制,你一边写数据,看着貌似问题不大,其实不然。比如现在Redis里有k1的值是1,k2的值是2,比如bgsave了,这时候rdb写入了k1的值,在写k2的值之前时,有个客户端请求
set k1 11
set k2 22
那么持久化进去的是k2 22,但是k1的值还是1,而不是最新的11,所以会造成数据问题,所以采取了copyonwrite技术来保证触发bgsave请求的时候无论你怎么更改,都对我rdb文件的数据持久化不会造成任何影响。
就是每次都在aof文件后面追加命令。他与主进程收到请求、处理请求是串行化的,而非异步并行的。图示如下:
所以aof的频率高的话绝逼会对Redis带来性能影响,因为每次都是刷盘操作。跟mysql一样了。Redis每次都是先将命令放到缓冲区,然后根据具体策略(每秒/每条指令/缓冲区满)进行刷盘操作。如果配置的always,那么就是典型阻塞,如果是sec,每秒的话,那么会开一个同步线程去每秒进行刷盘操作,对主线程影响稍小。
其实Redis每次在写入AOF缓冲区之前,他都会调用flushAppendOnlyFile(),判断是否需要将AOF缓冲区的内容写入和同步到AOF文件中。这个决策是由配置文件的三个策略来控制的
1、分析
这道题不严谨,因为不知道他问的是持久化过程哪个快还是说Redis主进程对外提供请求哪个快?(也就是说哪个持久化方式会对主进程影响较大)
2、持久化过程哪个快
那肯定AOF,因为AOF每次只追加命令,而RDB每次都是全量覆盖。
3、哪个持久化方式会对主进程影响较大
那肯定是AOF效率低,因为AOF是串行的,相当于每次处理完命令都要同步的写入磁盘(当然也看具体策略),而bgsave模式的RDB则是开启子进程并行的处理这件事,不影响主进程对外提供请求,即使AOF策略开到最容易丢失数据的那种,那也是不定期磁盘操作,这是毫秒级别的。而RDB持久化即使发生CopyOnWrite也只是寻址操作,纳秒级别的。
只能同时存在一个,原因如下:
不能!
1.如果bgsave正在执行,那么client发送的bgrewriteaof命令会被推迟到bgsave结束后才得到执行。 2.如果bgrewriteaof正在执行,那么client发送的bgsave命令将会被服务器拒绝
RDB:
AOF:
比如我有业务很简单,就来回delete set 同一个key。就这个业务运行了10年,那么aof文件将记录无数个delete k1, set k1 xxx。其实都是重复的,但是我aof每次都追加,文件变成了1T大小。这时候Redis宕机了,要恢复,你想想1TB大小的aof文件去恢复,累死了。最主要的是1TB大小只记录了两个命令,所以压缩其实就是来处理这件事的。
Redis4.0之前就是将aof文件中重复的命令给去掉。保留最新的命令。进而减少aof文件大小。
4.0之前的做法效率很是低下,需要逐条命令对比。4.0开始的rewrite支持混合模式(也是就是rdb和aof一起用),直接将rdb持久化的方式来操作将二进制内容覆盖到aof文件中(rdb是二进制,所以很小),然后再有写入的话还是继续append追加到文件原始命令,等下次文件过大的时候再次rewrite(还是按照rdb持久化的方式将内容覆盖到aof中)。但是这种模式也是配置的,默认是开,也可以关闭。
执行bgrewriteaof命令。
通过以下两个配置协作触发
- auto-aof-rewrite-min-size
AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。
- auto-aof-rewrite-percentage
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比,如100代表当前AOF文件是上次重写的两倍时候才重写。
混合持久化结合了RDB持久化 和 AOF 持久化的优点,采取了rdb的文件小易于灾难恢复,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,需要专业的工具来阅读,因为是二进制,所以阅读性较差。
Redis4.0以及以后新增的内容,很强大。默认开启,也建议开启。但具体还要看业务,比如业务就允许数据丢失,那直接RDB就完事了,不需要开AOF,这样效率还高。如果一点数据都不允许丢失,那只能RDB+always策略的AOF,换言之,不管怎样,都建议开启RDB,RDB可以应对灾难性快速恢复。混合持久化也不用过于担心文件大小问题,4.0开始的rewrite支持混合模式直接按rdb持久化的方式来操作将二进制内容覆盖到aof文件中(rdb是二进制,所以很小),然后再有写入的话还是继续append追加到文件原始命令,等下次文件过大的时候再次rewrite(还是按照rdb持久化的方式将内容覆盖到aof中),堪称完美!
会优先看是否存在aof文件,若存在则先按照aof文件恢复,因为aof毕竟比rdb全。若aof不存在,则才会查找rdb是否存在。这是默认的机制。毕竟aof文件也rewrite成rdb二进制格式,文件小,易于回复。所以redis会优先采取aof。
同步、阻塞。
致命的问题,持久化的时候redis服务阻塞(准确的说会阻塞当前执行save命令的线程,但是redis是单线程的,所以整个服务会阻塞),不能继对外提供请求,GG!数据量小的话肯定影响不大,数据量大呢?每次复制需要1小时,那就相当于停机一小时。
异步、非阻塞。
采取fork() + copyonwrite的方式,他可以一边进行持久化,一边对外提供读写服务,互不影响,新写的数据对我持久化不会造成数据影响,你持久化的过程中报错或者耗时太久都对我当前对外提供请求的服务不会产生任何影响。持久化完会将新的rdb文件覆盖之前的。
如果假设你设置一个一批key只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的? 答案是:定期删除+惰性删除
定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。
定期删除可能会导致很多过期key到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。 并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下
通过上述两种手段结合起来,保证过期的key一定会被干掉
很简单,就是说,你的过期key,靠定期删除没有被删除掉,还停留在内存里,占用着你的内存呢,除非你的系统去查一下那个key,才会被redis给删除掉
但是实际上这还是有问题的,如果定期删除漏掉了很多过期key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了,咋整?
答案是:走内存淘汰机制。
如果redis的内存占用过多的时候,此时会进行内存淘汰策略: 假设如下场景:
redis里有10000个key,现在已经满了,redis需要触发内存淘汰策略删除key
1个key,最近1min被查了2w次
1个key,最近10min被查了100次
1个key,最近1小时被查了10次
总结:Redis的内存淘汰策略的选取并不会影响过期的key的处理,而是用于处理内存不足时需要申请额外空间的数据,过期策略用于处理过期的缓存数据。
Redis的事务并不像Mysql那么灵活,有隔离级别,出问题后还能回滚数据等高级操作。Redis毕竟是非关系型数据库,他目前事务回滚机制是不执行命令,也就是可以采取watch命令模拟乐观锁,进行监听数据,发现数据不是事务开始时候的样子了,那么我这个事务里的命令就不会得到执行。
Redis是单进程的且它保证在执行事务时不会对事务进行中断,事务可以从运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。他目前事务回滚机制是不执行命令,也就是可以采取watch命令模拟乐观锁,进行监听数据,发现数据不是事务开始时候的样子了,那么我这个事务里的命令就不会得到执行。
lua脚本。
redis内存数据集大小上升到一定大小的时候,就会进行数据淘汰策略,热点数据不会被淘汰掉。
会进行内存淘汰机制,当Redis达到内存上限时会淘汰掉不活跃的数据。如果还是不够,内存还是满了的情况下Redis的写命令会返回错误信息,读命令还可以正常返回。
什么是epoll?这篇文章解释的淋漓尽致:
分布式锁。
建议提前做好数据量预估然后选择合适的Redis集群数,避免后期在新增服务器进行迁移重新分片。
情况一
大批量热点数据过期后,高并发请求,直接请求到db,引起db压力造成查询阻塞甚至宕机。
解决方案
情况二
Redis挂了。导致请求都到db了。假设高并发5000个请求,本来缓存再高峰期可以抗住每秒4000个请求,但是Redis挂了,5000个请求直接干到了数据库层,这可能又把数据库给打死了。
解决方案
限流的好处是数据库绝对不会死,因为限流组件保证了有多少请求能进来。只要数据库不死,就能对外继续提供请求。虽然有部分用户请求走降级,但不是全部,会有大部分请求得到执行。
比如电商网站,有以下商品在redis里和mysql里。且Redis里只存放热点商品, 而不是全部。 苹果、香蕉、鸭梨等,但是用户搜了个我这电商网站里没有卖的商品,这时候redis里肯定没有搜的数据,就去请求db了。db也不一定有(有可能有有可能没有),如果没有的话那白白浪费性能了。这就是缓存穿透。量少的话就别说了,量少都没必要redis缓存。大量的话会很恐怖,比如淘宝,他无所不卖,万一真找到一个不卖的东西去搜,然后redis没有,结果都去请求mysql了。那不GG了嘛?
解决方案
布隆过滤器。原理就是利用bitmap来解决缓存穿透的一种技术手段。实现步骤如下:
某个 key 非常非常热点,访问非常的频繁,高并发访问的情况下,当这个 key在失效(可能expire过期了,也可能LRU淘汰了)的瞬间,大量的请求进来,这时候就击穿了缓存,直接请求到了数据库,一下子来这么多,数据库肯定受不了,这就叫缓存击穿。某个key突然失效,然后这时候高并发来访问这个key,结果缓存里没有,都跑到db了。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
缓存预热就是系统启动的时候就查询数据库将相关的数据直接缓存到Redis中。这样可以避免用户查询的时候先判断是否有缓存,没的话在查db进行缓存等一系列操作了。
实现方案
如果数据量不大的话,可以在项目启动的时候进行缓存。
如果数据量很大的话可以写个简易的页面,上线时手工点一下进行缓存。
定时任务定时刷新缓存。
就是当缓存出故障的时候,别影响主业务系统,让他可以继续对外提供服务。
实现方案
很简单,在catch里去查db,而不是抛出异常。
热点key就是频繁被访问的key,为了防止频繁访问数据库,所以需要将这些数据缓存起来,这些数据就称为热点数据。
一般采取分布式锁来安全的缓存热点key,也就是对查询缓存加锁,即如果key不存在就加锁,然后查数据库set到缓存,然后解锁。其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询。
Redis的Pipeline类型,他会将一组命令用一次客户端服务端的请求来完成。
不适用场景:
适用场景:
比如群发短信、邮件这种,失败了我补偿,我不需要及时知道发送结果的,这种用pipeline合适 。
采取list数据类型,rpush生产消息,lpop消费消息,阻塞式的可以用blpop,在没有信息的时候,会一直阻塞,直到信息的到来。
采取zset数据类型,使用时间戳做score,然后用zadd来生产消息,消费组使用zrangbyscore
获取x秒之前的数据做轮询处理。
采取set数据类型的交集SINTER/SINTERSTORE
采取Redis的bitmap数据类型来存储,然后用Redis提供的bitmap命令进行位运算计算出连续登录天数,有多少用户活跃等。
不懂的看这篇:https://blog.csdn.net/ctwctw/article/details/105013817
选择key-value的原因:key-value简单粗暴,使用方便,效率更佳。 为什么不支持sql:因为redis的内存模型是一个hashtable,不使用表来存储数据,也不会预定义或强制要求用户对redis储存的不同数据进行关联。
仅针对Redis6.0之前的答案,因为Redis6.0之后处理命令也是多线程了。
redis中io多路复用器模块是单线程执行,事件处理器也是单线程执行,两个线程不一样。所以实际redis应该是单进程多线程,只是不同的模块都用的单线程实现。 两个维度来举例: (1)若是client发送命令到server的话,server处理命令是单线程逐条进行的。 (2)server内部可以是多线程的,比如aof持久化,假设策略每秒,那就是再单独开启一个线程去执行aof文件持久化操作,这就是多线程了。
参考redis各种部署方式的优缺点来决定。
前者用主从+哨兵进行高可用,加快读请求的速度,减轻单节点的压力。
后者用集群来均分这400G数据。
可以采取geo命令。3.2版本新增加的命令。主要原理是根据经纬度来计算距离。
存放慢查询语句的是一个先进先出的队列,他有固定长度,slowlog-max-len就是控制慢查询语句数量的,存放到内存中的,默认128,超过128的话会舍弃最先进入队列的命令。所以一般可以搞大点,因为他是存放到内存的,重启就没了。
慢查询阈值(单位:微妙,默认是10000,也就是10毫秒)。默认是超过10ms命令没执行完就扔到慢查询队列里(上面参数控制队列大小)。Redis号称QPS万级别,所以建议设置成1000,也就是1ms。
建议:
备份方案:
我们需要定时备份rdb文件来做冷备,为什么?不是有aof和rbd了吗为什么还要单独写定时任务去备份?因为Redis的aof和rdb是仅仅有一个最新的,比如谁手贱再Redis宕机的时候执行
rm -rf aof/rdb
了,那不就GG了吗?或者rdb/aof文件损坏了等不可预期的情况。所以我们需要单独备份rdb文件以防万一。为什么不定时备份aof而是rdb?定时备份aof没意义呀,定时本身就是冷备份,不是实时的,rdb文件又小恢复又快,她哪里不香?
恢复方案:
问题一:直接把备份的rdb扔到redis持久化目录下然后重启redis不行的原因在于:redis是按照先aof后rdb进行恢复的,所以都是开启aof的,redis启动后会重新生成新的aof文件,里面是空的。所以不会进行任何数据恢复,也就是说虽然你把rdb丢给redis了,但是redis会按照aof来恢复,而aof是redis启动的时候新生成的空文件,所以不会有任何数据进行恢复。
问题二:那么我们把rdb文件丢给redis后,先将redis的aof关闭再启动redis进程不就能按照rdb来进行恢复了吗?是这样的,没毛病!但是新的问题来了,我们aof肯定要开的,aof对数据保障更可靠。那什么我们按照rdb文件恢复完后再修改redis配置文件开启aof然后重启redis进程不就得了嘛?大哥…你打开aof然后重启redis,这时候redis又会生成一个空的aof文件,这时候恢复的时候又是啥数据都没了。因为数据是存到内存里,你重启后肯定没了,需要持久化文件来恢复。这时候aof是空的,我恢复个鸡毛啊。
具体方案:
我不管你是持久化文件丢了还是坏了,我都先
rm -rf *
给他删了。
- 停止redis进程
- 删除坏掉的rdb和aof持久化文件。
- 修改配置文件关闭redis的aof持久化。
- 找到最新备份的rdb文件扔到redis的持久化目录里。(这里最新的肯定是按照小时备份的最后一个)
- 启动Redis进程
- 执行
set appendonly yes
动态打开aof持久化。也就是说打开aof的操作不是修改配置文件然后重启,而是先热修改让他生成aof,这次生成肯定是会带着内存中完整的数据的。然后再修改配置文件重启。
- 等aof文件生成后再修改redis配置文件打开aof。
- 重启redis进程。
- 完美收官。
1、Redis的过期key删除策略有哪些?也是expire原理
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。