MySQL缓存策略

文章目录

  • 一、MySQL缓存方案的作用
  • 二、提高MySQL访问性能的方式
    • 2.1 读写分离
      • 2.1.1 是什么?
      • 2.1.2 解决了什么?
      • 2.1.3 原理是什么?
    • 2.2 连接池
      • 2.1.1 是什么?
      • 2.1.2 解决了什么?
      • 2.1.3 原理是什么?
    • 2.3 异步连接
      • 2.1.1 是什么?
      • 2.1.2 解决了什么?
      • 2.1.3 原理是什么?
  • 三、缓存方案
    • 3.1 缓存和MySQL一致性状态分析
    • 3.2 读写策略解决数据同步问题
      • 3.2.1 读策略
      • 3.2.1 写策略
    • 3.3 同步方案
  • 四、缓存故障及解决
    • 4.1 缓存穿透
    • 4.2 缓存击穿
    • 4.3 缓存雪崩
    • 4.4 缓存方案的弊端

一、MySQL缓存方案的作用

首先。我们进行一些具体的场景分析:

1)业务场景中,读的需求远远大于写的需求,因此应当主要关注如何提高读的性能。对于写没必要优化,但必须保证让数据正确的落盘。

2)另外,内存访问速度是磁盘访问速度的10万倍,访问磁盘的速度比较慢,因此尽量使得数据是从内存中获取。

3)项目中需要存储的数据应该远大于内存的容量,同时需要进行数据统计分析,所以数据存储获取的依据应该是关系型数据库,如MySQL 数据主要存储在磁盘当中。

4)MySQL自身缓冲层跟业务无关,这是由于 MySQL的缓冲层不由用户来控制,也就是不能由用户来控制缓存具体数据。MySQL的缓冲策略主要是 LRU。因此引入缓冲层,即需要一个缓存数据库,可以存储用户自定义的热点数据。如redis、memcached。

综合以上几点,MySQL缓存方案是:所有数据存放在主数据库,缓存数据库作为辅助数据库存放自定义的热点数据。这样用户可以直接从缓存获取热点数据,降低主数据库的读压力。

接下来,我们介绍几种提高MySQL访问性能的方式。

二、提高MySQL访问性能的方式

2.1 读写分离

2.1.1 是什么?

读写分离会设置多个从数据库,写操作依然在主数据库,而读操作在从数据库。

需要注意的是,从数据库可能会在多个机器中,主数据库是提供数据的主要依据。如果读操作有强一致性要求,那么还是需要去读主数据库。
在这里插入图片描述

2.1.2 解决了什么?

读写分离提高设置多个从数据库来解决主数据库的读压力。

2.1.3 原理是什么?

读写分离主要依据MySQL的主从复制原理,因为MySQL的主从复制是异步复制的,所以读写分离只能保证数据的最终一致性,不能保证实时一致性。如果读操作有强一致性要求,那么需要读操作去读主数据库。

MySQL主从复制

主从复制流程:

  1. 主库更新事件 ( update 、 insert 、 delete ) 通过 io-thread写到 binlog;
  2. 从库请求读取 binlog,通过 io-thread 写入从库本地 relay-log(中继日志);
  3. 从库通过 sql-thread 读取 relay-log,并把更新事件在从库中重放(replay)一遍;
    在这里插入图片描述

2.2 连接池

2.1.1 是什么?

连接池是在服务端当中创建多个与数据库的连接。参考Linux组件之数据库连接池
在这里插入图片描述

2.1.2 解决了什么?

数据库连接池并发提升了数据库的访问性能。同时复用连接,避免连接建立、断开、以及安全验证带来的额外开销。

2.1.3 原理是什么?

MySQL网络模型(select + 阻塞IO模型)

特别的,如果发送一个事务(多个sql语句),那么这个事务必须在一个连接中执行。

2.3 异步连接

2.1.1 是什么?

异步连接是在服务端创建一个连接,针对这个连接采用非阻塞IO

2.1.2 解决了什么?

异步连接节省了网络传输时间。

2.1.3 原理是什么?

使用了非阻塞IO

三、缓存方案

3.1 缓存和MySQL一致性状态分析

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

  1. MySQL有,缓存无
  2. MySQL无,缓存有
  3. 都有,但数据不一致
  4. 都有,数据一致
  5. 都没有

对于以上的几种情况:

  • 4 和 5显然是没问题的
  • 低于1,我们获取数据的主要依据是 mysql,只需要将mysql 的数据正确同步到缓存数据库就可以了。
  • 对于2,缓存有,mysql 没有,这比较危险,此时我们可以认为该数据为脏数据。需要在同步策略中避免该情况发生。
  • 对于3,mysql 和缓存都有数据,但是数据不一致。这是mysql 同步到 redis 是异步复制,短时间内会出现数据不一致。这种也需要在同步策略中避免。

需要注意的是:缓存不可用,整个系统依然要保持正常工作;mysql 不可用的话,系统停摆,停止对外提供服务;

3.2 读写策略解决数据同步问题

3.2.1 读策略

准确来说,是热点数据读缓存,非热点数据直接读主数据库。

先读缓存
1)若缓存存在,直接返回;
2)若缓存不存在,再访问mysql
∘ \quad \circ 若 mysql 没有,则返回没有
∘ \quad \circ 若 mysql 有,同步数据到redis

3.2.1 写策略

写策略分两种:以安全为主、以效率为主。

1、以安全为主的写策略
先删除redis当中的数据,然后再写MySQL,最后将MySQL数据同步到redis中(交由 go-
mysql-transfer 等中间件处理)。这种策略将状态 3 转化成状态 1;

为什么先删除redis数据?
先删除缓存,是为了避免其他服务读取旧的数据;也是告知系统这个数据已经不是最新,建议从 mysql 获取数据。

存在的问题:
为了安全降低效率,不断删除缓存,使得设置缓存没有了意义。

2、以效率为主的写策略
先写缓存并设置过期时间(如 200ms),再写MySQL,等待MySQL同步到redis中(交由中间件处理)。

这里设置的过期时间是预估时间,大致上是 :
与MySQL网络传输时间 + MySQL处理时间 + MySQL同步到redis的时间

存在的问题:
在过期时间内,如200ms,如果写的过程中 mysql 停止服务,或数据没写入 mysql,则200 ms 内提供了脏数据服务;但仅仅只有 200ms 的数据错乱。

3.3 同步方案

同步方案主要解决如何将 Mysql 数据同步到 redis 中。主要有两种方法:
1)伪装从数据库。比如阿里 canal,go-mysql-transfer 等。
2)触发器 + udf:把热点数据表设置触发器,在触发器中调用 udf,udf 与 redis 建立连接,进行数据同步。udf全称User-defined function,是MySQL提供的一种可扩展代码。UDF不具备事务,不能回滚。总体而言这种方法效率较低,不建议。

3.3 伪装从数据库
3.3.1 阿里canal
Canal可以实时捕获MySQL等数据库中的数据变更,并将这些变更事件传递给redis等缓存数据库,实现数据的实时同步和复制。并且canal会考虑分布式问题,如果一个canal宕机了,会有从canal顶替上来,保证服务正常提供。
在这里插入图片描述
3.3.2 go-mysql-transfer
go-mysql-transfer是一个基于Go语言开发的数据库变更数据传输工具,它可以实时捕获MySQL数据库中的数据变更,并将变更事件传输到给redis等缓存数据库。go-mysql-transfer只有一个节点,相对canal简单些,没有解决分布式问题。

缺点是需要引入etcd、zk等实现高可用。

go-mysql-transfer的项目地址、go-mysql-transfer产品手册

具体流程是:
1)安装 go-mysql-transfer

# 安装 Golang 1.14 及以上版本
wget https://golang.google.cn/dl/go1.17.8.linux-amd64.tar.gz
tar -zxvf go1.17.8.linux-amd64.tar.gz
# 配置
vim /etc/profile
export PATH=$PATH:/opt/go/bin  # 配置 go 环境变量# 安装 go-mysql-transfer
git clone https://gitee.com/mirrors/go-mysql-transfer.git
GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go build

2)修改 mysql 配置文件为主从模式,位置:/etc/mysql/my.cnf

log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和slave_id重复

3)修改 app.yml,配置 mysql 和 redis,配置热点数据

# mysql配置
addr: 127.0.0.1:3306
user: root
pass: 123456
charset : utf8
slave_id: 1001 #slave ID# redis连接配置
redis_addrs: 127.0.0.1:6379 # redis地址,多个用逗号分隔
redis_pass: 123456 # redis密码# 配置热点数据
schema: mark # 数据库名称
table: t_user # 表名称
order_by_column: id #排序字段,存量数据同步时不能为空
column_underscore_to_camel: true # 列名称下划线转驼峰,默认为false
lua_file_path: lua/t_user.lua   # lua脚本文件位置
# redis相关    
redis_structure: hash # 数据类型。

4)编写 Lua 同步逻辑

local ops = require("redisOps") --加载redis操作模块local row = ops.rawRow()  --当前数据库的一行数据,table类型,key为列名称
local action = ops.rawAction()  --当前数据库事件,包括:insert、update、delete-- 同步方法
if action == "insert" or action == "update" then -- 只监听insert事件local id = row["id"] --获取ID列的值local key = "user:" .. idlocal name = row["nick"] --获取USER_NAME列的值local sex = row["sex"]local height = row["height"] --获取PASSWORD列的值local age = row["age"]ops.HSET(key, "id", id) -- 对应Redis的HSET命令ops.HSET(key, "nick", name) -- 对应Redis的HSET命令ops.HSET(key, "sex", sex) -- 对应Redis的HSET命令ops.HSET(key, "height", height) -- 对应Redis的HSET命令ops.HSET(key, "age", age) -- 对应Redis的HSET命令
elseif action == "delete" thenlocal id = row['id']local key = "user:" .. idops.DEL(key)
end

5)启动 mysql, redis, go-mysql-transfer

# 全量数据同步,初次启动
./go-mysql-transfer -stock
# 启动
nohup go-mysql-transfer &

四、缓存故障及解决

4.1 缓存穿透

如果某个数据在redis缓存和MySQL中都不存在,但此时一直尝试读这个不存在的数据,最后数据压力堆积在MySQL,可能会造成MySQL崩溃。

例如恶意攻击者可以通过构造大量不存在的查询请求来压垮数据库。

解决办法:
1)缓存设置<key,nil>:当发现MySQL不存在某个数据,将redis中对应的key设置为<key,nil>并设置过期时间。通过这样的标识,使得下次访问key的时候不要再去访问MySQL,并且到期自动删除这个key。但是这种方法会造成redis缓存数据库缓存很多无效数据,浪费内存。

2)部署布隆过滤器:将 MySQL当中已经存在的 key,写入布隆过滤器,不存在的直接 pass 掉。即使发生了缓存穿透,通过布隆过滤器在缓存层拦截无效的请求,避免无效查询到达MySQL。最好在缓存数据库上部署布隆过滤器。

4.2 缓存击穿

如果某个频繁访问的热点数据在redis缓存不存在(过期或被淘汰),但在MySQL中存在。此时有大量的并发连接请求该热点数据,会直接访问数据库,导致MySQL数据库压力骤增,可能造成MySQL数据库崩溃

解决办法:
1)过热数据不过期,即不要对频繁访问的热点数据设置过期时间。

2)使用分布式锁机制,保证只有一个请求能够访问数据库,其他请求在等待时可以从缓存中获取数据。

4.3 缓存雪崩

redis缓存中的大量数据同时过期或失效,但是在MySQL中存在,导致大量请求直接访问MySQL数据库,造成系统性能下降甚至崩溃。

缓存数据库在整个系统不是必须的,也就是缓存宕机不会影响整个系统提供服务。
解决办法:
1)如果因为缓存数据库宕机,造成所有数据涌向 MySQL。采用高可用的集群方案,如哨兵模式、cluster模式。

2)如果因为设置了相同的过期时间,造成缓存集中失效。设置随机过期值或者其他机制错开失效时间。

3) 如果因为系统重启的时候,造成缓存数据消失。重启时间短,redis 开启持久化(过期信息也会持久化)就行了; 重启时间长,提前将热数据导入 redis 当中。

4.4 缓存方案的弊端

不能处理多语句事务。这是因为redis缓存数据库不支持回滚,造成redis 缓存数据库 与MySQL存储数据库数据不一致。


注:本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接,详细查看详细的服务器课程链接 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/85030.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人CPP编程基础-03变量类型Variables Types

机器人CPP编程基础-02变量Variables 全文AI生成。 C #include<iostream>using namespace std;main() {int a10,b35; // 4 bytescout<<"Value of a : "<<a<<" Address of a : "<<&a <<endl;cout<<"Val…

W5500-EVB-PICO 做TCP Server进行回环测试(六)

前言 上一章我们用W5500-EVB-PICO开发板做TCP 客户端连接服务器进行数据回环测试&#xff0c;那么本章将用开发板做TCP服务器来进行数据回环测试。 TCP是什么&#xff1f;什么是TCP Server&#xff1f;能干什么&#xff1f; TCP (Transmission Control Protocol) 是一种面向连…

NOSQL——redis的安装,配置与简单操作

目录 一、缓存的相关知识 1&#xff09;缓存的概念 2&#xff09;系统缓存 buffer与cache&#xff1a; 3&#xff09;缓存保存位置及分层结构 DNS缓存 应用层缓存 数据层缓存 分布式缓存服务&#xff1a; 数据库&#xff1a; 硬件缓存 二、关系型数据与非关系型数据…

晨控CK-GW06-E01与汇川H5U系列PLC通讯手册

晨控CK-GW06-E01与汇川H5U系列PLC通讯手册 晨控CK-GW06-E01是一款支持标准工业通讯协议 EtherNet IP 的网关控制器,方便用户集成到PLC等控制系统中。本控制器提供了网络 POE 供电和直流电源供电两种方式&#xff0c;确保用户在使用无 POE 供电功能的交换机时可采用外接电源供电…

-bash: ./startup.sh: Permission denied解决

今天在Linux上启动Tomcat&#xff0c;结果弹出&#xff1a;-bash: ./startup.sh: Permission denied 的提示。 这是因为用户没有权限&#xff0c;而导致无法执行。用命令chmod 修改一下bin目录下的.sh权限就可以了。 在Tomcat的bin目录下 &#xff0c;输入命令行 &#xff1a;c…

竞赛项目 深度学习实现语义分割算法系统 - 机器视觉

文章目录 1 前言2 概念介绍2.1 什么是图像语义分割 3 条件随机场的深度学习模型3\. 1 多尺度特征融合 4 语义分割开发过程4.1 建立4.2 下载CamVid数据集4.3 加载CamVid图像4.4 加载CamVid像素标签图像 5 PyTorch 实现语义分割5.1 数据集准备5.2 训练基准模型5.3 损失函数5.4 归…

Linux 内存管理新特性 - Memory folios 解读 | 龙蜥技术

本文内容基于 Linux 5.16&#xff0c;folio 基础部分开始合入。截止到目前 Linux 6.5&#xff0c;folio 已经有很大进展&#xff0c;会在后续文章中介绍。作者&#xff1a;徐宇。 01 folio [ˈfoʊlioʊ] 是什么 引用 LWN: Memory folios &#xff1a;https://lwn.net/Articl…

基于大模型的数据血缘异常归因分析

近日&#xff0c;以“元数据技术及应用创新”为主题&#xff0c;最新一季StartDT Hackathon&#xff08;奇点云黑客马拉松&#xff09;正式收官。 本期黑客松共吸引了近50位选手参赛&#xff0c;有的在实时数仓领域显神通&#xff0c;有的则再次请出了大模型。这些小组都有个共…

利用自动校对软件优化新闻稿件的拼写和语法

利用自动校对软件优化新闻稿件的拼写和语法&#xff0c;您可以按照以下步骤进行&#xff1a; 1.选择适合的校对软件&#xff1a;市场上有多种拼写和语法校对软件可供选择。根据您的需求和预算&#xff0c;选择一个功能强大且适合新闻稿件的软件。 2.导入稿件&#xff1a;将待校…

日常BUG ——乱码

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 A系统使用Feign调用B系统时&#xff0c;传递的String字符串&#xff0c;到了B系统中变为了乱…

Flutter:屏幕适配

flutter_screenutil flutter_screenutil是一个用于在Flutter应用程序中进行屏幕适配的工具包。它旨在帮助开发者在不同屏幕尺寸和密度的设备上创建响应式的UI布局。 flutter_screenutil提供了一些用于处理尺寸和间距的方法&#xff0c;使得开发者可以根据设备的屏幕尺寸和密度…

2023年游戏买量能怎么玩?

疫情过后&#xff0c;一地鸡毛。游戏行业的日子也不好过。来看看移动游戏收入&#xff1a;2022年&#xff0c;移动游戏收入达到920亿美元&#xff0c;同比下降6.4%。这告诉我们&#xff0c;2022年对移动游戏市场来说是一个小挫折。 但不管是下挫还是上升&#xff0c;移动游戏市…

Git 快速入门

Git 快速入门 文章目录 Git 快速入门一、代码托管平台&#xff08;远程仓库&#xff09;二、安装Git三、Git的命令实践Git 的四个区域Git 管理代码的3个场景Git 工作区的理念Git 工作区的生命周期Git 版本回退Git 文件重命名Git查看版本提交日志Git StashGit分支Git标签 四、创…

新利好带动 POSE 持续上扬,月内几近翻倍

PoseiSwap是Nautilus Chain上的首个DEX&#xff0c;得益于Nautilus Chain的模块化Layer3构架&#xff0c;PoseiSwap正在基于zk-Rollup方案构建全新的应用层&#xff0c;并基于此构建隐私、合规等全新的特性&#xff0c;为未来其布局RWA领域推动Web2、Web3世界的进一步融合构建基…

【Docker晋升记】No.1--- Docker工具核心组件构成(镜像、容器、仓库)及性能属性

文章目录 前言&#x1f31f;一、Docker工具&#x1f31f;二、Docker 引擎&#x1f30f;2.1.容器管理&#xff1a;&#x1f30f;2.2.镜像管理&#xff1a;&#x1f30f;2.3.资源管理&#xff1a;&#x1f30f;2.4.网络管理&#xff1a;&#x1f30f;2.5.存储管理&#xff1a;&am…

(二)结构型模式:2、桥接模式(Bridge Pattern)(C++实现示例)

目录 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 2、桥接模式应用场景 3、桥接模式的UML图学习 4、C实现桥接模式的示例 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 桥接模式是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#…

【算法】逆波兰表达式

文章目录 定义求法代码思想&#xff1a; 定义 逆波兰表达式也称为“后缀表达式”&#xff0c;是将运算符写在操作数之后的运算式。 求法 *如&#xff1a;(ab)c-(ab)/e的转换过程&#xff1a; 先加上所有的括号。 (((ab)*c)-((ab)/e))将所有的运算符移到括号外面 (((ab) c)* …

A33 QT 主线例程 opengl

点击查看 HW33-050 HW33-070 规格书 HW33-050 HW33-070 支持 android 系统和 Linux QT。 HW33-XXX采用4 核Cortex-A7 ARM、Mali400MP2 GPU架构&#xff0c;主频 1.2GHz 的 CPU。内存 存储标配分别为1GB、8GB&#xff0c;内置显卡为Mali400MP2&#xff0c;支持 H.264 1080P …

2023一带一路东盟工商领袖峰会在曼谷成功举行,发明家周初材被授予中泰友好交流大使

今年是共建“一带一路”倡议提出十周年。十年来&#xff0c;共建“一带一路”倡议从理念到行动&#xff0c;从愿景到现实&#xff0c;开展更大范围、更高水平、更深层次的区域合作&#xff0c;致力于维护全球自由贸易体系和开放型世界经济&#xff0c;推动文明交流互鉴&#xf…

uniapp input输入框placeholder文本右对齐

input输入框placeholder文本右对齐 给input标签加上placeholder-class&#xff0c;这个是给placeholder设置样式&#xff0c;右对齐这就是text-align:right;字体颜色之类依次编辑即可。