MySQL 缓存方案用来干什么 ?
- 缓存用户定义的热点数据,用户直接从缓存中获取热点数据,降低数据的读写压力。
- 场景分析
- 内存访问速度是磁盘访问速度的 10 万倍。
- 读的需求远远大于写的需求
- MySQL 自身缓冲层跟业务无关。
- MySQL 作为项目主要数据库,便于统计分析。
- 缓存数据库作为辅助数据库,存放热点数据。
还有哪些方式提升 MySQL 访问性能
- 读写分离
- 是什么 ?
- 设置多个从数据库,从数据库可能会在多个机器中。
- 写操作依然在主数据库。
- 主数据库提供数据的主要依据。
- 解决了什么问题:从数据库主要解决读压力。
- 原理:
- 主从原理。
- 异步复制:最终一致性;主从之间数据会有差异。
- 如果读操作有一致性要求,读操作去读主数据库。
- 连接池
- 在服务端中创建多个与数据库的链接。
- 解决了什么问题:并发提升数据库访问性能;复用连接资源,避免连接建立或断开、以及安全验证的开销。
- 原理:MySQL 的网络模型:select;阻塞的 IO 模型。如果发送一个事务(多个 SQL 语句),这个事务必须在一个连接中执行。
- 异步连接
- 在服务端创建一个连接,针对这个连接采用非阻塞 IO。
- 解决了什么问题:节省了网络传输时间。
- 原理:使用了非阻塞 IO。
MySQL 缓存方案
- Redis 缓存和 MySQL 一致性状态分析
- MySQL 有,Redis 无。
- MySQL 无,Redis 有。(错误状态)
- 都有,数据不一致。(错误状态)
- 都有,数据一致。
- 都没有
- 制定用户定义的热点数据读写策略
- 读:先读 Redis 缓存,缓存存在直接返回;缓存不存在,去访问 MySQL 获取,再写 Redis。
- 写:
- 以安全为主
- 先要删除 Redis 中的数据,然后再写 MySQL,最后将 MySQL 数据同步到 Redis。
- 问题:缓存方案的主要目标是提升效率,而现在为了安全降低效率。
- 只有一种情况使用:读远大于写。
- 以效率为主
- 先写 Redis 缓存并设置过期时间,再写 MySQL,等待 MySQL 同步到 Redis 中。
- 过期时间选择:与 MySQL 网络传输时间 + MySQL 处理时间 + MySQL 同步到 Redis 的时间有关。
- 安全问题:比如设置了过期时间为 200ms,在 200ms 窗口时间内,其它请求可能读到的是脏数据。
MySQL 主从复制
- 工作原理:
- 主库 DML 操作(增删改操作)通过 io-thread 写到 binlog。
- 从库请求读取 binlog,通过 io-thread 写入从库本地 relay-log(中继日志)。
- 从库通过 sql-thread 读取 relay-log,并把更新事件在从库重放(replay)一遍。
- 复制流程:
- Slave 中的 IO 线程连接上 Master,并请求从指定日志文件的指定位置之后的日志内容。
- Master 接收到来自 Slave 的 IO 线程的请求后,负责复制的 IO 线程会根据请求信息读取日志指定位置之后的日志信息,返回给 Slave 的 IO 线程。返回信息中包含日志信息、Master 端 binlog 文件的名称、下次读取 Master 端 binlog 文件中的起始位置。
- Slave 的 IO 线程接收到消息后,将接收到的日志内容依次添加到 Slave 端的 relay-log 文件末尾,并将读取到的 Master 端的 binlog 文件名和下次读取 binlog 文件中的起始位置记录到 master-info 文件中,以便在下一次读取的时候能够清楚地告诉 Master 从何处开始读取日志。
- Slave 的 SQL 进程在检测到 relay-log 中新增加了内容后,会马上解析 relay-log 的内容为在 Master 端真实执行时的可执行内容,并在自身执行(重放)。
如何把 MySQL 的数据同步到 Redis ?
- 额外增加一个中间件(go-mysql-transfer),该中间件伪装成 MySQL 的从数据库,不断地从 MySQL 中拉取数据(binlog)并进行解析,解析之后写到 Redis 中。(基于 MySQL 主从复制的原理,但是不会重放)
git clone https://gitee.com/mirrors/go-mysql-transfer
缓存方案的故障问题及解决
缓存穿透
- 数据在 Redis 和 MySQL 中都没有,一直读取不存在的数据,造成 MySQL 访问性能急剧降低。
- 假设黑客恶意请求服务器不存在的数据,此时 Redis 形同虚设,那么所有的请求压力都会堆积在 MySQL 中,相当于请求穿透了 Redis 直接到了 MySQL,造成 MySQL 访问性能急剧降低。
- 解决
- 在 Redis 中缓存不存在的热点数据为 <key, nil>,服务器每次读到 “nil” 字符串的时候,说明整个存储系统都没有这个数据,当客户端再次请求这个数据时,服务器可以直接返回。
- 缺点: 如果黑客不断地随机出新的 key,那么 Redis 就会写很多不存在的 <key, nil>,可能会把 Redis 的内存撑爆(Redis 不怕请求多,而是怕内存不够)。
- 部署布隆过滤器:布隆过滤器可以确定一个 key 是否存在。
- 布隆过滤器要部署到 Redis 中,而不是部署到服务器中(如果有多个服务器,那么每次都需要把热点数据写到每个服务器的布隆过滤器中,这样的话,就需要维护多个布隆过滤器,很麻烦)。
- 缺点:布隆过滤器只能增加数据,不能删除数据。
缓存击穿
- 数据在 Redis 中没有,在 MySQL 中有,大量并发连接请求,造成 MySQL 访问性能急剧降低。
- 解决
- 分布式锁:请求数据的时候获取锁,若获取成功,则操作后释放锁;若获取锁失败,则休眠一段时间(200 ms)再去获取锁。
- 服务器 A 先访问 Redis(要获取锁),发现数据不存在,然后访问 MySQL,发现数据存在,将数据同步给 Redis(要释放锁),后面的服务器 B、服务器 C 就可以直接从 Redis 中获取数据。
- 将很热的 key 设置为不过期。
缓存雪崩
- 大量缓存数据集中失效,但是数据在 MySQL 中存在,造成 MySQL 访问性能急剧降低。
- 解决
- 将很热的 key 设置为不过期。
- 间隔设置过期时间。
- 重启时,预先导入热数据到缓存(预热)。
缓存方案的弊端