关于mysql:MySQL-锁

7次阅读

共计 4438 个字符,预计需要花费 12 分钟才能阅读完成。

Thresh

锁分类

从操作的粒度可分为表级锁、行级锁和页级锁。

表级锁:每次操作锁住整张表。锁定粒度大,产生锁抵触的概率最高,并发度最低。利用在 MyISAM、InnoDB、BDB 等存储引擎中。行级锁:每次操作锁住一行数据。锁定粒度最小,产生锁抵触的概率最低,并发度最高。利用在 InnoDB 存储引擎中。页级锁:每次锁定相邻的一组记录,锁定粒度界于表锁和行锁之间,开销和加锁工夫界于表锁和行锁之间,并发度个别。利用在 BDB 存储引擎中。

从操作的类型可分为读锁和写锁。

读锁(S 锁):共享锁,针对同一份数据,多个读操作能够同时进行而不会相互影响。写锁(X 锁):排他锁,以后写操作没有实现前,它会阻断其余写锁和读锁。

IS 锁、IX 锁:动向读锁、动向写锁,属于表级锁,S 和 X 次要针对行级锁。在对表记录增加 S 或 X 锁之前,会先对表增加 IS 或 IX 锁。

S 锁:事务 A 对记录增加了 S 锁,能够对记录进行读操作,不能做批改,其余事务能够对该记录追加 S 锁,然而不能追加 X 锁,须要追加 X 锁,须要等记录的 S 锁全副开释。
X 锁:事务 A 对记录增加了 X 锁,能够对记录进行读和批改操作,其余事务不能对记录做读和批改操作。

从操作的性能可分为乐观锁和乐观锁

乐观锁:个别的实现形式是对记录数据版本进行比对,在数据更新提交的时候才会进行冲突检测,如果发现抵触了,则提醒错误信息。乐观锁:在对一条数据批改的时候,为了防止同时被其他人批改,在批改数据之前先锁定,再批改的管制形式。共享锁和排他锁是乐观锁的不同实现,但都属于乐观锁领域。

行锁原理

在 InnoDB 引擎中,咱们能够应用行锁和表锁,其中行锁又分为共享锁和排他锁。InnoDB 行锁是通过对索引数据页上的记录加锁实现的,次要实现算法有 3 种:Record Lock、Gap Lock 和 Next-key Lock。

RecordLock 锁:锁定单个行记录的锁。(记录锁,RC、RR 隔离级别都反对)GapLock 锁:间隙锁,锁定索引记录间隙,确保索引记录的间隙不变。(范畴锁,RR 隔离级别反对)Next-key Lock 锁:记录锁和间隙锁组合,同时锁住数据,并且锁住数据前后范畴。(记录锁 + 范畴锁,RR 隔离级别反对)

在 RR 隔离级别,InnoDB 对于记录加锁行为都是先采纳 Next-Key Lock,然而当 SQL 操作含有惟一索引时,Innodb 会对 Next-Key Lock 进行优化,降级为 RecordLock,仅锁住索引自身而非范畴。

1)select ... from 语句:InnoDB 引擎采纳 MVCC 机制实现非阻塞读,所以对于一般的 select 语句,InnoDB 不加锁
2)select ... from lock in share mode 语句:追加了共享锁,InnoDB 会应用 Next-Key Lock 锁进行解决,如果扫描发现惟一索引,能够降级为 RecordLock 锁。3)select ... from for update 语句:追加了排他锁,InnoDB 会应用 Next-Key Lock 锁进行解决,如果扫描发现惟一索引,能够降级为 RecordLock 锁。4)update ... where 语句:InnoDB 会应用 Next-Key Lock 锁进行解决,如果扫描发现惟一索引,能够降级为 RecordLock 锁。5)delete ... where 语句:InnoDB 会应用 Next-Key Lock 锁进行解决,如果扫描发现惟一索引,能够降级为 RecordLock 锁。6)insert 语句:InnoDB 会在将要插入的那一行设置一个排他的 RecordLock 锁。

乐观锁

乐观锁(Pessimistic Locking),是指在数据处理过程,将数据处于锁定状态,个别应用数据库的锁机制实现。从狭义上来讲,后面提到的行锁、表锁、读锁、写锁、共享锁、排他锁等,这些都属于乐观锁领域

表级锁
表级锁每次操作都锁住整张表,并发度最低。
手动减少表锁

lock table 表名称 read|write, 表名称 2 read|write;

查看表上加过的锁

show open tables;

删除表锁

unlock tables;

表级读锁:以后表追加 read 锁,以后连贯和其余的连贯都能够读操作;然而以后连贯增删改操作会报错,其余连贯增删改会被阻塞。
表级写锁:以后表追加 write 锁,以后连贯能够对表做增删改查操作,其余连贯对该表所有操作都被阻塞(包含查问)。
总结:表级读锁会阻塞写操作,然而不会阻塞读操作。而写锁则会把读和写操作都阻塞。

共享锁(行级锁 - 读锁)
共享锁又称为读锁,简称 S 锁。共享锁就是多个事务对于同一数据能够共享一把锁,都能拜访到数据,然而只能读不能批改。应用共享锁的办法是在 select … lock in share mode,只实用查问语句。
总结:事务应用了共享锁(读锁),只能读取,不能批改,批改操作被阻塞。

排他锁(行级锁 - 写锁)
排他锁又称为写锁,简称 X 锁。排他锁就是不能与其余锁并存,如一个事务获取了一个数据行的排他锁,其余事务就不能对该行记录做其余操作,也不能获取该行的锁。
应用排他锁的办法是在 SQL 开端加上 for update,innodb 引擎默认会在 update,delete 语句加上 for update。
行级锁的实现其实是依附其对应的索引,所以如果操作没用到索引的查问,那么会锁住全表记录。

总结:事务应用了排他锁(写锁),以后事务能够读取和批改,其余事务不能批改,也不能获取记录锁(select… for update)。如果查问没有应用到索引,将会锁住整个表记录。

乐观锁

乐观锁是绝对于乐观锁而言的,它不是数据库提供的性能,须要开发者本人去实现。在数据库操作时,想法很乐观,认为这次的操作不会导致抵触,因而在数据库操作时并不做任何的非凡解决,即不加锁,而是在进行事务提交时再去判断是否有抵触了。

乐观锁实现的关键点:抵触的检测

乐观锁和乐观锁都能够解决事务写写并发,在利用中能够依据并发解决能力抉择辨别,比方对并发率要求高的抉择乐观锁;对于并发率要求低的能够抉择乐观锁。

乐观锁实现原理
应用版本字段(version)
先给数据表减少一个版本(version) 字段,每操作一次,将那条记录的版本号加 1。version 是用来查看被读的记录有无变动,作用是避免记录在业务解决期间被其余事务批改。

应用工夫戳(Timestamp)
与应用 version 版本字段类似,同样须要给在数据表减少一个字段,字段类型应用 timestamp
工夫戳。也是在更新提交的时候查看以后数据库中数据的工夫戳和本人更新前取到的工夫戳
进行比照,如果统一则提交更新,否则就是版本抵触,勾销操作。

update products set quantity=quantity-1,version=version+1 where id=1 and version=#{version};

除了本人手动实现乐观锁之外,许多数据库拜访框架也封装了乐观锁的实现,比方
hibernate 框架。MyBatis 框架大家能够应用 OptimisticLocker 插件来扩大。

死锁与解决方案

表锁死锁
产生起因:

用户 A 拜访表 A(锁住了表 A),而后又拜访表 B;另一个用户 B 拜访表 B(锁住了表 B),而后希图拜访表 A;这时用户 A 因为用户 B 曾经锁住表 B,它必须期待用户 B 开释表 B 能力持续,同样用户 B 要等用户 A 开释表 A 能力持续,这就死锁就产生了。

用户 A –》A 表(表锁)–》B 表(表锁)
用户 B –》B 表(表锁)–》A 表(表锁)

解决方案:
这种死锁比拟常见,是因为程序的 BUG 产生的,除了调整的程序的逻辑没有其它的方法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量依照雷同的程序进行解决,尽量避免同时锁定两个资源,如操作 A 和 B 两张表时,总是按先 A 后 B 的程序解决,必须同时锁定两个资源时,要保障在任何时刻都应该依照雷同的程序来锁定资源。

行级锁死锁
产生起因 1:
如果在事务中执行了一条没有索引条件的查问,引发全表扫描,把行级锁回升为全表记录锁定(等价于表级锁),多个这样的事务执行后,就很容易产生死锁和阻塞,最终利用零碎会越来越慢,产生阻塞或死锁。

解决方案 1:
SQL 语句中不要应用太简单的关联多表的查问;应用 explain“执行打算 ” 对 SQL 语句进行剖析,对于有全表扫描和全表锁定的 SQL 语句,建设相应的索引进行优化。

==========================================================================

产生起因 2:
两个事务别离想拿到对方持有的锁,相互期待,于是产生死锁。

解决方案 2:
在同一个事务中,尽可能做到一次锁定所须要的所有资源
依照 id 对资源排序,而后按程序进行解决

共享锁转换为排他锁
产生起因:
事务 A 查问一条纪录,而后更新该条纪录;此时事务 B 也更新该条纪录,这时事务 B 的排他锁因为事务 A 有共享锁,必须等 A 开释共享锁后才能够获取,只能排队期待。事务 A 再执行更新操作时,此处产生死锁,因为事务 A 须要排他锁来做更新操作。然而,无奈授予该锁申请,因为事务 B 曾经有一个排他锁申请,并且正在期待事务 A 开释其共享锁。

事务 A:  select * from dept where deptno=1 lock in share mode; // 共享锁,1
        update dept set dname='java' where deptno=1;// 排他锁,3
事务 B: update dept set dname='Java' where deptno=1;// 因为 1 有共享锁,没法获取排他锁,需期待,2

解决方案:
对于按钮等控件,点击立即生效,不让用户反复点击,防止引发同时对同一条记录屡次操作;

应用乐观锁进行管制。乐观锁机制防止了长事务中的数据库加锁开销,大大晋升了大并发量
下的零碎性能。须要留神的是,因为乐观锁机制是在咱们的零碎中实现,来自内部零碎的用
户更新操作不受咱们零碎的管制,因而可能会造成脏数据被更新到数据库中;

死锁排查
MySQL 提供了几个与锁无关的参数和命令,能够辅助咱们优化锁操作,缩小死锁产生查看死锁日志

通过 show engine innodb status\G 命令查看近期死锁日志信息。

应用办法:1、查看近期死锁日志信息;2、应用 explain 查看下 SQL 执行打算

查看锁状态变量

通过 show status like'innodb_row_lock%‘命令查看状态变量,剖析零碎中的行锁的抢夺状况
    Innodb_row_lock_current_waits:以后正在期待锁的数量
    Innodb_row_lock_time:从系统启动到当初锁定总工夫长度
    Innodb_row_lock_time_avg:每次期待锁的均匀工夫
    Innodb_row_lock_time_max:从系统启动到当初期待最长的一次锁的工夫
    Innodb_row_lock_waits:系统启动后到当初总共期待的次数

如果期待次数高,而且每次等待时间长,须要剖析零碎中为什么会有如此多的期待,而后着
手定制优化

正文完
 0