MySQL缓存策略

MySQL缓存策略

一 缓存策略介绍

数据库提升读写性能的方式有:

  1. 连接池 阻塞IO和线程池
  2. 异步连接 非阻塞IO
  3. sql执行出发:即时执行+预编译执行
  4. 读写分离 对一致性要求不高
  5. 缓存方案

这篇blog重点就是介绍下MySQL的缓存方案。

MySQL缓存策略是指:
一般的项目中会有一个主要数据库,这个数据库一般就是关系型数据库,比如MySQL。为了提高MySQL的读写性能,会增加一个缓存数据库,用来缓存用户的热点数据。(MySQL中的缓存是为了缓存MySQL中的热点数据)。

对于用户热点数据,有五种情况:

  1. mysql有,cache没有 正常,通过策略避免
  2. mysql没有,cache有 不正常,避免
  3. mysql和cache都有,数据不一致 不正常,避免
  4. mysql和cache都有,数据一致 最终目标
  5. mysql和cache都没有 正常的

现在制定一下有缓存情况下的读写策略。

  1. 读策略:

    1. 先看cache是否有数据,如果有,直接返回;
    2. 如果没有,去访问mysql;
    3. 如果mysql有,缓存数据到cache;
    4. 如果mysql没有,就是没有。
  2. 写策略(这里做到了强一致性):

    1. 删除:先去删除缓存,再去删除mysql。为了避免发生2的情况。
    2. 修改:删除缓存,再去修改mysql
    3. 插入:删除缓存,再去修改mysql

当删除缓存并修改mysql后,mysql会主动将改变同步到缓存中。

这里假设用来缓存的数据库是redis。MySQL同步数据到redis,原理是主从复制(blog第二部分讲解)。
go-mysql-transfer会模拟成mysql的从节点,重mysql中复制某些指定的热点数据,并将它再次复制给redis。
在这里插入图片描述
上面这些缓存策略,逻辑上没有问题,但是实际上可能会有问题。

在这里插入图片描述

现在考虑下问题是否解决?是否有异常的情况?

显然,问题没有全部解决,我们刚刚思考的方向全是正常流程下的方式,我们来看异常情况。有三种异常的情况:1,缓存穿透;2,缓存击穿;3,缓存雪崩。

1. 缓存穿透
假设某个数据redis不存在,mysql也不存在,而且一直尝试读怎么办?缓存穿透,数据最终压力依然堆积在mysql,可能造成mysql不堪重负而崩溃;

解决方案:

  1. 发现mysql不存在,将redis设置为 <key, nil> 设置过期时间 下次访问key的时候 不再访问
    mysql 容易造成redis缓存很多无效数据;
  2. 布隆过滤器,将mysql当中已经存在的key,写入布隆过滤器,不存在的直接pass掉;

2. 缓存击穿
某些数据redis没有,但是mysql有;此时当大量这类数据的并发请求,同样造成mysql访问量过大;

解决方案:

  1. 加锁
    请求数据的时候获取锁,如果获取成功,则操作,获取失败,则休眠一段时间(200ms)再去获取;获取成功,则释放锁首先读redis,不存在,读mysql,存在,写redis key的锁
    整个流程走完,才让后面的服务器访问
  2. 将很热的key,设置不过期

3. 缓存雪崩
表示一段时间内,缓存集中失效(redis无 mysql 有),导致请求全部走mysql,有可能搞垮数据库,使整个服务失效;

解决方案:

  1. 如果因为缓存数据库宕机,造成所有数据涌向mysql;
    采用高可用的集群方案,如哨兵模式、cluster模式;
  2. 如果因为设置了相同的过期时间,造成缓存集中失效;
    设置随机过期值或者其他机制错开失效时间;
  3. 如果因为系统重启的时候,造成缓存数据消失;
    重启时间短,redis开启持久化(过期信息也会持久化)就行了; 重启时间长提前将热数据导入redis当中;

二 主从复制

MySQL同步数据到redis,原理就是主从复制。主从复制组要解决了单点故障的问题,也解决了读写分离的问题。读写分离指的是向主节点写,从从节点读。读写分离只是保证最终一致性。

原理图:
在这里插入图片描述

  1. 主库更新事件(update、insert、delete)通过 io-thread 写到binlog(binlog是在引擎之上的);
  2. 从库请求读取 binlog,通过 io-thread 写入(write)从库本地 relay log(中继日志);
  3. 从库通过sql-thread读取(read) relay log,并把更新事件在从库中执行(replay)一遍;

复制流程:

  1. Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
  2. Master接收到来自Slave的IO进程的请求后,负责复制的IO进程会根据请求信息读取日志指定位置之后的日志信息,返回给Slave的 IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置。
  3. Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master从何处开始读取日志。
  4. Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值