mysql理解(二)_缓存

MySql主从复制

mysql理解(二)_缓存_第1张图片

1.主库更新事件(update,insert,delete)通过io-thread写入binglog;

2.从库读取binglog,通过io-thread写入(write)从库本地relay log(中继日志);

3.从库通过sql-thread读取(read)relay log,并把更新事件在从库中执行(replay)一遍

例如微博读写分离架构如下图

mysql理解(二)_缓存_第2张图片

为什么需要缓冲层?
前提
读多写少,单个主节点能⽀撑项⽬数据量;数据的主要依据是mysql;
mysql
mysql有缓冲层,它的作⽤也是⽤来缓存热点数据,这些数据包括数据⽂件、索引⽂件等;mysql缓冲层是从⾃身出发,跟具体的业务⽆关;这⾥的缓冲策略主要是lru,当然是经过优化的lru;
mysql数据主要存储在磁盘当中,适合⼤量重要数据的存储;磁盘当中的数据⼀般是远⼤于内存当中的数据;mysql是关系型数据库,⽅便 OLTP 进⾏统计分析;⼀般业务场景关系型数据库(mysql)作为主要数据库;
缓冲层
缓存数据库可以选⽤redis,memcached;它们所有数据都存储在内存当中,当然也可以将内存当中的数据持久化到磁盘当中;内存的数据和磁盘的数据是⼀⽐⼀的;

存储⽐较

mysql理解(二)_缓存_第3张图片

⼏项重要的数据
1. 内存的访问速度是磁盘访问速度的10万倍(数量级倍率);内存的访问速度⼤约是100ns,⽽⼀次磁盘访问⼤约是10ms;访问mysql时访问磁盘的次数跟b+树的⾼度相关;
2. ⼀般⼤部分项⽬中,数据库读操作是写操作的10倍左右;
总结
1. 由于mysql的缓冲层不由⽤户来控制,也就是不能由⽤户来控制缓存具体数据;
2. 访问磁盘的速度⽐较慢,尽量获取数据从内存中获取;
3. 主要解决读的性能;因为写没必要优化,必须让数据正确的落盘;如果写性能出现问题,那么请使⽤横向扩展集群⽅式来解决;
4. 项⽬中需要存储的数据应该远⼤于内存的容量,同时需要进⾏数据统计分析,所以数据存储获取的依据应该是关系型数据库;
5. 缓存数据库可以存储⽤户⾃定义的热点数据;以下的讨论都是基于热点数据的同步问题;

原理图

mysql理解(二)_缓存_第4张图片

为什么有同步的问题?

没有缓冲层之前,我们对数据的读写都是基于mysql;所以不存在同步问题;这句话也不是必然,⽐如读写分离就存在同步问题(数据⼀致性问题);
引⼊缓冲层后,我们对数据的获取需要分别操作缓存数据库和mysql;那么这个时候数据可能存在
⼏个状态?
1. mysql有,缓存⽆
2. mysql⽆,缓存有
3. 都有,但数据不⼀致
4. 都有,数据⼀致
5. 都没有
4和5显然是没问题的,我们现在需要考虑1、2以及3;
⾸先明确⼀点:我们获取数据的主要依据是mysql,所以mysql数据正确就万事⼤吉,只需要将mysql的数据正确同步到缓存数据库就可以了;同理,缓存有,mysql没有,这⽐较危险,此时我们可以认为该数据为脏数据;所以我们需要在同步策略中避免该情况发⽣;同时可能存在mysql和缓存都有数据,但是数据不⼀致,这种也需要在同步策略中避免;缓存不可⽤的话,我们整个系统应该要保持正常⼯作;mysql不可⽤的话,应该停⽌对外提供服务;另外可以将问题 3 转化为问题 1;

尝试解决同步(⼀致性)


主要数据存储在mysql当中,所以先写mysql,如果mysql不可⽤,直接返回;mysql写成功后,
再将数据同步给redis就⾏了,如果此时redis不可⽤,应该怎么做?

先从redis当中获取数据,如果redis不可⽤,直接去mysql获取;如果redis有,直接返回;如果
redis没有,转⽽向mysql请求,如果mysql没有,直接返回;如果MySQL有,则返回并将数据回
写到redis当中;
总结
1. 业务层引⼊了两个变化⽅向,尽量减少redis的流程;
2. 业务层控制热数据流程;回写流程控制热数据流程;
3. 热数据不是总是热数据;需要将热数据设置超时时间;

解决数据同步问题
⼀致性问题
强⼀致性
同步是否成功的依据来源于mysql是否同步到redis,即使没有同步成功,也没关系;写流程:先删除缓存,再写mysql,后⾯数据的同步交由go-mysql-transfer;先删除缓存,为了避免其他服务读取旧的数据;也是告知系统这个数据已经不是最新,建议从mysql获取数据;
那么在删除缓存和写mysql之间发⽣读取mysql⾏为,是否是不⼀致⾏为?
如下图,可以理解连续两个sql操作,发起请求前后顺序并不能解读为数据的⼀致性;但是对于服务A⽽⾔,写⼊mysql后,接着读操作必须要能读到最新的数据;

mysql理解(二)_缓存_第5张图片

mysql理解(二)_缓存_第6张图片

最终⼀致性
读写分离,主库将数据同步到从库,是需要时间,那么在同步期间,主从之间数据有差异;
这⾥有写两种⽅案:
第⼀种:直接写mysql,等待mysql同步数据到redis;
第⼆种:先写redis,设置key的过期时间为200ms(经验值),等待mysql回写redis,覆盖key,
设置更⻓的过期时间;
200ms 默认的是 写mysql到mysql同步到redis的时⻓;这个需要根据实际环境进⾏设置;

mysql理解(二)_缓存_第7张图片

 

mysql理解(二)_缓存_第8张图片

 

数据同步方案二

mysql理解(二)_缓存_第9张图片

该方案用到的源码

git clone https://gitee.com/mirrors/go-mysql-transfer.git

缓存三大问题

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

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

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

mysql理解(二)_缓存_第10张图片

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

 

缓存雪崩
表示⼀段时间内,缓存集中失效(redis⽆ mysql 有),导致请求全部⾛mysql,有可能搞垮数据库,使整个服务失效;
解决
1. 如果因为缓存数据库宕机,造成所有数据涌向mysql;采⽤⾼可⽤的集群⽅案,如哨兵模式、cluster模式;
2. 如果因为设置了相同的过期时间,造成缓存集中失效;设置随机过期值或者其他机制错开失效;
3. 如果因为系统重启的时候,造成缓存数据消失;重启时间短,redis开启持久化(过期信息也会持久化)就⾏了; 重启时间⻓提前将热数据导⼊redis当中;

 

 

 

你可能感兴趣的:(网络编程)