MySQL中怎么实现死锁
这篇文章将为大家详细讲解有关MySQL中怎么实现死锁,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
1.了解锁等待与死锁出现锁等待或死锁的原因是访问数据库需要加锁,那你可能要问了,为啥要加锁呢?原因是为了确保并发更新场景下的数据正确性,保证数据库事务的隔离性。
锁等待也可称为事务等待,后执行的事务等待前面处理的事务释放锁,但是等待时间超过了 MySQL 的锁等待时间,就会引发这个异常。等待超时后的报错为“Lock wait timeout exceeded...”。
死锁发生的原因是两个事务互相等待对方释放相同资源的锁,从而造成的死循环。产生死锁后会立即报错“Deadlock found when trying to get lock...”。
2.现象复现及处理下面我们以 MySQL 5.7.23 版本为例(隔离级别是 RR ),来复现下上述两种异常现象。
mysql>showcreatetabletest_tb\G***************************1.row***************************Table:test_tbCreateTable:CREATETABLE`test_tb`(`id`int(11)NOTNULLAUTO_INCREMENT,`col1`varchar(50)NOTNULLDEFAULT'',`col2`int(11)NOTNULLDEFAULT'1',`col3`varchar(20)NOTNULLDEFAULT'',PRIMARYKEY(`id`),KEY`idx_col1`(`col1`))ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf81rowinset(0.00sec)mysql>select*fromtest_tb;+----+------+------+------+|id|col1|col2|col3|+----+------+------+------+|1|fdg|1|abc||2|a|2|fg||3|ghrv|2|rhdv|+----+------+------+------+3rowsinset(0.00sec)#事务一首先执行mysql>begin;QueryOK,0rowsaffected(0.00sec)mysql>select*fromtest_tbwherecol1='a'forupdate;+----+------+------+------+|id|col1|col2|col3|+----+------+------+------+|2|a|2|fg|+----+------+------+------+1rowinset(0.00sec)#事务二然后执行mysql>begin;QueryOK,0rowsaffected(0.01sec)mysql>updatetest_tbsetcol2=1wherecol1='a';ERROR1205(HY000):Lockwaittimeoutexceeded;tryrestartingtransaction
出现上种异常的原因是事务二在等待事务一的行锁,但事务一一直没提交,等待超时而报错。InnoDB 行锁等待超时时间由 innodb_lock_wait_timeout 参数控制,此参数默认值为 50 ,单位为秒,即默认情况下,事务二会等待 50s ,若仍拿不到行锁则会报等待超时异常并回滚此条语句。
对于 5.7 版本,出现锁等待时,我们可以查看 information_schema 中的几张系统表来查询事务状态。
innodb_trx 当前运行的所有事务。
innodb_locks 当前出现的锁。
innodb_lock_waits 锁等待的对应关系
#锁等待发生时查看innodb_trx表可以看到所有事务#trx_state值为LOCKWAIT则代表该事务处于等待状态mysql>select*frominformation_schema.innodb_trx\G***************************1.row***************************trx_id:38511trx_state:LOCKWAITtrx_started:2021-03-2417:20:43trx_requested_lock_id:38511:156:4:2trx_wait_started:2021-03-2417:20:43trx_weight:2trx_mysql_thread_id:1668447trx_query:updatetest_tbsetcol2=1wherecol1='a'trx_operation_state:startingindexreadtrx_tables_in_use:1trx_tables_locked:1trx_lock_structs:2trx_lock_memory_bytes:1136trx_rows_locked:1trx_rows_modified:0trx_concurrency_tickets:0trx_isolation_level:REPEATABLEREADtrx_unique_checks:1trx_foreign_key_checks:1trx_last_foreign_key_error:NULLtrx_adaptive_hash_latched:0trx_adaptive_hash_timeout:0trx_is_read_only:0trx_autocommit_non_locking:0***************************2.row***************************trx_id:38510trx_state:RUNNINGtrx_started:2021-03-2417:18:54trx_requested_lock_id:NULLtrx_wait_started:NULLtrx_weight:4trx_mysql_thread_id:1667530trx_query:NULLtrx_operation_state:NULLtrx_tables_in_use:0trx_tables_locked:1trx_lock_structs:4trx_lock_memory_bytes:1136trx_rows_locked:3trx_rows_modified:0trx_concurrency_tickets:0trx_isolation_level:REPEATABLEREADtrx_unique_checks:1trx_foreign_key_checks:1trx_last_foreign_key_error:NULLtrx_adaptive_hash_latched:0trx_adaptive_hash_timeout:0trx_is_read_only:0trx_autocommit_non_locking:02rowsinset(0.00sec)#innodb_trx字段值含义trx_id:事务ID。trx_state:事务状态,有以下几种状态:RUNNING、LOCKWAIT、ROLLINGBACK和COMMITTING。trx_started:事务开始时间。trx_requested_lock_id:事务当前正在等待锁的标识,可以和INNODB_LOCKS表JOIN以得到更多详细信息。trx_wait_started:事务开始等待的时间。trx_weight:事务的权重。trx_mysql_thread_id:事务线程ID,可以和PROCESSLIST表JOIN。trx_query:事务正在执行的SQL语句。trx_operation_state:事务当前操作状态。trx_tables_in_use:当前事务执行的SQL中使用的表的个数。trx_tables_locked:当前执行SQL的行锁数量。trx_lock_structs:事务保留的锁数量。trx_isolation_level:当前事务的隔离级别。#sys.innodb_lock_waits视图也可看到事务等待状况,且给出了杀链接的SQLmysql>select*fromsys.innodb_lock_waits\G***************************1.row***************************wait_started:2021-03-2417:20:43wait_age:00:00:22wait_age_secs:22locked_table:`testdb`.`test_tb`locked_index:idx_col1locked_type:RECORDwaiting_trx_id:38511waiting_trx_started:2021-03-2417:20:43waiting_trx_age:00:00:22waiting_trx_rows_locked:1waiting_trx_rows_modified:0waiting_pid:1668447waiting_query:updatetest_tbsetcol2=1wherecol1='a'waiting_lock_id:38511:156:4:2waiting_lock_mode:Xblocking_trx_id:38510blocking_pid:1667530blocking_query:NULLblocking_lock_id:38510:156:4:2blocking_lock_mode:Xblocking_trx_started:2021-03-2417:18:54blocking_trx_age:00:02:11blocking_trx_rows_locked:3blocking_trx_rows_modified:0sql_kill_blocking_query:KILLQUERY1667530sql_kill_blocking_connection:KILL1667530
sys.innodb_lock_waits 视图整合了事务等待状况,同时给出杀掉堵塞源端的 kill 语句。不过是否要杀掉链接还是需要综合考虑的。
死锁与锁等待稍有不同,我们同样也来简单复现下死锁现象。
#开启两个事务#事务一执行mysql>updatetest_tbsetcol2=1wherecol1='a';QueryOK,1rowaffected(0.00sec)Rowsmatched:1Changed:1Warnings:0#事务二执行mysql>updatetest_tbsetcol2=1whereid=3;QueryOK,1rowaffected(0.00sec)Rowsmatched:1Changed:1Warnings:0#回到事务一执行回车后此条语句处于锁等待状态mysql>updatetest_tbsetcol1='abcd'whereid=3;QueryOK,1rowaffected(5.71sec)Rowsmatched:1Changed:1Warnings:0#回到事务二再执行此时二者相互等待发生死锁mysql>updatetest_tbsetcol3='gddx'wherecol1='a';ERROR1213(40001):Deadlockfoundwhentryingtogetlock;tryrestartingtransaction
发生死锁后会选择一个事务进行回滚,想查明死锁原因,可以执行 show engine innodb status 来查看死锁日志,根据死锁日志,结合业务逻辑来进一步定位死锁原因。
在实际应用中,我们要尽量避免死锁现象的发生,可以从以下几个方面入手:
事务尽可能小,不要讲复杂逻辑放进一个事务里。
涉及多行记录时,约定不同事务以相同顺序访问。
业务中要及时提交或者回滚事务,可减少死锁产生的概率。
表要有合适的索引。
可尝试将隔离级别改为 RC 。
关于MySQL中怎么实现死锁就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。