mysql缓存方案

数据库提升性能的方式

  • 数据库连接池。使用的是阻塞io,连接池一般结合线程池一起使用。
  • 异步请求。使用异步io,需要自己实现mysql的协议。
  • 从sql执行处罚,使用sql预编译执行的方式,prepare,跳过词法句法分析、权限验证、优化器等步骤,提高执行效率。
  • 读写分离。对于一致性要求不是特别高的场景,也就是最终一致性,并且读多写少的场景,可以使用主数据库写,从数据库读的方式。

数据库主从方式可以解决数据库单点故障的问题。

使用数据库主从方式,如果一致性要求高:

  1. 使用主从半同步复制;
  2. 读去主数据库读取。
  • 缓存方案,本文介绍的主要内容

为什么需要数据库缓冲层?

前提

一般大部分项目中,数据库读操作是写操作的10倍左右;单个主节点能支撑项目数据量;数据的主要依据是 mysql。

mysql缓存

mysql本身是有缓存的,它的作用也是用来缓存热点数据,这些数据包括数据文件、索引文件等;mysql的缓存是从自身出发,跟应用层的具体业务无关;缓存策略主要是类似LRU的算法。

mysql数据主要存储在磁盘当中,适合大量重要数据的存储;磁盘当中的数据是远大于缓存中的数据;一般业务场景mysql作为主要数据库。

img

img

mysql缓存和缓存数据库的区别

缓存数据库就是内存数据库,包括redis,mamcached等。他们所有的数据都存储在内存当中,当然也可以选择将内存中的数据持久化到磁盘当中。

mysql本身的缓存和缓存数据的数据都是存在内存中。mysql缓存的内容是最近访问的热点数据,不受具体业务的控制;而在业务开发中,业务程序代码可以选择将哪些数据存到redis中去。

总结

  1. mysql本身的缓存数据不由应用程序控制,而应用程序可以控制redis中存储哪些数据,一般都是缓存应用层自定义的热点数据;
  2. 磁盘的访问速度相对内存来说比较慢,应尽量将热点数据放在内存中;
  3. 主要解决读的性能;因为写没必要优化,必须让数据正确的落盘;如果写性能出现问题,那么请使用横向扩展集群方式来解决;
  4. 项目中需要存储的数据应该远大于内存的容量,同时需要进行数据统计分析,所以mysql应该作为主要的数据依据

问题

系统中除了关系型数据库mysql,如果加上了缓存数据库redis,那么就有一个很关键的问题,redis中的数据和mysql数据的同步问题。下面的讨论都是基于热点数据的同步问题

img

为什么有同步的问题?

系统没有引入redis之前,我们对数据的读写都是基于mysql(将mysql内部作为一个整体,不考虑mysq主从数据库或者集群),所以不存在同步问题;

引入redis后,我们对数据的操作需要分别操作redis和 mysql,那么这个时候数据可能存在几个状态?

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

4和5显然是没问题的,我们现在需要考虑1、2以及3。

核心思想:系统主要数据的依据是mysql,所以mysql数据正确就OK了,只需要将mysql的数据正确同步到redis就可以了;同理,redis有,mysql没有,这比较危险,此时我们可以认为该数据为脏数据;所以我们需要在同步策略中避免该情况发生;同时可能存在mysql和redis都有数据,但是数据不一致,这种也需要在同步策略中避免。

redis不可用的话,系统应该要保持正常工作;

mysql不可用的话,应该停止对外提供服务。

另外可以将问题3转化为问题1。

解决数据同步问题

对于读操作,我们都采用一种策略;对于写操作,我们需要考虑对于数据一致性要求的程度,针对强一致性和最终一致性,制定不同的策略。

读策略

先从redis当中获取数据,如果redis不可用,去mysql获取;如果redis有,直接返回;如果redis没有,转而向mysql请求,如果mysql没有,直接返回;如果mysql有,则返回并将数据回写到redis当中;

写策略

强一致性

img

会有两次写redis。

缺点是数据不能频繁修改,效率会降低。

最终一致性

最终一致性,有时候是可以接受的。

img

先修改redis,再写mysql。有个问题,如果在写mysql前,mysql宕机了,如果有客户端去读数据,就会从redis中读,但mysql里面根本没有这个数据。

这个问题是可以接受的,因为mysql如果宕机了,整个系统是需要停止服务的。

重要数据不会作为热点数据,不会存储在redis中。

这种方案性能肯定比上面的方案高。但可能会存在拿到一个错误数据,作为业务逻辑的依据。

主从复制

dml操作会生成两个文件,binlog,redo log。

binlog,Mysql Server层生成的。binlog中存储行信息。

redolog,是事务的,顺序写,物理日志,记录要修改B+树对应磁盘中的起始地址+偏移量+具体内容。是由引擎层Innodb生成的。

mysql主从复制,是通过binlog实现的。

img

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

Mysql同步数据到Redis

原理: 主从复制。一个组件(如go-mysql-transfer)伪装成mysql的从数据库,当mysql主数据库数据有更新时,获取更新数据,并同步到redis中。

img

img

img

go-mysql-transfer使用

  1. mysql和redis的地址;
  2. 定义热点数据;
  3. 使用lua实现热点数据的同步;
  4. 第一次使用.\go-mysql-transfer.exe -stock,进行全量同步;
  5. 通过go-mysql-transfer.exe启动;

之后对mysql的所有dml操作,都会同步到redis中。

异常情况

以上介绍的都是对mysql和redis数据一致性的正常逻辑处理,对于异常情况,我们并没有考虑。下面来看异常情况。

缓存穿透(Cache Penetration)

假设某个数据redis不存在,mysql也不存在,也就是前面介绍的数据5种状态的状态5。有黑客利用它对系统进行攻击,一直尝试读怎么办?缓存穿透,数据最终压力依然堆积在mysql,可能造成mysql不堪重负而崩溃;

解决方法

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

缓存击穿(Hotspot Invalid)

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

解决方法

  1. 通过分布式锁,控制并发请求;
  2. 将很热的key, 设置为不过期。

缓存雪崩(Cache Avalanche)

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

解决方法

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

总结

mysql缓存方案,主要需要考虑redis和mysql数据的5种状态,结合强一致性和最终一致性,以及mysql数据如何同步到redis中,并需要考虑缓存击穿等异常情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值