当前位置: 首页 > 科技观察

突然挂了!Redis缓存全部在内存中,完了!

时间:2023-03-22 15:22:50 科技观察

“醒醒!醒醒!”,隐隐约约,我听到有人在叫我。缓缓睁开眼睛,原来身边站着一个MySQL大哥。“我怎么睡着了?”“哎,你刚才是不是出错了,整个过程崩溃了!我收到了很多查询请求!”,MySQL说。刚睡醒,脑子还有些迷糊,MySQL大哥扶我起来继续干活。“糟糕!我之前缓存的数据全没了!”“卧槽?你没做坚持吗?”MySQL小哥一听脸色变了。我不好意思地摇摇头,“我保存在内存里,所以才这么快。”“那你也可以保存在硬盘上,这样的话,你还要从头开始建立缓存,这不是浪费时间吗?!”我点点头,“让我想一想,看看这个坚持要怎么做。”在RDB持久化几天后,我想出了一个方案:由于我在RDB中的所有数据都存储在内存中,所以最简单的方法就是遍历它,将它们全部写入文件。为了节省空间,我定义了一个二进制格式,把数据一个一个放在一起,生成一个RDB文件。不过我的数据量有点大,一次性全部备份起来会很费时间,所以不能太频繁,不然就不用做生意了,花时间就好了在备份上。还有,如果一直没有写操作,全是读操作,那我也不需要重复备份,浪费时间。想了想,还是决定提供一个配置参数,既可以支持定期备份,又可以避免做无用功。像这样:save9001#1writein900seconds(15minutes)save30010#10writein300seconds(5minutes)save6010000#10000in60seconds(1minute)write多个条件可以组合使用。只要满足上述条件之一,我就会进行备份。后来又想了想,这样还是不行,得fork一个子进程来做,不能浪费时间。有了备份文件,下次再遇到死机退出,甚至服务器断电罢工的时候,只要我的备份文件还在,开机就可以读取,快速恢复到之前的状态!MySQL:binlogI有了这一套图,我兴冲冲地拿给MySQL大哥看,希望他能给我一些鼓励。“大哥,你的计划有点问题。”没想到,他给我泼了一盆冷水。“问题?什么问题?”“你看,你定期备份,而且周期还是分钟级的,你知道我们的服务每秒响应多少请求,有多少数据不能丢失吗?”,MySQL认真地说。我有些喘不过气来,“不过这个备份需要一次遍历所有数据,开销还是挺大的,不适合高频执行。”“谁让你一次遍历所有数据的?来,我给你看个东西”,MySQL小哥带我到一个文件目录:mysql-bin.000001mysql-bin.000002mysql-bin.000003“看,这些我的二进制日志是binlog,你猜怎么着?什么?”,MySQL小哥指着那堆文件说。我看了一眼,全是一堆二进制数据。我听不懂,摇了摇头。“它记录了我对数据进行的所有操作,如INSERT、UPDATE、DELETE等,当我要恢复数据时,这会派上用场。”听到他的话,我顿时灵感降了下来!我告别了我的兄弟MySQL,回去研究新的解决方案。AOF持久化你也知道,我也是基于命令式的,日常工作就是响应业务程序发来的命令请求。回来之后,我决定跟着葫芦,向MySQL大哥学习,把我执行的所有写命令都记录下来,写成一个文件,并给这个持久化方式起了个名字:AOF(AppendOnlyFile).但是,我在使用RDB解决方案时遇到了同样的问题。我应该多久写一次文件?我绝对不能把每一个写入文件的命令都记录下来,这样会严重拖累我的性能!我决定先准备一个缓冲区,然后先把要记录的命令暂时保存在这里,然后再择机写入文件。我称这个临时缓冲区为aof_buf。照做吧,我试了一下,发现数据没有写入文件。多方查询才知道,操作系统也是有缓存区的。我写的数据是他缓存的,并没有给我写入文件。这不是骗人的吗!写完好像得刷新一下,记下数据。想了想还是提供一个参数让业务程序设置什么时候刷新。appendfsync参数,三个值:always:每个事件周期同步刷新everysec:每秒同步刷新no:我就写,让操作系统决定什么时候真正写AOFrewrite这次我不像以前那么冲动,我决定试运行一会再告诉MySQL大哥,免得再被他戳到。试用了一段时间后,一切正常,但是我发现随着时间的推移,我写的AOF备份文件越来越大了!不仅占用大量硬盘空间,复制、移动、载入分析都非常麻烦,也很耗时。我必须找到一种方法来压缩文件。我把这个过程称为AOF重写。一开始打算分析原AOF文件,然后去掉里面多余的指令来瘦身AOF文件,但是很快就放弃了这个想法,工作量太大,而且分析起来相当困难麻烦,浪费精力和时间。原来一个一个记录的方式真的很蠢。数据变来变去,有很多中间状态是没用的。为什么我不直接记录最终的数据状态呢?例如:RPUSHname_list'编程技术宇宙'RPUSHname_list'帅气玩编程'RPUSHname_list'后端技术派'可以合二为一搞定:RPUSHname_list'编程技术宇宙''帅气玩编程''返回-endtechnologyschool'AOF文件重写的想法我是有的,但是做这个还是比较费时间的。我决定fork一个子进程来以与RDB相同的方式执行此操作。像我一样谨慎。这样做之后,如果我在重写子进程的时候修改了数据,就会和重写的内容不一致!MySQL小弟肯定会批评的,这个漏洞我得补上。所以,除了之前的aof_buf,我还准备了另一个buffer:AOFrewritebuffer。从重写子进程创建的那一刻起,我也将后面的写命令复制到这个重写缓冲区中。子进程重写AOF文件后,我会写入这个缓冲区。该命令被写入新的AOF文件。最后,重命名新的AOF文件并替换原来臃肿的文件,大功告成!确定自己的思路没有问题后,又找到了MySQL师兄,有了新的方案,就全部搞定了。这一次,我想这一次他应该是无话可说了吧?MySQL小哥看到我的方案,露出了满意的笑容。他就问了一个问题:这个AOF计划这么好,RDB计划可以扔掉吗?什么?没想到他居然问我这个问题,我陷入了沉思。你觉得我应该怎么回答?本文转载自微信♂“编程技术宇宙”,可通过以下二维码关注。转载本文请联系编程技术宇宙公众号。