redis在启动之后,从数据库加载数据。
创新互联自2013年创立以来,先为繁昌等服务建站,繁昌等地企业,进行企业商务咨询服务。为繁昌企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
读请求:
不要求强一致性的读请求,走redis,要求强一致性的直接从mysql读取
写请求:
数据首先都写到数据库,之后更新redis(先写redis再写mysql,如果写入失败事务回滚会造成redis中存在脏数据)
在并发不高的情况下,读操作优先读取redis,不存在的话就去访问MySQL,并把读到的数据写回Redis中;写操作的话,直接写MySQL,成功后再写入Redis(可以在MySQL端定义CRUD触发器,在触发CRUD操作后写数据到Redis,也可以在Redis端解析binlog,再做相应的操作)
在并发高的情况下,读操作和上面一样,写操作是异步写,写入Redis后直接返回,然后定期写入MySQL
1.当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,而缓存是100,这导致数据库和缓存不一致。
解决方法:
这种情况应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。
2.在高并发的情况下,如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况
解决方法:
遇到这种情况,可以用队列的去解决这个问,创建几个队列,如20个,根据商品的ID去做hash值,然后对队列个数取摸,当有数据更新请求时,先把它丢到队列里去,当更新完后在从队列里去除,如果在更新的过程中,遇到以上场景,先去缓存里看下有没有数据,如果没有,可以先去队列里看是否有相同商品ID在做更新,如果有也把查询的请求发送到队列里去,然后同步等待缓存更新完成。
这里有一个优化点,如果发现队列里有一个查询请求了,那么就不要放新的查询操作进去了,用一个while(true)循环去查询缓存,循环个200MS左右,如果缓存里还没有则直接取数据库的旧数据,一般情况下是可以取到的。
1、读请求时长阻塞
由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时间内返回,该解决方案最大的风险在于可能数据更新很频繁,导致队列中挤压了大量的更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库,像遇到这种情况,一般要做好足够的压力测试,如果压力过大,需要根据实际情况添加机器。
2、请求并发量过高
这里还是要做好压力测试,多模拟真实场景,并发量在最高的时候QPS多少,扛不住就要多加机器,还有就是做好读写比例是多少
3、多服务实例部署的请求路由
可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,都通过nginx服务器路由到相同的服务实例上
4、热点商品的路由问题,导致请求的倾斜
某些商品的读请求特别高,全部打到了相同的机器的相同丢列里了,可能造成某台服务器压力过大,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是很大,但是确实有可能某些服务器的负载会高一些。
img
搜索微信号(ID:芋道源码),可以获得各种 Java 源码解析。
并且,回复【书籍】后,可以领取笔者推荐的各种 Java 从入门到架构的书籍。
用 pt-table-checksum 时,会不会影响业务性能?
实验
实验开始前,给大家分享一个小经验:任何性能评估,不要相信别人的评测结果,要在自己的环境上测试,并(大概)知晓原理。
我们先建一对主从:
然后用 mysqlslap跑一个持续的压力:
开另外一个会话,将 master 上的 general log 打开:
然后通过 pt-table-checksum 进行一次比较:
查看 master 的 general log,由于 mysqlslap 的影响,general log 中有很多内容,我们找到与 pt-table-checksum 相关的线程:
将该线程的操作单独列出来:
操作比较多,我们一点一点来说明:
这里工具调小了 innodb 锁等待时间。使得之后的操作,只要在 innodb 上稍微有锁等待,就会马上放弃操作,对业务影响很小。
另外工具调小了 wait_timeout 时间,倒是没有特别的作用。
工具将隔离级别调整为了 RR 级别,事务的维护代价会比 RC 要高,不过后面我们会看到工具使用的每个事务都很小,加上之前提到 innodb 锁等待时间调到很小,对线上业务产生的成本比较小。
RR 级别是数据对比的基本要求。
工具通过一系列操作,了解表的概况。工具是一个数据块一个数据块进行校验,这里获取了第一个数据块的下边界。
接下来工具获取了下一个数据块的下边界,每个 SQL前都会 EXPLAIN 一下,看一下执行成本,非常小心翼翼。
之后工具获取了一个数据块的 checksum,这个数据块不大,如果跟业务流量有冲突,会马上出发 innodb 的锁超时,立刻退让。
以上是 pt-table-checksum 的一些设计,可以看到这几处都是精心维护了业务流量不受影响。
工具还设计了其他的一些机制保障业务流量,比如参数 --max-load 和 --pause-file 等,还有精心设计的数据块划分方法,索引选择方法等。大家根据自己的情况配合使用即可达到很好的效果。
总结
本期我们介绍了简单分析 pt-table-checksum 是否会影响业务流量,坊间会流传工具的各种参数建议或者不建议使用,算命的情况比较多,大家都可以用简单的实验来分析其中机制。
还是那个观点,性能测试不能相信道听途说,得通过实验去分析。
以mysql来说,可能出现脏读、不可重复读以及幻读,mysql默认设置是可重复读,即一次事务中不会读取到不同的数据。
可以做如下操作:
1)打开两个客户端,均设置为RR;
2)在一个事务中,查询某个操作查到某份数据;比如是某个字段version=1存在数据;
3)在另一个事务中,删除这份version=1的数据;删除后,在2所属的事务中查询数据是没有变化的,还是存在version=1的数据;
4)当我们在2所属的事务中继续更新数据,那么会发现更新不了,明明我们就看到了这份version=1的数据;
缓存一致性:
缓存一致,与什么一致?是与数据库一致,对外查询每个时刻一致;所以在针对于缓存与数据库之间该先更新哪一个呢?可能有人觉得我先更新数据库,再更新缓存不就行了吗?但是有想过个问题吗?
当用户已经支付成功了,更新到数据库,但是呢?你还在缓存中显示未支付,在用户点击频率很高并且数据库压力过大,来不及同步到缓存时,那你是不是很尴尬,这就是典型的不一致了。此时用户再支付,那你又告诉他已经支付了,那他会把你骂死的
那该怎么来做呢?我们可以这样,先更新缓存再更新数据库,那么存在什么问题呢?
1)缓存更新成功,但是数据库更新失败,而被其它的并发线程访问到
2)缓存淘汰成功,但是数据库更新失败,这也会引发后期数据不一致
1. 概述
我们在考虑MySQL数据库的高可用的架构时,主要要考虑如下几方面:
关于对高可用的分级在这里我们不做详细的讨论,这里只讨论常用高可用方案的优缺点以及高可用方案的选型。
2. 高可用方案
2.1. 主从或主主半同步复制
使用双节点数据库,搭建单向或者双向的半同步复制。在5.7以后的版本中,由于lossless replication、logical多线程复制等一些列新特性的引入,使得MySQL原生半同步复制更加可靠。
常见架构如下:
通常会和proxy、keepalived等第三方软件同时使用,即可以用来监控数据库的 健康 ,又可以执行一系列管理命令。如果主库发生故障,切换到备库后仍然可以继续使用数据库。
优点:
缺点:
2.2. 半同步复制优化
半同步复制机制是可靠的。如果半同步复制一直是生效的,那么便可以认为数据是一致的。但是由于网络波动等一些客观原因,导致半同步复制发生超时而切换为异步复制,那么这时便不能保证数据的一致性。所以尽可能的保证半同步复制,便可提高数据的一致性。
该方案同样使用双节点架构,但是在原有半同复制的基础上做了功能上的优化,使半同步复制的机制变得更加可靠。
可参考的优化方案如下:
半同步复制由于发生超时后,复制断开,当再次建立起复制时,同时建立两条通道,其中一条半同步复制通道从当前位置开始复制,保证从机知道当前主机执行的进度。另外一条异步复制通道开始追补从机落后的数据。当异步复制通道追赶到半同步复制的起始位置时,恢复半同步复制。
搭建两条半同步复制通道,其中连接文件服务器的半同步通道正常情况下不启用,当主从的半同步复制发生网络问题退化后,启动与文件服务器的半同步复制通道。当主从半同步复制恢复后,关闭与文件服务器的半同步复制通道。
优点:
缺点:
2.3. 高可用架构优化
将双节点数据库扩展到多节点数据库,或者多节点数据库集群。可以根据自己的需要选择一主两从、一主多从或者多主多从的集群。
由于半同步复制,存在接收到一个从机的成功应答即认为半同步复制成功的特性,所以多从半同步复制的可靠性要优于单从半同步复制的可靠性。并且多节点同时宕机的几率也要小于单节点宕机的几率,所以多节点架构在一定程度上可以认为高可用性是好于双节点架构。
但是由于数据库数量较多,所以需要数据库管理软件来保证数据库的可维护性。可以选择MMM、MHA或者各个版本的proxy等等。常见方案如下:
MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master,整个故障转移过程对应用程序完全透明。
MHA Node运行在每台MySQL服务器上,主要作用是切换时处理二进制日志,确保切换尽量少丢数据。
MHA也可以扩展到如下的多节点集群:
优点:
缺点:
Zookeeper使用分布式算法保证集群数据的一致性,使用zookeeper可以有效的保证proxy的高可用性,可以较好的避免网络分区现象的产生。
优点:
缺点:
2.4. 共享存储
共享存储实现了数据库服务器和存储设备的解耦,不同数据库之间的数据同步不再依赖于MySQL的原生复制功能,而是通过磁盘数据同步的手段,来保证数据的一致性。
SAN的概念是允许存储设备和处理器(服务器)之间建立直接的高速网络(与LAN相比)连接,通过这种连接实现数据的集中式存储。常用架构如下:
使用共享存储时,MySQL服务器能够正常挂载文件系统并操作,如果主库发生宕机,备库可以挂载相同的文件系统,保证主库和备库使用相同的数据。
优点:
缺点:
DRBD是一种基于软件、基于网络的块复制存储解决方案,主要用于对服务器之间的磁盘、分区、逻辑卷等进行数据镜像,当用户将数据写入本地磁盘时,还会将数据发送到网络中另一台主机的磁盘上,这样的本地主机(主节点)与远程主机(备节点)的数据就可以保证实时同步。常用架构如下:
当本地主机出现问题,远程主机上还保留着一份相同的数据,可以继续使用,保证了数据的安全。
DRBD是linux内核模块实现的快级别的同步复制技术,可以与SAN达到相同的共享存储效果。
优点:
缺点:
2.5. 分布式协议
分布式协议可以很好解决数据一致性问题。比较常见的方案如下:
MySQL cluster是官方集群的部署方案,通过使用NDB存储引擎实时备份冗余数据,实现数据库的高可用性和数据一致性。
优点:
缺点:
基于Galera的MySQL高可用集群, 是多主数据同步的MySQL集群解决方案,使用简单,没有单点故障,可用性高。常见架构如下:
优点:
缺点:
Paxos 算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。这个算法被认为是同类算法中最有效的。Paxos与MySQL相结合可以实现在分布式的MySQL数据的强一致性。常见架构如下:
优点:
缺点:
3. 总结
随着人们对数据一致性的要求不断的提高,越来越多的方法被尝试用来解决分布式数据一致性的问题,如MySQL自身的优化、MySQL集群架构的优化、Paxos、Raft、2PC算法的引入等等。
而使用分布式算法用来解决MySQL数据库数据一致性的问题的方法,也越来越被人们所接受,一系列成熟的产品如PhxSQL、MariaDB Galera Cluster、Percona XtraDB Cluster等越来越多的被大规模使用。
随着官方MySQL Group Replication的GA,使用分布式协议来解决数据一致性问题已经成为了主流的方向。期望越来越多优秀的解决方案被提出,MySQL高可用问题可以被更好的解决。
分布式解决方案 tidb
多主 多备 master lvs做vip 读写分离中间件
mysqldump命令将数据库中的数据备份成一个文本文件。表的结构和表中的数据将存储在生成的文本文件中。
mysqldump命令的工作原理很简单。它先查出需要备份的表的结构,再在文本文件中生成一个CREATE语句。然后,将表中的所有记录转换成一条INSERT语句。然后通过这些语句,就能够创建表并插入数据。
阿牛去一家中意的公司面试,本以为凭借以往丰富的经验,肯定手到擒来,结果第一个问题,我就“出门右拐”了。
问题就是:MySQL是怎么保证事务一致性的?
回到家阿牛翻阅资料,终于搞懂了,在这里分享给大家。
定义
在搞清楚问题答案之前,先搞清楚以下几个名词以及大致的用处
redo log:
通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)、Innodb特有的,他在存储引擎层。循环写的,空间固定会用完。作用是crash-safe能力
binlog:
是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ” 是 MySQL 的 Server 层实现的,所有引擎都可以使用。是可以追加写入的,“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。作用是数据归档
undo log:
有两个作用:提供回滚和多个行版本控制(MVCC)。
在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。
SQL执行的过程
了解了以上名词之后,让我们看一下“一条更新SQL语句执行的过程是什么?”
如图1有几个关键步骤:
1、先查找记录所在的Innodb页在不在内存里;如果不在内存里则将记录所在的页加载在内存里;根据SQL语句在内存中将记录更新
2、将更新前的记录写入undolog
3、根据记录的更新值将变更写入redolog(buffer)中,并将状态变更为prepare
4、将变更记录到逻辑日志
5、redolog日志中的状态修改为commit,返回结束
至此:一条更新语句的过程结束
上面的步骤中有些同学可能会有一些疑问:为什么更新一条记录要把一整页数据加载到内存里答:因为Innodb引擎中,最小的存储单位是页为什么一定要加载到内存里?答:因为所有的计算操作都是在内存里,操作完成后最终才写回磁盘为什么要写入redolog,直接写入磁盘,然后写入binlog就好了啊?答:这将在下面会提到,请往后看
为了加深理解,准备了下面2张图辅助理解
以图3为例,让我们看看在每个步骤出现异常的时候,到底怎么保证事物一致性的吧!1、步骤123,所有的操作最多还只是内存里,如果出现宕机、断电等异常, 记录不会有任何变动,事物是一致的2、步骤4刚执行完,断电了,因为redolog还处在prepare状态, 这时候事物也是一致的3、步骤5记录binlog的过程中断电了,这时候要保证主从一致性, 事物也是不生效的,最终也是一致的4、步骤6、7如果中间任何一个时刻断电了,这时候情况就不一样了,事物是生效的,因为redolog、binlog的数据都是完整的,服务器重启后可以按照xid来去查看binlog、redolog中是否都存在, 都存在该事物就是生效的。上面就是怎么保证事务一致性的根本原因
为什么要使用redolog?
回答这个问题之前,我们先看看redolog用图形表示的
图4是redolog的形象一点的表现,并不是说redolog 长这个样子,只是为了更形象;一般情况下redolog一组4个文件,每个文件1个G,其中write pos是指redolog当前写到什么位置了,check point是指上次刷脏结束的位置,当write log和check point重合时,所有的进程停止,开始新一轮的刷脏操作。刷完后redolog清空开始下一轮的写入,往返重复。
可能这样表示有点抽象,让我们看下图5
从上图中可以看的更形象一点,在sql执行的时候,会有磁盘IO将数据页加载到内存,然后在内存中将数据修改,修改后的数据页在内存中叫做脏页(叫脏页因为和磁盘中的数据不一致啊),又因为在内存中容易丢失,所以将数据页的变更记录如redolog中,随着记录插入、更新等操作的增多,redolog空间慢慢的满了,这时候就开始刷脏操作了,page cleaner thread线程会将所有的脏页数据刷新到磁盘,使得变更最终被持久化到磁盘。
讲到这里一定还会有人不太理解,刷脏之前断电了咋办?
这就是redolog的另一个重要的作用,crash-safe能力,实现的逻辑是这样的,断电后内存的数据都没了,重启后读取redolog文件,因为redolog文件记录的是在Innodb页x的m处做了y的修改,所以根据redolog将涉及到的Innodb页重新加载到内存,根据redolog的记录将内存中的数据重新修改,这样就能恢复断电前的数据了。
完
下期预告:还是MySQL,敬请期待
本文首发自: 程序员阿牛