MySQL缓存策略
提高MySQL性能的方法有如下几种:
- 连接池:阻塞IO+线程池
- 异步连接:非阻塞IO
- sql执行过程优化:不选即使执行,而选择预编译执行(跳过词法句法分析、权限验证、优化器,直接选择前面已经生成的执行计划(接口:prepare))
- 读写分离
- 缓存分案
一、MySQL主从复制
主从复制主要解决单点故障问题。主从复制实现的是最终一致性。
1. 主从复制流程
- 主库更新事件(update、insert、delete)通过io-thread写到binlog;
- 从库请求读取binlog,通过io-thread写入(write)从库本地relay log(中继日志);
- 从库通过sql-thread读取(read)relay log,并把更新事件在从库中执行(replay)一遍
2. 具体复制流程
- . Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)拷贝之后的日志内容。
- Master接收到来自Slave的IO进程的请求后,负责复制的IO进程会根据请求信息读取日志指 定位置之后的日志信息,返回给Slave的 IO进程。返回信息中除了日志所包含的信息之外, 还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置。
- Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最 末端,并将读取到的Master端的 bin-log的文件名和位置记录到master-info文件中,以便在 下一次读取的时候能够清楚的告诉Master从何处开始读取日志。
- . Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在 Master端真实执行时候的那些可执行的内容,并在自身执行。
二、读写分离
读写分离能优化数据库的写性能。但是读写分离场景难布署。
1. 最终一致性
写主读从。
数据写入主数据库,主从同步,从从数据库读取数据。
2. 强一致性
数据库写入主数据库。
当一致性要求高时,从主数据库读取数据;当一致性要求不高时,从从数据库读取数据。
三、缓存方案
缓存方案包括读写策略(主要是写策略)和同步策略。
写策略主要考虑是遵守最终一致性还是强一致性。
1. 前提
读多写少,单个主节点能支撑整个项目的数据量;数据的主要依据是mysql。
讲热点读数据在缓存数据库备份,讲热点读操作转移到缓存数据库。
2. 缓冲层
2.1 mysql的缓冲层
mysql有缓冲层,它的作用是用来缓存热点数据,这些数据包括数据文件、索引文件等;mysql缓冲层是从自身触发,跟具体业务无关;这里的缓冲策略主要是经过优化的LRU。
mysql数据主要存储在磁盘当中,适合大量重要数据的存储;磁盘当中的数据一般是远大于内存当中的数据;一般业务场景关系型数据库(mysql)作为主要数据库。
2.2 外置缓冲层
缓存数据库可以选用redis、memcached;它们所有数据都存储在内存当中,当然也可以将内存当中的数据持久化到磁盘当中;内存的数据库和磁盘的数据是一比一的。
2.3 存储比较
3. 几项重要的数据
- 内存的访问速度是磁盘访问速度的10万倍(数量级倍率);内存的访问速度大约是100ns,而一次磁盘访问大约是10ms;访问mysql时访问磁盘的次数跟b+树的高度相关。
- 一般大部分项目中,数据库读操作是写操作的10倍左右。
4. 应用场景分析
- 内存访问速度是磁盘访问速度的10万倍
- 大多数业务场景读次数是写次数的10倍以上
- mysql自身的缓冲层跟业务无关,我们需要跟业务相关的缓存数据
- mysql作为项目主要数据库,便于统计分析
- 缓存数据库作为辅助数据库,存放热点数据
缓存方案主要优化的是读性能。
5. 同步问题分析
5.1 原因
没有缓存层之前,我们对数据库的读写都是基于mysql,所以不存在同步问题。(这句话不是绝对的,比如读写分离的场景也会存在同步问题)。
引入了缓存数据库后,需要引入同步策略,使缓存数据库和mysql的数据保持一致。
5.2 存在的数据状态
- mysql有数据,缓存无数据(通过策略将mysql数据同步到redis)
- mysql无,缓存有(不可以接受,通过策略避免)
- 都有数据,但是数据不一致(不可以接受,通过策略避免)
- 都有且数据一致(策略的目标状态)
- 都没有(这是正常状态)
由于数据的主要依据是mysql,所以最重要的是保证mysql数据的正确性。对于情况2、3,这是比较危险的数据状态,我们应该通过缓存策略避免这种情况的发生。
当缓存不可用时,我们的整个系统应该能保持正常工作;当mysql不可用时,系统应停止对外服务。
5.3 目标场景
当只有一个数据请求中心时,数据请求中心直接访问数据库+缓存。如部分游戏业务场景,有单独的DBServer。
当有多个数据请求中心时,我们可以在数据请求中心和数据库+缓存间设置一个代理层,将多个数据请求中心伪装成一个数据请求中心。如大部分的web项目,server端不缓存数据,直接操作数据库。
5..4 缓存设置建议
对于重要数据,不应该作为热点数据存储在cache中;频繁变动的数据也不适合放在cache中。
6. 最终一致性解决方案
6.1 读策略
先读缓存,缓存存在直接返回;缓存不存在,去访问mysql获取,再写redis。
6.2 写策略
直接写mysql,等到mysql同步到缓存。
这种写操作可能会带来的问题是,写mysql时,缓存中可能存在相关的旧数据,此时并发读操作可能没有读到最新的数据。
最终一致性保证的是数据库和缓存最终能数据一致,允许中间出现不一致状态。适用于一致性要求不高的场景,如博客的修改更新。
7. 强一致性解决方案
因为多个数据中心最终能转换成一个数据中心,所以我们此处只分析一个数据中心的情况。
7.1 读策略
先读缓存,缓存存在直接返回;缓存不存在,去访问mysql获取,再写redis。
7.2 写策略
写策略有两种:
- 先删除缓存,再写mysql,等到mysql同步到缓存
- 直接在缓存写入新的数据并设置该条数据的过期时间,再写mysql,等到mysql同步到缓存(当同步到缓存时记得删除该数据的过期时间)
从性能分析,第二种写策略比第一种写策略的性能要好,特别是频繁修改数据时。但是,第二种写策略也带来了丢失更新的问题。
当我们选择第二种写策略时,当对mysql的修改时,mysql宕机导致修改失败,而在此前又有客户端通过cache获取了新数据。此时,对于mysql而已,丢失了更新;对于客户端而言,因为mysql没有更新成功,所以读取的是错误数据(mysql宕机,此时服务端会停摆,所以客户端的后续不会影响数据库,不会影响到最终数据的正确性)。
针对第二种写策略,缓存中新数据的过期时间的设置应该考虑访问mysql后mysql同步到cache的时间。
8. 数据同步方案
数据同步是在对mysql的写操作完成后发生的,负责将mysql的数据更新同步到cache。
8.1 触发器+UDF
该方案的缺点比较明显,UDF不具备事务,不能回滚;并且该方案效率低。
https://gitee.com/josinli/mysql_redis
8.2 go-mysql-transfer
go-mysql-transfer是伪装从数据库 从mysql复制数据。它的缺点是需要引入zk等实现高可用。
https://www.cnblogs.com/unrealCat/p/16114732.html
8.3 cancel+cancel客户端
cancel也是伪装成从数据库从mysql复制数据。但是它的流程因为有一个客户端中间件,所以比go-mysql-transfer多一步流程。但是cancel+cancel客户端方案具备高可用性。(常用于java)
9. 缓存故障
缓存穿透常见于黑客攻击。缓存击穿和缓存雪崩的区别是,缓存击穿的同个数据的并发请求,而雪崩是大量数据的缓存丢失。
9.1 缓存穿透
假设某个数据redis不存在,mysql也不存在,而且一直尝试读怎么办?缓存穿透,数据最终压力 依然堆积在mysql,可能造成mysql不堪重负而崩溃;
解决方案:
- 发现mysql不存在,将redis设置为 <key, nil=""> 设置过期时间 下次访问key的时候 不再访问 mysql 容易造成redis缓存很多无效数据;
- 布隆过滤器,将mysql当中已经存在的key,写入布隆过滤器,不存在的直接pass掉;
9.2 缓存击穿
某一些数据redis没有,但是mysql有;此时当大量这类数据的并发请求,同样造成mysql 访问量过大;
解决方案:
- 使用分布式锁。请求数据的时候获取锁,如果获取成功,则操作,获取失败,则休眠一段时间(200ms)再去获取;获取成功,则释放锁。读取数据的流程为:首先读redis,不存在,读mysql,存在,写redis,写完释放key的锁。
- 将很热的key设置为不过期。
9.3 缓存雪崩
在一段时间内,缓存集中失效(redis无 mysql 有),导致请求全部走mysql,有可能搞垮数据库, 使整个服务失效;
解决方案:
缓存数据库在整个系统不是必须的,也就是缓存宕机不会影响整个系统提供服务;
- 如果因为缓存数据库宕机,造成所有数据涌向mysql,可采用高可用的集群方案,如哨兵模式、cluster模式。
- 如果因为设置了相同的过期时间,造成缓存集中失效,泽设置随机过期值或者其他机制错开失效时间。
- 如果因为系统重启时,造成缓存数据消失。当重启所需时间短,redis开启持久化(过期信息也会持久化)就行了;当重启时间较长,则选择提前将热数据导入redis当中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)