MySQL缓存策略(一致性问题、数据同步以及缓存故障)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: MySQL缓存策略(一致性问题、数据同步以及缓存故障)

一、数据库有哪些提升读写性能的方式呢?


  1. 连接池:阻塞io+线程池
  2. 异步连接:非阻塞io
  3. 从sql执行出发:即时执行+预编译执行(可以直接跳过句法分析、优化器等步骤,执行速度更快)
  4. 读写分离
  5. 缓存方案

1、连接池和异步连接

左边是多个线程来操作数据库和缓存,可以加上代理层proxy统一管理

或者使用dbserver(代理层)去代理访问数据库

skynet采用异步连接的方式,节约了网络传输的时间。

openresty采用的是连接池的方式

2、读写分离

主数据库处理写操作,而从数据库处理查询的操作,数据库的一致性则通过主从复制来实现

适用于读远比写多的场景,分摊服务器压力,提高机器的系统处理效率

主从复制这种集群方式:解决了单点故障问题(比如主数据库宕机了,就可以从 从数据库去获得备份)

主从复制还可以实现读写分离,将读写操作,分配到主从数据库中,分摊服务器压力

但是这个方案,由于主从复制并不能保证同时复制到从数据库中,因而可能在中途出现读取数据不一致的问题,但是最终是一致的(具体要看业务使用场景)。

下面介绍主从复制的原理

主从复制

原理图

  1. 主库更新事件(update、insert、delete)通过 io-thread 写到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端真实执行时候的那些可执行的内容,并在自身执行。
    写的时候写主数据库,读的时候读从数据库

二、为什么需要缓冲层?

1、前提

读多写少,单个主节点能支撑项目数据量;数据的主要依据是 mysql;

2、mysql

mysql 有缓冲层,它的作用也是用来缓存热点数据,这些数据包括数据文件、索引文件等;mysql

缓冲层是从自身出发,跟具体的业务无关;这里的缓冲策略主要是 lru,当然是经过优化的 lru;

mysql数据主要存储在磁盘当中,适合大量重要数据的存储;磁盘当中的数据一般是远大于内存当

中的数据;一般业务场景关系型数据库(mysql)作为主要数据库;

3、缓冲层

缓存数据库可以选用redis,memcached;它们所有数据都存储在内存当中,当然也可以将内存

当中的数据持久化到磁盘当中;内存的数据和磁盘的数据是一比一的;

4、存储比较

5、几项重要的数据

  1. 内存的访问速度是磁盘访问速度的10万倍(数量级倍率);内存的访问速度大约是100ns,
    而一次磁盘访问大约是10ms;访问mysql时访问磁盘的次数跟b+树的高度相关;
  2. 一般大部分项目中,数据库读操作是写操作的10倍左右;

6、总结

  1. 由于mysql的缓冲层不由用户来控制,也就是不能由用户来控制缓存具体数据;
  2. 访问磁盘的速度比较慢,尽量获取数据从内存中获取;
  3. 主要解决读的性能;因为写没必要优化,必须让数据正确的落盘;如果写性能出现问题,那
    么请使用横向扩展集群方式来解决;
  4. 项目中需要存储的数据应该远大于内存的容量,同时需要进行数据统计分析,所以数据存储
    获取的依据应该是关系型数据库;
  5. 缓存数据库可以存储用户自定义的热点数据;以下的讨论都是基于热点数据的同步问题;

三、引入缓存

在没有引入缓存前,是服务器直接访问mysql,由于mysql的读写性能(磁盘io较慢)问题,额外再使用一个缓存数据库

原理图

为了解决mysql的读写性能(磁盘io较慢)问题,增加了缓存数据库(cache,比如redis),主要用于缓存用户的热点数据(内存访问的速度相比磁盘快很多)。


mysql是主要数据库,数据主要依据

cache缓存用户的热点数据

mysql中的缓冲池和这里的缓存的区别?

mysql中也有缓冲池,但是它存储的mysql的索引等数据,是mysql内部的热点数据。

这里的缓存cache是为了缓存业务中用户的热点数据。

例子:

比如12点钟有个秒杀活动,12点会涌入大量用户,但是12点前几乎没什么人登入,从而mysql中没有相应的缓存热点数据,为了能使得12点用户正常登入,因此要提前缓存用户的热点数据,避免大量用户涌入,mysql磁盘读写的性能直接影响到的了整个系统的响应能力。

需要考虑的问题

在没有缓存cache的时候,仅有一个mysql,(读:直接读mysql,写:直接写mysql)那么去访问很容易,现在有了mysql和cache,因此需要指定相应的缓存策略

四、为什么有同步的问题?

没有缓冲层之前,我们对数据的读写都是基于 mysql;所以不存在同步问题;这句话也不是必

然,比如读写分离就存在同步问题(数据一致性问题);

引入缓冲层后,我们对数据的获取需要分别操作缓存数据库和 mysql;那么这个时候数据可能存

在几个状态?

针对热点数据讨论,热点数据有以下几种状态

1.mysql有,cache没有(正常的情况下,可以通过策略去避免它)

2.mysql没有,cache有(不正常,避免)

3.mysql和cache都有,数据不一致(不正常,避免)

4.mysql和cache都有,数据一致(策略最终要实现的目的)

5.mysql和cache都没有(正常)

五、尝试解决同步(一致性)

制定读写策略

读策略:

1.先看cache是否有数据,如果有直接返回

2.如果没有,去访问mysql

3.如果mysql有,缓存数据传cache

4.如果mysql中没有,说明数据没有

写策略:

有 删除、修改、插入 这几种操作。

在写策略中,会出现一致性的问题

六、一致性问题以及如何解决

1、强一致性

强一致性(任何时候都要保证访问mysql和cache中对应数据的一致):当cache中数据不存在的时候,就会去访问mysql,也能保证强一致性。具体选择哪种一致性,跟业务逻辑有关)

也就是说,不能出现 1.mysql没有,cache有 2.mysql和cache都有,数据不一致

这种情况的出现

缓存和数据库一致性问题

实现强一致性的方案

删除:先去删除缓存(避免出现.mysql没有,cache有这种情况),

修改: 删除缓存,再去修改mysql(调换顺序,如果先修改mysql可能会出现,读到的可能是缓存中还没更新的数据,导致缓存和mysql中数据不一致问题)

插入:删除缓存,再去修改mysql(新数据避免缓存里已经有了这个数据,也是要先删除缓存)

上述这样操作后变成了(mysql有,cache没有)情况,cache中删除了,而我们最终要达到的目的是(mysql和cache都有,数据一致),因此要将mysql中的数据同步到cache(redis)当中。

即使是mysql中数据同步到cache中,有时间延迟,但是顺序是一致的,如下方的黑色(mysql中数据)和红色时间线(cache中数据),如果有数据还未到达cache,那么会直接访问mysql,能保证强一致性。

Redis缓存和MySQL数据一致性的后端高并发架构解决方案

2、最终一致性

读写分离,主库将数据同步到从库,是需要时间,那么在同步期间,主从之间数据有差异;

这里有写两种方案:

第一种:直接写mysql,等待mysql同步数据到redis;

第二种:先写redis,设置key的过期时间为200ms(经验值),等待mysql回写redis,覆盖key,

设置更长的过期时间;

200ms 默认的是 写mysql到mysql同步到redis的时长;这个需要根据实际环境进行设置;

最终一致性(开始不一致,最终访问mysql和cache中对应数据达成一致):在修改过程中,读取数据可能会出现mysql和cache中数据不一致的问题

如果读写分离,一致性要求高

第一种:主从半同步辅助

第二种:直接去主数据读取

实现最终一致性的方案:给缓存设置失效时间

1)先更新缓存,后更新数据库

写的时候往缓存中写,同时设置缓存的失效时间,失效后再刷入数据库

在性能方面,比方案一的性能要高。(这是先更新缓存,后更新数据库的情况,但是可能会出现问题)

2)先更新数据库,后更新缓存

写的时候只往数据库mysql写,刷新(同步)到cache中,同时,写入缓存中的数据,都设置失效时间(这样后续更新该数据的时候,不需要删除这一步骤了,缓存中失效删除后,会直接访问mysql从而保证强一致性)

七、如何实现数据同步呢?

在读取数据的时候,如果redis中没有相应的数据,就会将mysql中的数据同步到redis中,那么如何实现这个数据同步呢?

也就是下图中,虚线那个过程

数据同步方案

对于 触发器+UDF(user define function),不建议使用,会出现异常数据,效率非常低

1、数据同步方案

中间采用 一个组件 使得数据同步

mysql发送数据给canel

然后redis从canel客户端取数据

还有一种方案是

实现的原理就是,将中间件(go-mysql-transfer)伪装成一个从数据库,通过主从复制数据,然后再用中间件同步给redis

既然已经复制到 从数据库了

那么如果将数据通过 从数据库(中间件) 同步到redis呢?

首要对于中间件部分,它需要知道

1.mysql和redis的连接地址

2.热点数据是什么?定义热点数据

3.定义热点数据同步

八、缓存故障

1、缓存穿透

假设某个数据redis不存在,mysql也不存在,而且一直尝试读怎么办?缓存穿透,数据最终压力

依然堆积在mysql,可能造成mysql不堪重负而崩溃;

解决

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

2、缓存击穿

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

过大

解决

  1. 加锁
    请求数据的时候获取锁,如果获取成功,则操作,获取失败,则休眠一段时间(200ms)再去获
    取;获取成功,则释放锁
    首先读redis,不存在,读mysql,存在,写redis key的锁
    整个流程走完,才让后面的服务器访问
    (当同个业务不同线程访问redis未命中时,先获取一把互斥锁,然后进行数据库操作,此时另外一个线程未命中时,拿不到锁,等待一段时间后重新查询缓存,此时之前的线程已经重新把数据加载到redis之中了,线程二就直接缓存命中。这样就不会使得大量访问进入数据库)
  2. 将很热的key,设置不过期

3、缓存雪崩

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

使整个服务失效;

解决

缓存数据库在整个系统不是必须的,也就是缓存宕机不会影响整个系统提供服务;

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

缓存雪崩和缓存击穿的区别?

缓存雪崩和缓存击穿 都是数据在 mysql有,redis无 的情况,

缓存击穿是指1个key并发访问mysql

缓存雪崩是指大量或者全部的key并发访问mysql

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
53 3
|
29天前
|
SQL 缓存 关系型数据库
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴因未能系统梳理MySQL缓存机制而在美团面试中失利。为此,尼恩对MySQL的缓存机制进行了系统化梳理,包括一级缓存(InnoDB缓存)和二级缓存(查询缓存)。同时,他还将这些知识点整理进《尼恩Java面试宝典PDF》V175版本,帮助大家提升技术水平,顺利通过面试。更多技术资料请关注公号【技术自由圈】。
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
|
1月前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
63 10
|
27天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
75 4
|
27天前
|
缓存 NoSQL 关系型数据库
mysql和缓存一致性问题
本文介绍了五种常见的MySQL与Redis数据同步方法:1. 双写一致性,2. 延迟双删策略,3. 订阅发布模式(使用消息队列),4. 基于事件的缓存更新,5. 缓存预热。每种方法的实现步骤、优缺点均有详细说明。
|
28天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
1月前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
45 4
|
2月前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
2月前
|
存储 缓存 监控
|
2月前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
70 2

热门文章

最新文章