MySQL的锁

锁,是数据库区别于文件系统的一个关键特性。
锁机制用于管理对共享资源的并发访问。
锁在数据库中是为了解决并发问题的。
对共享空间来说,存在并发,并发就需要使用锁来实现并发控制。

Mutex和Latch锁:用来保护链。
情景:
A线程想把链中的某个数据从冷区放到热区。B线程想把某个数据给删除。如果没有锁的话,就会出现冲突。
对chain(链)的保护:

  • ①mutex锁:排他锁。适用于不经常出现并发的链,或者并发时间很短的。
  • ②latch锁:比mutex大点。r、rw。适用于经常出现并发访问的链,且并发时间比较长。

本人的另一篇笔记:latch的争用
http://blog.youkuaiyun.com/qq_18312025/article/details/78587334

1.引入
锁,是用来管理对共享文件的并发访问。

latch称为栓锁(轻量级的锁),因为其要求锁的时间非常短。

在innodb存储引擎中,又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临街资源的正确性,并且通常没有死锁检测的机制。

而lock锁的对象是事务,用来锁定数据库的对象,如表、页、行。并且一般lock的对象仅在事务commit或者rollback后释放(不同事务的隔离级别释放的时间可能不一样)。有死锁机制。

2.latch争用的过程
当一个线程持有latch时(也就是对这个链进行操作的时候),其他的线程得在一边看着。比如线程1持有latch,正在对freelist进行遍历,线程2也想进行遍历,是不可能的,所以现在线程2阻塞,只有两种选择,一是退出,二是等待,但如果等待就会占用cpu,如果想占用cpu就要有事可做,否则cpu’会把没事做的线程踢出去,所以线程2就会执行某段空代码来让自己忙起来(所以虽然线程2在等待,但cpu还是忙碌的)。

3.线程2的三种情况:

  • gets:去试试能不能获得latch锁;
  • misses:获取latch锁失败;
  • sleeps:去执行空代码让自己看起来忙了。

4.latch争用的现象:

  • 1.latch争用会表现为cpu繁忙;
  • 2.latch争用没有排队。

原因:

  • 1.内存访问频繁(链在内存中);
  • 2.list 太长了。

5.Latch争用的监控指标:

Mysql>show engine innodb status   \G 
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 2, signal count 2
Mutex spin waits 0, rounds 0, OS waits 0
RW-shared spins 2, rounds 60, OS waits 2
RW-excl spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 mutex, 30.00 RW-shared, 0.00 RW-excl
--------

6.解读监控指标:
innodb在获得mutex时是两阶段的。如果Mutex被人锁住了,先做一个循环,不断去获得mutex锁,称之为spin-wait,然后才sleep。因为sleep等待被唤醒的代价还是比较高的。通过spin-wait,可以明显降低这个开销。

mutex spin waits:这个代表的是线程获得的锁,但是被别人锁住了,所以它得首先spin-wait;

  • rounds:是线程在spin-wait循环检查mutex是否已经释放的探测次数,旋转一周就是1round;
  • OS waits:是spin-wait完成以后,还是没有获得mutex,不得不sleep的次数。这个主要是评估mutex获取不到的比例。比如:

    • 请求mutex不到的情况是 mutex spin waits的数(比如8000),但是经过spin-wait,实际上只有OS
      waits的次数(比如2000),也就是说,中间的差值(75%),就是稍微等一下就能拿到mutex。
  • 第二行RW-shared是:以共享的形式访问的时候的统计;

  • 第三行RW-excl 是:以排他的形式访问的时候的统计。

7.如何降低latch争用

  • 1.优化SQL,降低对内存读的数量;
  • 2.增加innodb_buffer_pool_instances的数量。
  • 3.对访问不是很频繁,同时相对较短的链,我们使用mutex(mutex可以理解为排他的latch。) 就可以来保护这些链。 但是,对于访问频繁的链(会有大量的读和写),还是要用latch来进行保护。

lock与latch的比较

比较内容locklatch
对象事务线程
保护数据库内容内存数据结构
持续时间整个事务过程临界资源
模式行锁、表锁、意向锁读写锁、互斥锁
死锁通过waits-for graph、time out等机制进行死锁检测与处理无死锁检测与处理机制。仅通过应用程序加锁的顺序(lock leveling)保证无死锁的情况发生
存在于lock manager的哈希表中每个数据结构的对象中

命令 show engine innodb mutex 输出结果说明:

名称说明
countmutex被请求的次数
spin_waitsspin lock(自旋锁)的次数,innodb存储引擎latch在不能获得锁时首先进行自旋,若自旋后还不能获得锁,则进入等待状态
spin_rounds自旋内部循环的总次数,每次自旋的内部循环是一个随机数。spin_rounds/spain_waits表示平均每次自旋所需的内部循环次数
os_waits表示操作系统等待的次数。当 spin lock 通过自旋还不能获得latch时,则会进入操作系统等待状态,等待被唤醒
os_yields进行os_thread_yield唤醒操作的次数
os_wait_times操作系统等待的时间,单位是ms

InnnoDB存储引擎中的锁

  • 共享锁(S Lock),允许事务读一行数据。【读锁。其他事务只能加共享锁不能加写锁】
  • 排他锁(X Lock),允许事务删除或者更新一行数据。【写锁。其他事务不能再加任何锁】

排它锁和共享锁的兼容性:

锁类型XS
X冲突冲突
S冲突兼容

表级锁
InnoDB存储引擎支持多粒度锁定 ,这种锁定允许在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,我们称之为意向锁。意向锁是表级别的锁,其设计目的主要是为了在一个事务中揭示下一行将被请求的锁的类型。InnoDB存储引擎支持两种意向锁:

  • 意向共享锁(IS Lock):事务想要获得一个表中某几行的共享锁(S锁)。
  • 意向排他锁(IX Lock):事务想要获得一个表中某几行的排他锁(X锁)。

因为lnnoDB存储引擎支持的是行级别的锁 ,所以意向锁只会阻塞全表扫描请求。
可以通过SHOW ENGINE INNODB STATUS命令来查看当前请求锁的信息。

共享表空间里有chain【页在链上】
保护chain的:mutex,latch

修改一个数据行的过程:

  • ①遍历相应的链,这个时候需要持有这个链的latch或者mutex;
  • ②找到链上的数据页以后,释放latch或者mutex

对链的并发访问,我们使用mutex或者latch;【mutex,latch不排队,持有时间短】
对数据页内容的并发访问,我们使用lock。【lock有排队机制】

锁的粒度

  • 表级别:并发比较差
  • 行级别:并发最好

对一个表来说:

  • InnoDB引擎:行级锁。
  • MyISAM引擎:锁是表级锁。

Microsoft SQL Server来说,

  • 2005版本以前是页锁;
  • 之后又乐观并发(支持行级锁)和悲观并发。

行级锁的实现机制:
这里写图片描述
InnoDB实现了行级锁,但是表现形式却是事务锁。
表现形式为:各个事务可以修改自己的数据行。

模拟S、X、IS、IX,做出它们的兼容性列表。
答:
四种锁在表级别的兼容性

锁类型XSIXIS
X(写)互斥互斥互斥互斥
S(读)互斥兼容互斥兼容
IX(删)互斥互斥兼容(表级别)兼容(表级别)
IS(查)互斥兼容兼容(表级别)兼容(表级别)

(注意!此表的S和IS不准!实际:先加IS,后加S是可以的。而先加S,后加IS是不行的。)

在表t1上 加S共享锁:lock table t1 read;
(新开会话:)
在t1 加IX锁:delete(update,insert) from t1 limit 1; #在t1删除一行数据,回车,会发现被锁住了。执行不了。
(新开会话:)
在t1 加IS锁:select * from t1 limit 1 for update; #也锁住了
(新开会话:)
在t1 加X锁:lock(drop,alter) table t1 write; #也锁住了

锁的释放:
【注意!用rollback或者commit是不能释放S锁的,只能先status看锁,然后kill 线程号;或者exit退出】
【而IS或IX是可以通过commit/rollback来释放的。】

表锁的缺陷
行级锁是能够查到的:

mysql>use information_schema;
mysql>select * from INNODB_LOCKS \G;

mysql>select * from INNODB_LOCK_WAITS \g

这里写图片描述
所以说,第三列把第一列锁住了。

  • mysql对于表锁:无能为力。
  • mysql对于行锁,事务锁:可以很清楚的查出来。
### MySQL 机制详解 MySQL 中的机制是为了保障数据库在高并发环境下的数据一致性和稳定性而设计的重要功能之一。以下是关于 MySQL 机制的具体解析: #### 1. 的分类 MySQL 主要分为两种类型的:表级和行级。 - **表级 (Table-Level Lock)** 表级是最简单的定策略,适用于 MyISAM 存储引擎。它会在整个表上施加,无论是读操作还是写操作都会影响到整张表。对于 `SELECT` 操作,MyISAM 会自动给涉及的所有表加上读;而对于 `UPDATE`, `INSERT`, 和 `DELETE` 则会自动加上写[^3]。 - **行级 (Row-Level Lock)** 行级由 InnoDB 存储引擎实现,提供更高的并发能力。只有涉及到具体记录的操作才会触发行级。例如,在执行 `SELECT ... FOR UPDATE` 或者 `SELECT ... LOCK IN SHARE MODE` 时,InnoDB 只会对符合条件的特定行加而不是封整个表[^2]。 #### 2. 不同存储引擎的特性 不同的存储引擎有不同的行为: - **MyISAM**: 默认使用的是表级别的,这意味着即使是一个小范围内的更新也会阻塞其他线程对该表任何部分的访问[^3]。 - **InnoDB**: 支持事务以及更细粒度的行级,这使得它可以更好地处理复杂的多用户场景下的并发请求[^4]。 #### 3. 常见类型及其作用 除了基本的表级与行级区分外,还有几种具体的形式用于满足不同需求: - **共享 (Shared Lock, S-Lock)** 当一个客户端通过命令如 `LOCK TABLES table_name READ` 获取了一个表上的共享之后,其它客户也可以获得该表上的共享,但不能再获取排他直到当前持有共享的所有连接释放它们为止[^2]。 - **排他 (Exclusive Lock, X-Lock)** 排他允许独占式的修改权限。如果某个事务已经获得了某条记录或者某些列上的排他,则在此期间不允许别的事务再对此同一组资源申请任何形式的新——既包括另外的排他也包括新的共享[^2]。 - **GAP ** 这种特殊的用来阻止新纪录插入到现有两条连续记录之间的空隙(gap)里去。比如当我们运行下面这条SQL语句的时候就会用到gap lock:`SELECT * FROM table_name WHERE id > 10 FOR UPDATE;` - **Next-Key Lock** 是一种组合了索引记录本身(record)和其前面那个区间(gap)两者一起保护起来的一种复合型模式。主要用于解决可重复读(repeatable read)隔离级别下可能出现的幻影问题(phantom problem)[^4]。 #### 4. 死现象及预防措施 死是指两个或更多事务相互等待对方持有的资源从而进入僵局的状态。为避免这种情况发生可以采取以下方法: - 尽量按照固定的顺序访问资源; - 减少单次事务持续时间; - 设置合理的超时参数让系统能够及时发现并终止潜在的死循环状况等等[^4]。 ```sql -- 示例:如何手动解表 UNLOCK TABLES; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

斯言甚善

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

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

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

打赏作者

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

抵扣说明:

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

余额充值