mysql关于锁的问题吗_关于mysql一些锁问题的总结

大家都知道,锁是可以阻碍读写的,锁越多,数据库并发性能就越差,所以我们要随时准备监测锁的问题.

当然根本问题还是要开发要控制好锁粒度,把where条件写好,这样才能减少锁的影响.

另外,死锁其实不可怕,因为mysql内部发现死锁会发生回滚操作,所以不会真的"死"了,可怕的是锁等待,并发太高下一大堆语句等着某个语句释放锁,这才是严重阻碍效率的情况.

当然了,也不是说死锁的问题不重要,如果出现大量的死锁,那显然就是业务逻辑的问题了,需要和开发人员好好谈谈,不是随便谁谁能解决的事情.

查看事务隔离级别

首先,我们知道mysql有四种隔离级别,不同隔离级别,锁的等级也不一样,所以,查看锁的信息之前一定要先看当前的隔离级别是什么.mysql> select @@global.tx_isolation,@@tx_isolation;

+-----------------------+-----------------+

| @@global.tx_isolation | @@tx_isolation  |

+-----------------------+-----------------+

| REPEATABLE-READ       | REPEATABLE-READ |

+-----------------------+-----------------+

1 row in set (0.00 sec)

当前是RR的隔离级别

当然,除了改配置文件,你是能在线改的,下面是改成RC级别,注意[ global | session ]区别:mysql>set global transaction isolation level READ COMMITTED;

或者

mysql>set global tx_isolation='read-committed';

查看锁信息

在mysql里面有两个记录数据库性能的库information_schema和performance_schema,是专门记录mysql其他库的事务ID、锁信息、锁等待时间、缓存使用情况、sql执行信息等等信息,现在我们只看我们需要的信息。而performance_schema在5.7之前是默认不开启的,因为会占用性能,而在5.7之后是默认开的。#查看innodb事务ID,会显示是什么操作和一些常规信息,例如是否在运行running,还是等待锁.

SELECT * FROM information_schema.INNODB_TRX\G

#查看当前innodb的锁的信息,会显示是什么锁类型,属于那个事务ID,通常只显示行锁

SELECT * FROM information_schema.innodb_locks\G

#查看innodb锁的等待时间,和等待的是那的是那个事务ID的锁

select * from information_schema.innodb_lock_waits\G

#查看metadata lock信息,即元数据锁,这种锁会让任何语句都不能操作这个表,包括表结构

select * from information_schema.processlist where state = 'Waiting for table metadata lock';

#上面这些语句要是查不到数据,那就证明当前没有响应的锁信息,查到了那就是有了.

#要想查询历史数据来统计性能,可以用下面这两个语句来计算,计算方法可以另外查查

show status like '%lock%';

show variables like '%timeout%';

#查看表锁,上面那些sql未必会显示出来

show OPEN TABLES where In_use > 0;

#handles锁,可以理解为要执行写操作的表锁,所以会排除其他所有sql的读写

select * from performance_schema.table_handles where OWNER_THREAD_ID > 0;

#查看那个线程加表锁

select pth.* from performance_schema.table_handles as pt join performance_schema.threads as pth on pt.OWNER_THREAD_ID=pth.thread_id where pt.OWNER_THREAD_ID > 0 \G

#当前正在干活的线程在执行什么语句以及相关的信息,也就是确认那些sql在锁数据

select * from performance_schema.events_statements_current\G

#查看当前会话信息,有时候可以分析下会话的操作

select * from sys.session\G

查看死锁信息

注意,查询死锁的信息的语句,记录的是最后一条的死锁信息,之前的是没记录下来的,

查询当前死锁信息的命令:show engine innodb status\G

如果有DEADLOCK标识,也就是说出现过死锁,如果没有,也就是没出现过.

这个界面只会记录最后一个死锁,不是只记录一个,所以你能看到的死锁语句只有一条而且是最近出现的一条,这个要注意一下.

除非你在配置文件my.cnf里面加入下面参数,那就会将死锁记录到mysql的报错文件中,例如默认的mysql.err里面innodb_print_all_deadlocks = 1

也正如开头说的,InnoDB中死锁会自动被检测出,并选择代价较小的事务进行回滚以打破死锁。事务完全回滚后其保持的锁被全部释放,若是仅有单条SQL由于错误发生了回滚则语句保持的锁可能不会被释放,因为InnoDB中不保存哪条语句持有哪些锁的信息。若事务中的select调用了存储函数,函数中的SQL执行失败,则该语句被回滚。

因此,死锁并不危险但频繁出现就有问题了。应用中应做好出现死锁导致事务回滚后的后续处理逻辑,频繁出现就应该和开发沟通去修改业务逻辑避免死锁。

查找锁相关的语句的线程号

很多时我们说假如有个锁等待实在太长,已经不想去执行他,但是他严重阻碍了后面重要语句的执行,那怎么办呢?虽然说能kill掉语句,但是又不确定那条跟他有关系,所以就很头痛,这个时候可以用下面这条语句

select `r`.`trx_wait_started` AS `wait_started`,timediff(now(),`r`.`trx_wait_started`) AS `wait_age`,timestampdiff(SECOND,`r`.`trx_wait_started`,now()) AS `wait_age_secs`,`rl`.`lock_table` AS `locked_table`,`rl`.`lock_index` AS `locked_index`,`rl`.`lock_type` AS `locked_type`,`r`.`trx_id` AS `waiting_trx_id`,`r`.`trx_started` AS `waiting_trx_started`,timediff(now(),`r`.`trx_started`) AS `waiting_trx_age`,`r`.`trx_rows_locked` AS `waiting_trx_rows_locked`,`r`.`trx_rows_modified` AS `waiting_trx_rows_modified`,`r`.`trx_mysql_thread_id` AS `waiting_pid`, `r`.`trx_query` AS `waiting_query`,`rl`.`lock_id` AS `waiting_lock_id`,`rl`.`lock_mode` AS `waiting_lock_mode`,`b`.`trx_id` AS `blocking_trx_id`,`b`.`trx_mysql_thread_id` AS `blocking_pid`, `b`.`trx_query` AS `blocking_query`,`bl`.`lock_id` AS `blocking_lock_id`,`bl`.`lock_mode` AS `blocking_lock_mode`,`b`.`trx_started` AS `blocking_trx_started`,timediff(now(),`b`.`trx_started`) AS `blocking_trx_age`,`b`.`trx_rows_locked` AS `blocking_trx_rows_locked`,`b`.`trx_rows_modified` AS `blocking_trx_rows_modified`,concat('KILL QUERY ',`b`.`trx_mysql_thread_id`) AS `sql_kill_blocking_query`,concat('KILL ',`b`.`trx_mysql_thread_id`) AS `sql_kill_blocking_connection` from ((((`information_schema`.`innodb_lock_waits` `w` join `information_schema`.`innodb_trx` `b` on((`b`.`trx_id` = `w`.`blocking_trx_id`))) join `information_schema`.`innodb_trx` `r` on((`r`.`trx_id` = `w`.`requesting_trx_id`))) join `information_schema`.`innodb_locks` `bl` on((`bl`.`lock_id` = `w`.`blocking_lock_id`))) join `information_schema`.`innodb_locks` `rl` on((`rl`.`lock_id` = `w`.`requested_lock_id`))) order by `r`.`trx_wait_started`\G

这条语句会计算出当前锁的关联信息,如果你不想那个锁继续执行,那就执行最后显示的kill命令就可以了.

附录一些information_schema表结构的信息information_schema > desc innodb_locks;

+-------------+---------------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------------+---------------------+------+-----+---------+-------+

| lock_id | varchar(81) | NO | | | |#锁ID

| lock_trx_id | varchar(18) | NO | | | |#拥有锁的事务ID

| lock_mode | varchar(32) | NO | | | |#锁模式

| lock_type | varchar(32) | NO | | | |#锁类型

| lock_table | varchar(1024) | NO | | | |#被锁的表

| lock_index | varchar(1024) | YES | | NULL | |#被锁的索引

| lock_space | bigint(21) unsigned | YES | | NULL | |#被锁的表空间号

| lock_page | bigint(21) unsigned | YES | | NULL | |#被锁的页号

| lock_rec | bigint(21) unsigned | YES | | NULL | |#被锁的记录号

| lock_data | varchar(8192) | YES | | NULL | |#被锁的数据

+-------------+---------------------+------+-----+---------+-------+

10 rows in set (0.00 sec)

information_schema  > desc innodb_lock_waits;

+-------------------+-------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------------------+-------------+------+-----+---------+-------+

| requesting_trx_id | varchar(18) | NO | | | |#请求锁的事务ID

| requested_lock_id | varchar(81) | NO | | | |#请求锁的锁ID

| blocking_trx_id | varchar(18) | NO | | | |#当前拥有锁的事务ID

| blocking_lock_id | varchar(81) | NO | | | |#当前拥有锁的锁ID

+-------------------+-------------+------+-----+---------+-------+

4 rows in set (0.00 sec)

information_schema  > desc innodb_trx ;

+----------------------------+---------------------+------+-----+---------------------+-------+

| Field | Type | Null | Key | Default | Extra |

+----------------------------+---------------------+------+-----+---------------------+-------+

| trx_id | varchar(18) | NO | | | |#事务ID

| trx_state | varchar(13) | NO | | | |#事务状态:

| trx_started | datetime | NO | | 0000-00-00 00:00:00 | |#事务开始时间;

| trx_requested_lock_id | varchar(81) | YES | | NULL | |#innodb_locks.lock_id

| trx_wait_started | datetime | YES | | NULL | |#事务开始等待的时间

| trx_weight | bigint(21) unsigned | NO | | 0 | |#

| trx_mysql_thread_id | bigint(21) unsigned | NO | | 0 | |#事务线程ID

| trx_query | varchar(1024) | YES | | NULL | |#具体SQL语句

| trx_operation_state | varchar(64) | YES | | NULL | |#事务当前操作状态

| trx_tables_in_use | bigint(21) unsigned | NO | | 0 | |#事务中有多少个表被使用

| trx_tables_locked | bigint(21) unsigned | NO | | 0 | |#事务拥有多少个锁

| trx_lock_structs | bigint(21) unsigned | NO | | 0 | |#

| trx_lock_memory_bytes | bigint(21) unsigned | NO | | 0 | |#事务锁住的内存大小(B)

| trx_rows_locked | bigint(21) unsigned | NO | | 0 | |#事务锁住的行数

| trx_rows_modified | bigint(21) unsigned | NO | | 0 | |#事务更改的行数

| trx_concurrency_tickets | bigint(21) unsigned | NO | | 0 | |#事务并发票数

| trx_isolation_level | varchar(16) | NO | | | |#事务隔离级别

| trx_unique_checks | int(1) | NO | | 0 | |#是否唯一性检查

| trx_foreign_key_checks | int(1) | NO | | 0 | |#是否外键检查

| trx_last_foreign_key_error | varchar(256) | YES | | NULL | |#最后的外键错误

| trx_adaptive_hash_latched | int(1) | NO | | 0 | |#

| trx_adaptive_hash_timeout | bigint(21) unsigned | NO | | 0 | |#

+----------------------------+---------------------+------+-----+---------------------+-------+

22 rows in set (0.01 sec)

XA事务锁

严格来说,XA事务分为内部XA和外部XA,我们常说的XA事务其实指的是外部XA事务,内部XA一般只涉及binlog提交,和外部XA没关联。

外部xa事务是分布式事务的意思,可以实现不同数据库同步查询和修改数据的目的,mysql很久之前就支持XA事务了,不过仅支持innodb引擎。在5.7之前性能都非常差,并难以保证数据一致性,所以几乎没人用,在5.7较后的版本中,mysql修复了XA事务的bug,数据一致性得到了很大得提高,所以就可以提上使用的日情了。但是,性能还是很差。。。

所谓不同数据库的同步,可以同是mysql,也可以是mysql+sql server或mysql+oracle。不过无论你是什么组合都好,都必须要有一个XA事务控制器,用来统筹XA事务的锁和提交、回滚,可以是用开源的,也可以是你自己写的(技术足够NB)。

而我们用的环境是mysql+sql server外加spring could的XA事务控制器,怎么安装我就不多说了,这篇文章也不是重点介绍这个方面,重点来说XA事务锁的问题。

好了,回归正题,XA事务锁是个让人很头痛的问题,因为动不动就是来个表锁,为什么说性能很差,也正是因为并发大的时候会出现很多表锁,而表锁的粒度太大,直接就影响了并发。

而且XA的bug虽然修复了,但是机制的缺陷还是存在,mysql没有记录外部xa日志,在5.7也只是prepare和commit的时候才记录binlog,粒度还不够。xa事务在你控制器断掉了之后,事务节点会一直死等你控制器起来。如果节点自己有日志可以判断事务是要回滚还是提交就自己做,他自己没办法判断就要等控制器,一直等的结果可能就是挂死一个表的锁,如果是读锁就阻碍alter,如果是写锁就完全拒绝别的表操作,最终导致服务不能使用,这是坑点。

说了那么多,是怎么查XA事务锁呢,上面那些语句是查询一般sql的锁,XA事务的锁,是无能为力的,也就只有查到一些事务在跑,但是没有任何信息,你也不能判断他是否XA事务,因为XA事务有专门的命令提供:#启动XA事务

XA {START|BEGIN} xid [JOIN|RESUME]

#完成XA事务

XA END xid [SUSPEND [FOR MIGRATE]]

#PREPARE这个XA事务

XA PREPARE xid

#提交这个XA事务

XA COMMIT xid [ONE PHASE]

#回滚这个XA事务

XA ROLLBACK xid

#查看正在运行的XA事务

XA RECOVER [CONVERT XID]

来看看实际例子:#一般情况下,你用查锁的语句是查不到的,因为他不是mysql的锁,而是XA的锁

mysql> SELECT * FROM information_schema.innodb_locks\G

Empty set, 1 warning (0.00 sec)

#但是你去查事务的话,倒是能查到出来,但是没任何信息,意义有点比较虚

mysql> SELECT * FROM information_schema.INNODB_TRX\G

*************************** 1. row ***************************

trx_id: 148834655

trx_state: RUNNING

trx_started: 2017-09-28 22:58:19

trx_requested_lock_id: NULL

trx_wait_started: NULL

trx_weight: 9

trx_mysql_thread_id: 0

trx_query: NULL

trx_operation_state: NULL

trx_tables_in_use: 0

trx_tables_locked: 2

trx_lock_structs: 2

trx_lock_memory_bytes: 1136

trx_rows_locked: 0

trx_rows_modified: 7

trx_concurrency_tickets: 0

trx_isolation_level: REPEATABLE READ

trx_unique_checks: 1

trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

trx_adaptive_hash_latched: 0

trx_adaptive_hash_timeout: 0

trx_is_read_only: 0

trx_autocommit_non_locking: 0

#但是并不是语句有问题,而是根本没用对

#XA就是要用XA的语句

mysql> xa recover;

+------------+--------------+--------------+-------------------------------------------------------+

| formatID   | gtrid_length | bqual_length | data                                                  |

+------------+--------------+--------------+-------------------------------------------------------+

| 1096044365 |           32 |           21 | 192.168.0.3.tm150617699467628158192.168.0.3.tm1123145 |

+------------+--------------+--------------+-------------------------------------------------------+

1 row in set (0.00 sec)

#连续查几次,这个XA事务还在,基本上可以肯定这个XA事务是挂起的了,怎么办?

#那就回滚吧,注意逗号的位置

mysql> XA ROLLBACK '192.168.0.3.tm150617699467628158','192.168.0.3.tm1123145',1096044365;

Query OK, 0 rows affected (0.00 sec)

mysql> xa recover;

Empty set (0.00 sec)

#彻底清空了

#再去尝试查这个事务,就不存在了

mysql> SELECT * FROM information_schema.INNODB_TRX\G

Empty set (0.00 sec)

这个时候,锁没了,系统又恢复正常了。一个正常的XA事务,虽然是表锁,但是执行时间一般不会很长,所以长时间挂起的并不多,这类一般就建议回滚,因为你也不知道究竟提交后会影响什么数据,或者说你根本不知道能不能提交。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值