MySQL读写分离、MySQL锁机制

一:MySQL读写分离(主从)

大型网站为了解决大量的并发访问,除了在网站实现分布式负载均衡之外还远远不够。到了数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器来处理如此多的数据库连接操作,数据库必然会崩溃,特别是数据丢失的话,后果更是不堪设想。这时候,我们会考虑如何减少数据库的连接,下面就进入我们今天的主题。

利用主从数据库来实现读写分离,从而分担主数据库的压力。在多个服务器上部署mysql,将其中一台作为主数据库,而其它作为从数据库,实现主从同步。其中主数据库负责主动写的操作,而从数据库则只负责主动读的操作(slave从数据库仍然会被动的进行写操作,为了保持数据一致性),这样就可以很大程度上的避免数据丢失的问题,同时也可减少数据库的连接,减轻主数据库的负载。
在这里插入图片描述
那么主数据库和从数据库是怎么保证数据一致的呢?

首先主服务器主要进行写操作。它会把执行写入操作的SQL语句记录日志文件 bin log 中

从服务器中有两个核心的线程。IO线程、SQL线程

主服务器利用IO线程将 bin log 传送到从服务器中,从服务器将其写入到 relay log(中继日志) 中,

当外部需要读数据时会通过SQL线程,SQL线程会从 relay log 读到它想获取的数据。

一般从服务器中会存放数据库的快照,便于解决主服务器数据丢失的情况。
在这里插入图片描述
MySQL的读写分离是建立在主从架构的基础之上的,而主从模式是基于两个 log 实现的。

二:MySQL锁

① 从性能上分为 乐观锁(用版本对比来实现)和 悲观锁。
② 从对数据库操作的类型分,分为 读锁(S锁)和 写锁(X锁)(都属于悲观锁)。
③ 从对数据操作的粒度分,分为 表锁 和 行锁。

2.1 锁的类型

行级锁:

InnoDB存储引擎实现了如下两种标准的行级锁。

  • 共享锁(S Lock)
  • 排他锁(X Lock)

每次操作锁住一行数据。开销大,加锁慢会出现死锁锁定粒度最小,发生锁冲突的概率最低,并发度最高。

InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。

表级锁:

InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为
了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁。
若将上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,
那么首先需要对粗粒度的对象上锁。InnoDB存储引擎支持意向锁设计比较简练,为表级别的锁,它
支持两种意向锁:

  • 意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁;
  • 意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁;

表级意向锁与行级锁的兼容性如下表所示:
在这里插入图片描述
每次操作锁住整张表。开销小,加锁快不会出现死锁锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

2.2 锁的定义

在这里插入图片描述
为什么 InnoDB 要把行锁放在页上来存呢?

我们知道在各种语言中,我们能操作的最小的数据长度就是一个字节(8位),而不能按位去处理数据。加锁(加一个标识)其实只需要一位就可以了,那么就相当于浪费了其余七位。所以如果把行锁加在某一行上的话就会极其的浪费磁盘空间。

页是 InnoDB 磁盘管理的最小单元。(在InnoDB中,默认每个页的大小为 16 KB)
这样每次加载完一页,找到这一页中所有行加锁的信息。这样的话会比较合理。

(0-无锁 1-有锁)
在这里插入图片描述

2.3 一致性非锁定读

MVCC机制

2.4 一致性锁定读

在REPEATABLE READ隔离级别下,InnoDB存储引擎的SELECT操作使用一致性非锁定读。但是在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。而这要求数据库支持加锁语句,即使是对于SELECT的只读操作。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读操作:

  • SELECT … FOR UPDATE; (加X锁)
  • SELECT … LOCK IN SHARE MODE; (加S锁)

实际业务举例:
秒杀项目,获取订单流水号(一致性锁定读)
在这里插入图片描述
在这里插入图片描述

2.5 锁的算法

InnoDB存储引擎有3种行锁的算法,其分别是:

  • Record Lock:单个行记录上的锁;
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身;
  • Next-Key Lock:Gap Lock + Record Lock 临键锁,锁定一个范围,并且锁定记录本身;

Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,
那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。Gap Lock的作用是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生。Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。例如一个索引有10,11,13和20这四个值,那么该索引可能被Next-Key Locking的区间为:(-∞,10],(10,11],(11,13],(13,20],(20,+∞)。

2.6 死锁

死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。

解决方案:
1、超时回滚
当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。在InnoDB存储引擎中,参数 innodb_lock_wait_timeout 用来设置超时时间。
超时机制虽然简单,但是其仅通过超时后对事务进行回滚的方式来处理,或者说其是根据 FIFO (先入先出队列)的顺序选择回滚对象。但若超时的事务所占权重比较大,如事务操作更新了很多行,占用了较多的undo log,这时采用FIFO的方式,就显得不合适了,因为回滚这个事务的时间相对另一个事务所占用的时间可能会很多。

2、死锁检测
除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超
时的解决方案,这是一种更为主动的死锁检测方式。wait-for graph要求数据库保存以下两种信息:
锁的信息链表、事务等待链表。通过上述链表可以构造出一张图,而在这个图中若存在回路,就代
表存在死锁,因此资源间相互发生等待。在wait-for graph中,事务为图中的节点。
在这里插入图片描述

2.7 锁升级

InnoDB存储引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的。相反,其根据每个
事务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。

InnoDB行锁是通过给索引上的索引项加锁来实现的,InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

无索引行锁会升级为表锁(RR级别会升级为表锁,RC级别不会升级为表锁)

确切的说,MySQL为了保证在RR级别下,多次读取的数据始终保持不变,在InnoDB的引擎下就会触发行锁,以及间隙锁,来确保数据在查询期间不会被其他事务进行修改,保证数据的可重复读,所以在RR级别下,如果没有击中索引,就不能确保锁具体哪行的数据,所以直接升级为表锁。而在RC和RU级别下不用去确保多次读取数据的一致性, 就不会有间隙锁(防止出现幻读),以及行锁升级表锁,实际开发中用的比较多的还是RC和RR,RU会出现脏读的情况,一般不会用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小本科生debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值