锁,是数据库区别于文件系统的一个关键特性。
锁机制用于管理对共享资源的并发访问。
锁在数据库中是为了解决并发问题的。
对共享空间来说,存在并发,并发就需要使用锁来实现并发控制。
Mutex和Latch锁:用来保护链。
情景:
A线程想把链中的某个数据从冷区放到热区。B线程想把某个数据给删除。如果没有锁的话,就会出现冲突。
对chain(链)的保护:
- ①mutex锁:排他锁。适用于不经常出现并发的链,或者并发时间很短的。
- ②latch锁:比mutex大点。r、rw。适用于经常出现并发访问的链,且并发时间比较长。
本人的另一篇笔记:latch的争用
http://blog.csdn.net/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。
- 请求mutex不到的情况是 mutex spin waits的数(比如8000),但是经过spin-wait,实际上只有OS
第二行RW-shared是:以共享的形式访问的时候的统计;
- 第三行RW-excl 是:以排他的形式访问的时候的统计。
7.如何降低latch争用
- 1.优化SQL,降低对内存读的数量;
- 2.增加innodb_buffer_pool_instances的数量。
- 3.对访问不是很频繁,同时相对较短的链,我们使用mutex(mutex可以理解为排他的latch。) 就可以来保护这些链。 但是,对于访问频繁的链(会有大量的读和写),还是要用latch来进行保护。
lock与latch的比较
比较内容 | lock | latch |
---|---|---|
对象 | 事务 | 线程 |
保护 | 数据库内容 | 内存数据结构 |
持续时间 | 整个事务过程 | 临界资源 |
模式 | 行锁、表锁、意向锁 | 读写锁、互斥锁 |
死锁 | 通过waits-for graph、time out等机制进行死锁检测与处理 | 无死锁检测与处理机制。仅通过应用程序加锁的顺序(lock leveling)保证无死锁的情况发生 |
存在于 | lock manager的哈希表中 | 每个数据结构的对象中 |
命令 show engine innodb mutex 输出结果说明:
名称 | 说明 |
---|---|
count | mutex被请求的次数 |
spin_waits | spin 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),允许事务删除或者更新一行数据。【写锁。其他事务不能再加任何锁】
排它锁和共享锁的兼容性:
锁类型 | X | S |
---|---|---|
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,做出它们的兼容性列表。
答:
四种锁在表级别的兼容性
锁类型 | X | S | IX | IS |
---|---|---|---|---|
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对于行锁,事务锁:可以很清楚的查出来。