哪些网站可以赚钱,怎样做关键词排名优化,wordpress显示文章标题,设计本源文章目录 一、MySQL缓存方案的作用二、提高MySQL访问性能的方式2.1 读写分离2.1.1 是什么#xff1f;2.1.2 解决了什么#xff1f;2.1.3 原理是什么#xff1f; 2.2 连接池2.1.1 是什么#xff1f;2.1.2 解决了什么#xff1f;2.1.3 原理是什么#xff1f; 2.3 异步连接2… 文章目录 一、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 数据主要存储在磁盘当中。
4MySQL自身缓冲层跟业务无关这是由于 MySQL的缓冲层不由用户来控制也就是不能由用户来控制缓存具体数据。MySQL的缓冲策略主要是 LRU。因此引入缓冲层即需要一个缓存数据库可以存储用户自定义的热点数据。如redis、memcached。
综合以上几点MySQL缓存方案是所有数据存放在主数据库缓存数据库作为辅助数据库存放自定义的热点数据。这样用户可以直接从缓存获取热点数据降低主数据库的读压力。
接下来我们介绍几种提高MySQL访问性能的方式。
二、提高MySQL访问性能的方式
2.1 读写分离
2.1.1 是什么
读写分离会设置多个从数据库写操作依然在主数据库而读操作在从数据库。
需要注意的是从数据库可能会在多个机器中主数据库是提供数据的主要依据。如果读操作有强一致性要求那么还是需要去读主数据库。
2.1.2 解决了什么
读写分离提高设置多个从数据库来解决主数据库的读压力。
2.1.3 原理是什么
读写分离主要依据MySQL的主从复制原理因为MySQL的主从复制是异步复制的所以读写分离只能保证数据的最终一致性不能保证实时一致性。如果读操作有强一致性要求那么需要读操作去读主数据库。
MySQL主从复制
主从复制流程
主库更新事件 ( update 、 insert 、 delete ) 通过 io-thread写到 binlog从库请求读取 binlog通过 io-thread 写入从库本地 relay-log中继日志从库通过 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那么这个时候数据可能存在几个状态
MySQL有缓存无MySQL无缓存有都有但数据不一致都有数据一致都没有
对于以上的几种情况
4 和 5显然是没问题的低于1我们获取数据的主要依据是 mysql只需要将mysql 的数据正确同步到缓存数据库就可以了。对于2缓存有mysql 没有这比较危险此时我们可以认为该数据为脏数据。需要在同步策略中避免该情况发生。对于3mysql 和缓存都有数据但是数据不一致。这是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伪装从数据库。比如阿里 canalgo-mysql-transfer 等。 2触发器 udf把热点数据表设置触发器在触发器中调用 udfudf 与 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
GO111MODULEon
go env -w GOPROXYhttps://goproxy.cn,direct
go build2修改 mysql 配置文件为主从模式位置/etc/mysql/my.cnf
log-binmysql-bin # 开启 binlog
binlog-formatROW # 选择 ROW 模式
server_id1 # 配置 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/clinux课程感兴趣的读者可以点击链接,详细查看详细的服务器课程链接 。