转 How to Find Out Who Is Locking a Table in MySQL
MySQL is adding more tools to monitor its internals with every new release, but one thing it still lacks is a way to find out who is locking what, and therefore which transactions block which other ones. This is such a vital feature that I’m considering writing my own patch to the source! Still, it is possible, to a limited extent, to find out who’s locking resources. In this article I’ll explain how you can do that.
This article is the second in a series on how to use the innotop
MySQL and InnoDB monitor.
Introduction
Here’s the situation: you are trying to update a table and every time you issue the query, it hangs until it times out and tells you the lock wait timeout was exceeded. Someone has locked the table you’re trying to update, but you have no idea who. This can be incredibly frustrating, because this could go on indefinitely. I’ve sometimes had to put work off till another day, because the table is locked all day long.
I’ve found only a very limited set of circumstances in which MySQL will say what’s happening with locks. These are all printed out in the text of SHOW ENGINE INNODB STATUS
.
When there was a deadlock
The first way to see locks is when there’s been a deadlock. The status text will show transaction information on the transactions that deadlocked, which locks they held, and which they were waiting for. Here is a sample. Look at the sections titled “WAITING FOR THIS LOCK TO BE GRANTED” and “HOLDS THE LOCKS.”
------------------------
LATEST DETECTED DEADLOCK
------------------------
060731 20:19:58
*** (1) TRANSACTION:
TRANSACTION 0 93698, ACTIVE 2 sec, process no 12767, OS thread id 1141946720 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1216
MySQL thread id 3, query id 19 localhost root Updating
update test.innodb_deadlock_maker set a = 0 where a <> 0
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 131120 n bits 72 index `GEN_CLUST_INDEX` of table `test/innodb_deadlock_maker` trx id 0 93698 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000019000; asc ;; 1: len 6; hex 000000016e01; asc n ;; 2: len 7; hex 80000000320110; asc 2 ;; 3: len 4; hex 80000000; asc ;;
*** (2) TRANSACTION:
TRANSACTION 0 93699, ACTIVE 2 sec, process no 12767, OS thread id 1142212960 starting index read, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1216
MySQL thread id 4, query id 20 localhost root Updating
update test.innodb_deadlock_maker set a = 1 where a <> 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 131120 n bits 72 index `GEN_CLUST_INDEX` of table `test/innodb_deadlock_maker` trx id 0 93699 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000019000; asc ;; 1: len 6; hex 000000016e01; asc n ;; 2: len 7; hex 80000000320110; asc 2 ;; 3: len 4; hex 80000000; asc ;;
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000019001; asc ;; 1: len 6; hex 000000016e01; asc n ;; 2: len 7; hex 8000000032011f; asc 2 ;; 3: len 4; hex 80000001; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 131120 n bits 72 index `GEN_CLUST_INDEX` of table `test/innodb_deadlock_maker` trx id 0 93699 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000019000; asc ;; 1: len 6; hex 000000016e01; asc n ;; 2: len 7; hex 80000000320110; asc 2 ;; 3: len 4; hex 80000000; asc ;;
*** WE ROLL BACK TRANSACTION (2)
More importantly, the lines beginning “RECORD LOCKS space id 0” show which index of which table was locked. That is the real meat of the matter—that’s what you need to know.
There’s just one problem: after there’s been a deadlock, it’s too late. You don’t want to know what held locks in the past, you want to know what holds them now. The deadlock information isn’t usually helpful in finding out what transaction is blocking something from happening.
When a transaction is waiting for locks
The next place you can sometimes see lock information is in the transaction section of the output. Here’s a sample:
---TRANSACTION 0 93789802, ACTIVE 19 sec, process no 9544, OS thread id 389120018
MySQL thread id 23740, query id 194861248 worker1.office 192.168.0.12 robot
---TRANSACTION 0 93789797, ACTIVE 20 sec, process no 9537, OS thread id 389005359 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 320
MySQL thread id 23733, query id 194861215 elpaso 192.168.0.31 robot Updating
update test.test set col1 = 4
------- TRX HAS BEEN WAITING 20 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 299998 n bits 200 index `PRIMARY` of table `test/test` trx id 0 93789797 lock_mode X locks rec but not gap waiting
Record lock, heap no 77 PHYSICAL RECORD: n_fields 15; compact format; info bits 0
0: len 4; hex 80474fd6; asc GO ;; 1: len 6; hex 000005970680; asc ;; 2: len 7; hex 000017c02b176c; asc + l;; 3: len 4; hex 80000003; asc ;; 4: len 8; hex 800000000da0c93a; asc :;; 5: len 8; hex 800000000eb2ea7e; asc ~;; 6: len 4; hex c771fe44; asc q D;; 7: len 4; hex 8000003e; asc >;; 8: len 8; hex 8000123eb9e5dfd5; asc > ;; 9: len 4; hex 8000003a; asc :;; 10: len 8; hex 8000123eb9e43603; asc > 6 ;; 11: len 4; hex 80000035; asc 5;; 12: len 8; hex 8000123eb9d6c130; asc > 0;; 13: len 4; hex 80000033; asc 3;; 14: len 8; hex 8000123eb9c7c853; asc > S;;
---------------------
---TRANSACTION 0 93789679, ACTIVE 31082 sec, process no 9535, OS thread id 388972583 starting index read, thread declared inside InnoDB 6
mysql tables in use 4, locked 4
11614 lock struct(s), heap size 683328
MySQL thread id 23731, query id 194861117 elpaso 192.168.0.31 robot
Notice the first transaction has been waiting 20 seconds for a lock to be granted, and it tells you which table and index as above. The other transaction I included (there were many in this section, but I omitted most) says it has 4 tables open and 4 locked. What it doesn’t say is which ones.
Again, there’s some information here, but not a lot. If you issue a query and it hangs and waits for a lock, knowing what lock it’s waiting for isn’t really helpful. And knowing some other transaction holds a lock isn’t always helpful either.
It can be useful sometimes though, and that’s better than nothing. If you only see two transactions with locks, you know the one that’s not waiting for a lock is probably the one that holds them. Notice something scary in the information above? Transaction “0 93789771”, on connection 23731, has been active for… eight and a half hours! Whoa. It’s time to find out what owns that connectionand possibly kill it.
The take-away here is, if you’re getting blocked on an InnoDB table, and you’re lucky enough to see only one other transaction with locks, it’s probably the one blocking you.
What about table locks?
Ah, good question. What if the table isn’t InnoDB, or what if someone locked it with LOCK TABLES
, and it doesn’t show up in the output of SHOW ENGINE INNODB STATUS
? As far as I know, you’re helpless. I don’t know how to get any information on who’s locking the table then. Table lock information doesn’t seem to be exposed in any fashion—only row lock information.
In fact, if you’re in a transaction, LOCK TABLES
seems to “kick you out” of the transaction. Try experimenting with START TRANSACTION
and LOCK TABLES
on an InnoDB table, and you’ll see what I mean. If you lock a table for writing, then try to select from it in another connection, the other connection will block. If you then issue START TRANSACTION
on the first connection, the second connection will immediately unblock, and the first connection’s transaction will disappear from the InnoDB status text.
Who wants to read all that mess?
Who, indeed? The text I included above is a pain to read, and it’s not even representative of what you’ll really be looking at. For one thing, you might have to scan through 40 or more transactions to find the ones you care about, and then there’s all the other information in the output, some of which can be voluminous (such as deadlocks). What a hassle!
Fortunately, there’s a tool to do that for you: innotop
. This tool formats the output neatly and gives you filtering options to display only transactions with locks (or just sort them to the top of the display). Here’s how you can do that:
Start innotop and use the “T” key to enter InnoDB Transaction mode, if it’s not already in that mode. You will see a list of transactions. Next, make the “Locks,” “Tbl Used,” and “Tbl Lck” columns visible. Press the “c” key to activate the “choose columns” dialog.
Now sort transactions with locks to the top by pressing the “s” key and choosing lock_structs
as the sort column. You may need to press the “r” key afterwards to reverse the sort order if they go to the bottom instead. Alternatively, you can use the “w” key to add a filter on the lock_structs
column, such as “[1-9]” to match only rows where the column isn’t zero (this is a handy filter to add in general, just so you can see how many transactions have locks).
When you have a very busy server it can really help to hide all the transactions without locks.
Isn’t that easier than digging through the output of SHOW ENGINE INNODB STATUS
? I think so.
Is there more?
Though I’ve searched the Internet, searched the source code and the MySQL manual, I haven’t been able to find any other ways to get information on current locks in MySQL. But I’d be delighted if you prove me wrong! If you have anything to add, please comment.
想看那些乱七八糟的东西?
真的是谁?我上面的文章读起来很痛苦,甚至不能代表你真正要看的东西。首先,您可能需要扫描40个或更多事务,以找到您关心的事务,然后在输出中包含所有其他信息,其中一些信息可能非常庞大(例如死锁)。真麻烦!
幸运的是,有一个工具可以帮你做到这一点:innotop。这个工具可以灵活地格式化输出,并为您提供筛选选项,以便仅显示带有锁的事务(或者将它们排序到显示的顶部)。你可以这样做:
启动innotop并使用“t”键进入innodb事务模式(如果它还没有进入该模式)。您将看到一个事务列表。接下来,使“locks”、“tbl used”和“tbl lck”列可见。按“C”键激活“选择列”对话框。
现在,通过按“s”键并选择lock_structs作为排序列,将带有锁的事务排序到顶部。如果它们转到底部,则可能需要在之后按“R”键来反转排序顺序。或者,您可以使用“w”键在lock-structscolumn上添加一个过滤器,例如“[1-9]”来只匹配列不为零的行(这是一个方便的过滤器,通常可以添加,这样您就可以看到有多少事务具有锁)。
当您有一个非常繁忙的服务器,它可以真正帮助隐藏所有的事务没有锁。
这难道不比挖掘show engine innodb status的输出更容易吗?我认为是这样。
还有吗?
尽管我搜索过互联网,搜索过源代码和mysql手册,但我还没有找到任何其他方法来获取mysql中当前锁的信息。但如果你证明我错了我会很高兴的!如果你有什么要补充的,请评论。
################# sample 0
http://blog.itpub.net/28916011/viewspace-2158012/
--sys库的介绍
mysql 5.6也可以有sys库(基于performance_schema的视图)。sys库是一个开源项目,在githup上早就有,是一个DBA的开源工具,后来mysql感觉好,就放在了mysql5.7上。
下载地址:https://github.com/mysql/mysql-sys
[tms@m-db3 ~]$ cd mysql-sys-master
[tms@m-db3 ~]$ mysql < sys_56.sql
这样,就可以在mysql5.6里面加入sys库了,不过mysql 5.6只有88张表,而mysql 5.7有101张,这是因为Mysql 5.7的performace_schema库里面又多了几张表。
sys库是performance_schema的视图。
--MySQL锁等待
当Mysql发生锁等待情况时,可以通过如下语句来在线查看:
mysql> select * from sys.innodb_lock_waits \G;
*************************** 1. row ***************************
wait_started: 2018-07-16 16:25:17 //锁等待开始的时间,16:25开始等待
wait_age: 00:10:08 //发现问题时已经等待了10分钟了
wait_age_secs: 608 //608秒,也就是等10分钟了
locked_table: `iws`.`busi_reconciliationgbgsinfo_inputdetails` //被锁住的表名
locked_index: PRIMARY //被锁住的索引
locked_type: RECORD //锁的类型为行锁
waiting_trx_id: 13666265 //waiting transaction id,正在等待事务的id号
waiting_trx_started: 2018-07-16 16:24:54 //这个事务是从16:24开始等待
waiting_trx_age: 00:10:31 //等了10分钟了
waiting_trx_rows_locked: 1 //正在等待的这个事务锁住了1行记录
waiting_trx_rows_modified: 0 //正在等待的这个事务修改了0行记录
waiting_pid: 441805 //这个等待事务的线程id是多少,通过show processlist 命令可以查到它,结果看到是一个sleep的线程,没有执行具体sql语句,见下
waiting_query: update busi_reconciliationgbgs ... where id = 4510 //等待锁释放的语句
waiting_lock_id: 13666265:2924:21:94 //正在等待的锁id
waiting_lock_mode: X //等待锁的类型是排它锁
blocking_trx_id: 13666259 //这个事务id阻塞了waiting lock
blocking_pid: 441803 阻塞事务的pid
blocking_query: NULL //阻塞事务的sql语句
blocking_lock_id: 13666259:2924:21:94
blocking_lock_mode: X
blocking_trx_started: 2018-07-16 16:24:51
blocking_trx_age: 00:10:34
blocking_trx_rows_locked: 1
blocking_trx_rows_modified: 1
sql_kill_blocking_query: KILL QUERY 441803
sql_kill_blocking_connection: KILL 441803
1 row in set (0.00 sec)
ERROR:
No query specified
上面看到输出了很多的东西,看的我都蒙圈了。后来查看mysql官方文档,慢慢的才发现,其实只关注上面的waiting_pid、waiting_query和blocking_pid、blocking_query四个参数即可;其中waiting_pid和blocking_pid两个参数就是通过执行show processlist命令里面输出的线程id号,如下:
mysql> show full processlist \G;
*************************** 8. row ***************************
Id: 441803
User: iws
Host: 172.16.21.7:46121
db: iws
Command: Sleep
Time: 655
State:
Info: NULL
*************************** 9. row ***************************
Id: 441805
User: iws
Host: 172.16.21.7:46122
db: iws
Command: Query
Time: 652
State: updating
Info: update busi_reconciliationgbgsinfo_inputdetails set bgs_id = 1622 , date = '2018-06-24 00:00:00' , awbnumber = '006-85516771' , incidental = 15.00 , entry_exit = 23.00 , warehousing_fee = 0.00 , loading_unloading = 0.00 , other = 0.00 , total = 38.00 , state = 20 , comparison_resultsid = 30 , confirmation_method = '人工' , confirmationid = 'root' , confirmationtime = '2018-07-16 16:25:17' , confirmation_note = '.' , createtime = '2018-06-24 20:00:07' , createrid = '9862ebdbaf3249a88bcaa8f01bde0471' where id = 4510
通过上面两个的输出结果,我们明白了,是441803线程锁住了表,造成线程441805的等待。
我们看到发生等待的线程441805对应的sql语句是:update busi_reconciliationgbgs ... where id = 4510,但是锁表的线程441803对应的sql语句竟然是Null。这就更让人迷惑了。
于是我默默的翻开了ysql官方文档,原来里面已经对这个null专门做了说明。
官方文档说,要想找到这个null值对应的阻塞语句,可以通过下面几个步骤寻找:
a)、根据锁表的processlist id 441803,运用如下sql,找到null对应的sql语句,如下:
SELECT SQL_TEXT FROM performance_schema.events_statements_current WHERE THREAD_ID
in
(SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=441803)
b)、如果上面找到的sql语句,你还是不能分析出为什么他们会锁表,一直拿着锁不释放,那么你可以查看 performance_schema.events_statements_history表里面最近执行过的10条sql(假设上面查到的thread_id=28):
SELECT EVENT_ID,CURRENT_SCHEMA, SQL_TEXT FROM performance_schema.events_statements_history WHERE THREAD_ID
in
(SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=441803) order by event_id
其他:
上面查询锁的sql可以只关注已下几个列,如下:
SELECT
wait_started,
wait_age,
waiting_pid,
waiting_query,
blocking_trx_id,
blocking_pid,
blocking_query,
blocking_lock_mode,
sql_kill_blocking_query
FROM
sys.innodb_lock_waits
~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~
最近我用python 2.6写了个自动杀锁的脚本,只要发现sys.innodb_lock_waits表里面有锁表的内容,就杀死相应的sql线程,并输出杀死sql的内容到当前目录下:
#!/usr/bin/env python
#-*-coding:utf8-*-
#下载rpm包安装,下载地址:https://dev.mysql.com/downloads/connector/python/,注意mysql-connector-python版本需要是1.1.17的,2.x的版本运行会有问题
from __future__ import print_function
import mysql.connector as mdb
import os
#全局变量
username = 'root'
password = ''
hostname = 'localhost'
database = 'sys'
#配置信息
config = {
'user': username,
'password': password,
'host': hostname,
'database': database
}
#定义函数,查看锁表的行数
def Get_sys_lock():
show_locked_num = "select count(*) from sys.innodb_lock_waits"
cursor.execute(show_locked_num)
for i in cursor:
locked_sql_num = i[0]
return locked_sql_num
#定义函数,如果有锁表,就重定向到locked_sql.txt文件里面
def show_locked_sql():
count = 0
count1 = 0
#如果日志文件存在就删除
if os.path.isfile('locked_sql.txt'):
os.remove('locked_sql.txt')
if os.path.isfile('null_sql.txt'):
os.remove('null_sql.txt')
if os.path.isfile('last_10_null_sql.txt'):
os.remove('last_10_null_sql.txt')
#引用函数
locked_sql_num = Get_sys_lock()
print("锁表的行数是:{0}".format(locked_sql_num))
if locked_sql_num > 0: #如果有锁表
show_locked_sql = " SELECT \
wait_started, \
wait_age, \
waiting_pid, \
waiting_query, \
blocking_trx_id, \
blocking_pid, \
blocking_query, \
blocking_lock_mode, \
sql_kill_blocking_query \
FROM \
sys.innodb_lock_waits \
"
cursor.execute(show_locked_sql)
for i in cursor:
wait_started = i[0]
wait_age = i[1]
waiting_pid = i[2]
waiting_query = i[3]
blocking_trx_id = i[4]
blocking_pid = i[5]
blocking_query = i[6]
blocking_lock_mode = i[7]
sql_kill_blocking_query = i[8]
if not str(blocking_query).strip(): #如果blocking_query字符串为Null
#import pdb;pdb.set_trace()
show_null_sql = "SELECT SQL_TEXT FROM performance_schema.events_statements_current WHERE THREAD_ID in (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=%s)" % blocking_pid
conn = mdb.connect(**config)
cursor1 = conn.cursor()
cursor1.execute(show_null_sql)
#print(cursor1.fetchall())
for j in cursor1:
SQL_TEXT = j[0]
print(SQL_TEXT)
cursor1.close
try:
count1 += 1
f = open('null_sql.txt','a') #a表示追加
f.write (
'##########' + 'The ' + str(count1) + ' rows ' + 'Blocking null query对应的具体sql为##########\n' +
'blocking_pid: ' + str(blocking_pid) + '\n'
'sql_text: ' + str(SQL_TEXT) + '\n\n'
)
except OSError as reason:
print('出错了:' + str(reason))
finally:
f.close
#再查看null对应的最后10条sql
show_last_10_null_sql = "SELECT EVENT_ID,CURRENT_SCHEMA, SQL_TEXT FROM performance_schema.events_statements_history WHERE THREAD_ID in (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID=%s) order by event_id" % blocking_pid
cursor2 = conn.cursor()
cursor2.execute(show_last_10_null_sql)
cursor2.close
#print(cursor1.fetchall())
count2 = 0
for j in cursor2:
EVENT_ID = j[0]
CURRENT_SCHEMA = j[1]
SQL_TEXT = j[2]
try:
count2 += 1
f = open('last_10_null_sql.txt','a') #a表示追加
f.write (
'##########' + 'The ' + str(count2) + ' rows ' + 'laster blocking null query对应的具体sql为##########\n' +
'blocking_pid: ' + str(blocking_pid) + '\n'
'EVENT_ID: ' + str(EVENT_ID) + '\n'
'CURRENT_SCHEMA: ' + str(CURRENT_SCHEMA) + '\n'
'SQL_TEXT: ' + str(SQL_TEXT) + '\n\n'
)
except OSError as reason:
print('出错了:' + str(reason))
finally:
f.close
#把锁表的情况重定向到一个locked_sql.txt文件里面
try:
count += 1
f = open('locked_sql.txt','a') #a表示追加
f.write('##########' + 'The ' + str(count) + ' rows' + '###########\n')
f.write (
'wait_started: ' + str(wait_started) + '\n' +
'wait_age: ' + str(wait_age) + '\n' +
'waiting_pid: ' + str(waiting_pid ) + '\n' +
'waiting_query: ' + str(waiting_query) + '\n' +
'blocking_trx_id: ' + str(blocking_trx_id) + '\n' +
'blocking_pid: ' + str(blocking_pid) + '\n' +
'blocking_query: ' + str(blocking_query) + '\n' +
'blocking_lock_mode: ' + str(blocking_lock_mode) + '\n' +
'sql_kill_blocking_query: ' + str(sql_kill_blocking_query) + '\n\n'
)
'''
f.write (
'##########' + 'Blocking null query对应的具体sql为##########\n' +
'blocking_pid:' + str(blocking_pid) +
'sql_text:' + str(SQL_TEXT)
)
'''
except OSError as reason:
print('出错了:' + str(reason))
finally:
f.close
#定义函数,列出当前所有执行的sql线程
def show_processlist():
count = 0
#如果日志文件存在就删除
if os.path.isfile('show_processlist.txt'):
os.remove('show_processlist.txt')
#引用函数
locked_sql_num = Get_sys_lock()
#print("锁表的行数是:{0}".format(locked_sql_num))
if locked_sql_num > 0: #如果有锁表
show_processlist = "select \
id, \
user, \
host, \
db, \
time, \
state, \
info \
from information_schema.`PROCESSLIST` order by time desc \
"
cursor.execute(show_processlist)
for i in cursor:
id = i[0]
user = i[1]
host = i[2]
db = i[3]
time = i[4]
state = i[5]
info = i[6]
#把锁表的情况重定向到一个show_processlist.txt文件里面
try:
count += 1
f = open('show_processlist.txt','a') #a表示追加
f.write('##########' + 'The ' + str(count) + ' rows' + '###########\n')
f.write (
'id: ' + str(id) + '\n' +
'user: ' + str(user) + '\n' +
'host: ' + str(host) + '\n' +
'db: ' + str(db) + '\n' +
'time: ' + str(time) + '\n' +
'state: ' + str(state) + '\n' +
'info: ' + str(info) + '\n\n'
)
except OSError as reason:
print('出错了:' + str(reason))
finally:
f.close
#定义函数,如果有锁表,就杀死
def kill_locked_sql():
#引用函数
locked_sql_num = Get_sys_lock()
#print("锁表的行数是:{0}".format(locked_sql_num))
if locked_sql_num > 0: #如果有锁表
execute_locked_sql = " SELECT \
sql_kill_blocking_query \
FROM \
sys.innodb_lock_waits \
"
cursor.execute(execute_locked_sql)
for i in cursor:
sql_kill_blocking_query = i[0]
conn = mdb.connect(**config)
cursor1 = conn.cursor()
try:
cursor1.execute(sql_kill_blocking_query)
except:
print('出错了')
cursor1.close
#主程序
conn = mdb.connect(**config)
cursor = conn.cursor()
show_locked_sql()
show_processlist()
kill_locked_sql()
cursor.close
conn.close
--参考文档:
https://dev.mysql.com/doc/refman/8.0/en/sys-innodb-lock-waits.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-information-schema-examples.html#innodb-information-schema-examples-null-blocking-query
### sample 1 show engine status 数据 挖掘
https://mariadb.com/kb/en/library/show-engine-innodb-status/
-》监控智能监控到innnodb lock table 的数目,无法查到innodb 的锁的源头。
-》后继可以在 5.6 加入 sys 对象。使用这个对象监控。
select waiting_pid as '被阻塞线程',
waiting_query as '被阻塞SQL',
blocking_pid as '阻塞线程',
blocking_query as '阻塞SQL',
wait_age as '阻塞时间',
sql_kill_blocking_query as '建议操作'
from sys.innodb_lock_waits
where(unix_timestamp()-unix_timestamp(wait_started))>阻塞秒数
(这个语句可以检查 行锁,但是无法检查 lock table test read; flush table 锁 )
-》网上说innotop 可以图形化查找出来,但是还没调试出
(可以检查 行锁,但是无法检查 lock table test read; flush table 锁 )
-》 SHOW ENGINE INNODB STATUS 中的 TRANSACTIONS 段,可以看到 正在锁的表,和该锁的 session_id.
(可以检查 行锁,也可以检查 lock table test read; flush table 锁 )
SHOW ENGINE INNODB STATUS
SHOW ENGINE INNODB STATUS
is a specific form of the SHOW ENGINE statement that displays the InnoDB Monitor output, which is extensive InnoDB information which can be useful in diagnosing problems.
The following sections are displayed
- Status: Shows the timestamp, monitor name and the number of seconds, or the elapsed time between the current time and the time the InnoDB Monitor output was last displayed. The per-second averages are based upon this time.
- BACKGROUND THREAD: srv_master_thread lines show work performed by the main background thread.
- SEMAPHORES: Threads waiting for a semaphore and stats on how the number of times threads have needed a spin or a wait on a mutex or rw-lock semaphore. If this number of threads is large, there may be I/O or contention issues. Reducing the size of the innodb_thread_concurrency system variable may help if contention is related to thread scheduling.
Spin rounds per wait
shows the number of spinlock rounds per OS wait for a mutex. - LATEST FOREIGN KEY ERROR: Only shown if there has been a foreign key constraint error, it displays the failed statement and information about the constraint and the related tables.
- LATEST DETECTED DEADLOCK: Only shown if there has been a deadlock, it displays the transactions involved in the deadlock and the statements being executed, held and required locked and the transaction rolled back to.
- TRANSACTIONS: The output of this section can help identify lock contention, as well as reasons for the deadlocks.
- FILE I/O: InnoDB thread information as well as pending I/O operations and I/O performance statistics.
- INSERT BUFFER AND ADAPTIVE HASH INDEX: InnoDB insert buffer and adaptive hash index status information, including the number of each type of operation performed, and adaptive hash index performance.
- LOG: InnoDB log information, including current log sequence number, how far the log has been flushed to disk, the position at which InnoDB last took a checkpoint, pending writes and write performance statistics.
- BUFFER POOL AND MEMORY: Information on buffer pool pages read and written, which allows you to see the number of data file I/O operations performed by your queries. See InnoDB Buffer Pool for more. Similar information is also available from the INFORMATION_SCHEMA.INNODB_BUFFER_POOL_STATS table.
- ROW OPERATIONS:Information about the main thread, including the number and performance rate for each type of row operation.
If the innodb_status_output_locks system variable is set to 1
, extended lock information will be displayed.
Example output:
=====================================
2019-09-06 12:44:13 0x7f93cc236700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 2 srv_active, 0 srv_shutdown, 83698 srv_idle
srv_master_thread log flush and writes: 83682
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 15
OS WAIT ARRAY INFO: signal count 8
RW-shared spins 0, rounds 20, OS waits 7
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 20.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 236
Purge done for trx's n:o < 236 undo n:o < 0 state: running
History list length 22
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421747401994584, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421747401990328, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
286 OS file reads, 171 OS file writes, 22 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 445926
Log flushed up to 445926
Pages flushed up to 445926
Last checkpoint at 445917
0 pending log flushes, 0 pending chkp writes
18 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 167772160
Dictionary memory allocated 50768
Buffer pool size 8012
Free buffers 7611
Database pages 401
Old database pages 0
Modified db pages 0
Percent of dirty pages(LRU & free pages): 0.000
Max dirty pages percent: 75.000
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 264, created 137, written 156
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 401, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=4267, Main thread ID=140272021272320, state: sleeping
Number of rows inserted 1, updated 0, deleted 0, read 1
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
Number of system rows inserted 0, updated 0, deleted 0, read 0
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
Comments
#######sample 2 模拟 innodb 锁
https://www.cnblogs.com/phpfeng/p/9374002.html
当选择存储引擎为innodb时,默认会为每一条insert\update\delete语句启动事务,根据mysql的autocommit参数设置来决定是否自动提交,每次执行修改语句时,不会直接修改物理表,会将结果写入缓冲区,直到触发commit才会真正的修改表数据.
autocommit参数都是开启状态[1],条件说明:
1.autocommit=0,不会自动提交,需要手动commit;
2.autocommit=1,每次执行修改语句会自动执行commit,但是在transcation流程控制中不会触发。
命令:
select @@autocommit; --查看autocommit配置
set autocommit=1; --设置参数[0,1]
InnoDB的行锁模式及加锁方法
请求锁模式
是否兼容
当前锁模式
|
X
|
IX
|
S
|
IS
|
X
|
冲突
|
冲突
|
冲突
|
冲突
|
IX
|
冲突
|
兼容
|
冲突
|
兼容
|
S
|
冲突
|
冲突
|
兼容
|
兼容
|
IS
|
冲突
|
兼容
|
兼容
|
兼容
|
session_1
|
session_2
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select actor_id,first_name,last_name from actor where actor_id = 178;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select actor_id,first_name,last_name from actor where actor_id = 178;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
|
当前session对actor_id=178的记录加share mode 的共享锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 178lock in share mode;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.01 sec)
|
|
其他session仍然可以查询记录,并也可以对该记录加share mode的共享锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 178lock in share mode;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.01 sec)
|
|
当前session对锁定的记录进行更新操作,等待锁:
mysql> update actor set last_name = 'MONROE T' where actor_id = 178;
等待
|
|
其他session也对该记录进行更新操作,则会导致死锁退出:
mysql> update actor set last_name = 'MONROE T' where actor_id = 178;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|
|
获得锁后,可以成功更新:
mysql> update actor set last_name = 'MONROE T' where actor_id = 178;
Query OK, 1 row affected (17.67 sec)
Rows matched: 1 Changed: 1 Warnings: 0
|
session_1
|
session_2
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select actor_id,first_name,last_name from actor where actor_id = 178;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select actor_id,first_name,last_name from actor where actor_id = 178;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
|
当前session对actor_id=178的记录加for update的排它锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 178 for update;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
|
|
其他session可以查询该记录,但是不能对该记录加共享锁,会等待获得锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 178;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE |
+----------+------------+-----------+
1 row in set (0.00 sec)
mysql> select actor_id,first_name,last_name from actor where actor_id = 178 for update;
等待
|
|
当前session可以对锁定的记录进行更新操作,更新后释放锁:
mysql> update actor set last_name = 'MONROE T' where actor_id = 178;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
|
|
其他session获得锁,得到其他session提交的记录:
mysql> select actor_id,first_name,last_name from actor where actor_id = 178 for update;
+----------+------------+-----------+
| actor_id | first_name | last_name |
+----------+------------+-----------+
| 178 | LISA | MONROE T |
+----------+------------+-----------+
1 row in set (9.59 sec)
|
InnoDB行锁实现方式
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_no_index where id = 1 ;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_no_index where id = 2 ;
+------+------+
| id | name |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)
|
mysql> select * from tab_no_index where id = 1 for update;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
|
|
mysql> select * from tab_no_index where id = 2 for update;
等待
|
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 ;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 2 ;
+------+------+
| id | name |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)
|
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
|
|
mysql> select * from tab_with_index where id = 2 for update;
+------+------+
| id | name |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)
|
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select * from tab_with_index where id = 1 and name = '1' for update;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
+------+------+
1 row in set (0.00 sec)
|
|
虽然session_2访问的是和session_1不同的记录,但是因为使用了相同的索引,所以需要等待锁:
mysql> select * from tab_with_index where id = 1 and name = '4' for update;
等待
|
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id | name |
+------+------+
| 1 | 1 |
| 1 | 4 |
+------+------+
2 rows in set (0.00 sec)
|
|
Session_2使用name的索引访问记录,因为记录没有被索引,所以可以获得锁:
mysql> select * from tab_with_index where name = '2' for update;
+------+------+
| id | name |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)
|
|
由于访问的记录已经被session_1锁定,所以等待获得锁。:
mysql> select * from tab_with_index where name = '4' for update;
|
间隙锁(Next-Key锁)
session_1
|
session_2
|
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
|
当前session对不存在的记录加for update的锁:
mysql> select * from emp where empid = 102 for update;
Empty set (0.00 sec)
|
|
这时,如果其他session插入empid为102的记录(注意:这条记录并不存在),也会出现锁等待:
mysql>insert into emp(empid,...) values(102,...);
阻塞等待
|
|
Session_1 执行rollback:
mysql> rollback;
Query OK, 0 rows affected (13.04 sec)
|
|
由于其他session_1回退后释放了Next-Key锁,当前session可以获得锁并成功插入记录:
mysql>insert into emp(empid,...) values(102,...);
Query OK, 1 row affected (13.35 sec)
|
恢复和复制的需要,对InnoDB锁机制的影响
session_1
|
session_2
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 1 | 1 |
| 5 | 1 | 1 |
| 6 | 1 | 1 |
| 7 | 1 | 1 |
| 8 | 1 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 1 | 1 |
| 5 | 1 | 1 |
| 6 | 1 | 1 |
| 7 | 1 | 1 |
| 8 | 1 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
|
mysql> insert into target_tab select d1,name from source_tab where name = '1';
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
|
|
mysql> update source_tab set name = '1' where name = '8';
等待
|
|
commit;
|
|
返回结果
commit;
|
session_1
|
session_2
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql>set innodb_locks_unsafe_for_binlog='on'
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 1 | 1 |
| 5 | 1 | 1 |
| 6 | 1 | 1 |
| 7 | 1 | 1 |
| 8 | 1 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 1 | 1 |
| 5 | 1 | 1 |
| 6 | 1 | 1 |
| 7 | 1 | 1 |
| 8 | 1 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
|
mysql> insert into target_tab select d1,name from source_tab where name = '1';
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
|
|
session_1未提交,可以对session_1的select的记录进行更新操作。
mysql> update source_tab set name = '8' where name = '1';
Query OK, 5 rows affected (0.00 sec)
Rows matched: 5 Changed: 5 Warnings: 0
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 8 | 1 |
| 5 | 8 | 1 |
| 6 | 8 | 1 |
| 7 | 8 | 1 |
| 8 | 8 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
|
|
更新操作先提交
mysql> commit;
Query OK, 0 rows affected (0.05 sec)
|
|
插入操作后提交
mysql> commit;
Query OK, 0 rows affected (0.07 sec)
|
|
此时查看数据,target_tab中可以插入source_tab更新前的结果,这符合应用逻辑:
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 8 | 1 |
| 5 | 8 | 1 |
| 6 | 8 | 1 |
| 7 | 8 | 1 |
| 8 | 8 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> select * from target_tab;
+------+------+
| id | name |
+------+------+
| 4 | 1.00 |
| 5 | 1.00 |
| 6 | 1.00 |
| 7 | 1.00 |
| 8 | 1.00 |
+------+------+
5 rows in set (0.00 sec)
|
mysql> select * from tt1 where name = '1';
Empty set (0.00 sec)
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
| 4 | 8 | 1 |
| 5 | 8 | 1 |
| 6 | 8 | 1 |
| 7 | 8 | 1 |
| 8 | 8 | 1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> select * from target_tab;
+------+------+
| id | name |
+------+------+
| 4 | 1.00 |
| 5 | 1.00 |
| 6 | 1.00 |
| 7 | 1.00 |
| 8 | 1.00 |
+------+------+
5 rows in set (0.00 sec)
|
InnoDB在不同隔离级别下的一致性读及锁的差异
隔离级别
一致性读和锁
SQL
|
Read Uncommited
|
Read Commited
|
Repeatable Read
|
Serializable
|
|
SQL
|
条件
|
||||
select
|
相等
|
None locks
|
Consisten read/None lock
|
Consisten read/None lock
|
Share locks
|
范围
|
None locks
|
Consisten read/None lock
|
Consisten read/None lock
|
Share Next-Key
|
|
update
|
相等
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
Exclusive locks
|
范围
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
|
Insert
|
N/A
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
replace
|
无键冲突
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
键冲突
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
|
delete
|
相等
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
范围
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
exclusive next-key
|
|
Select ... from ... Lock in share mode
|
相等
|
Share locks
|
Share locks
|
Share locks
|
Share locks
|
范围
|
Share locks
|
Share locks
|
Share Next-Key
|
Share Next-Key
|
|
Select * from ... For update
|
相等
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
exclusive locks
|
范围
|
exclusive locks
|
Share locks
|
exclusive next-key
|
exclusive next-key
|
|
Insert into ... Select ...
(指源表锁)
|
innodb_locks_unsafe_for_binlog=off
|
Share Next-Key
|
Share Next-Key
|
Share Next-Key
|
Share Next-Key
|
innodb_locks_unsafe_for_binlog=on
|
None locks
|
Consisten read/None lock
|
Consisten read/None lock
|
Share Next-Key
|
|
create table ... Select ...
(指源表锁)
|
innodb_locks_unsafe_for_binlog=off
|
Share Next-Key
|
Share Next-Key
|
Share Next-Key
|
Share Next-Key
|
innodb_locks_unsafe_for_binlog=on
|
None locks
|
Consisten read/None lock
|
Consisten read/None lock
|
Share Next-Key
|
什么时候使用表锁
关于死锁
session_1
|
session_2
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from table_1 where where id=1 for update;
...
做一些其他处理...
|
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from table_2 where id=1 for update;
...
|
select * from table_2 where id =1 for update;
因session_2已取得排他锁,等待
|
做一些其他处理...
|
mysql> select * from table_1 where where id=1 for update;
死锁
|
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select first_name,last_name from actor where actor_id = 1 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| PENELOPE | GUINESS |
+------------+-----------+
1 row in set (0.00 sec)
|
|
mysql> insert into country (country_id,country) values(110,'Test');
Query OK, 1 row affected (0.00 sec)
|
|
mysql> insert into country (country_id,country) values(110,'Test');
等待
|
|
mysql> select first_name,last_name from actor where actor_id = 1 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| PENELOPE | GUINESS |
+------------+-----------+
1 row in set (0.00 sec)
|
|
mysql> insert into country (country_id,country) values(110,'Test');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|
session_1
|
session_2
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select first_name,last_name from actor where actor_id = 1 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| PENELOPE | GUINESS |
+------------+-----------+
1 row in set (0.00 sec)
|
|
mysql> select first_name,last_name from actor where actor_id = 3 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| ED | CHASE |
+------------+-----------+
1 row in set (0.00 sec)
|
|
mysql> select first_name,last_name from actor where actor_id = 3 for update;
等待
|
|
mysql> select first_name,last_name from actor where actor_id = 1 for update;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|
|
mysql> select first_name,last_name from actor where actor_id = 3 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| ED | CHASE |
+------------+-----------+
1 row in set (4.71 sec)
|
session_1
|
session_2
|
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
|
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
|
当前session对不存在的记录加for update的锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 201 for update;
Empty set (0.00 sec)
|
|
其他session也可以对不存在的记录加for update的锁:
mysql> select actor_id,first_name,last_name from actor where actor_id = 201 for update;
Empty set (0.00 sec)
|
|
因为其他session也对该记录加了锁,所以当前的插入会等待:
mysql> insert into actor (actor_id , first_name , last_name) values(201,'Lisa','Tom');
等待
|
|
因为其他session已经对记录进行了更新,这时候再插入记录就会提示死锁并退出:
mysql> insert into actor (actor_id, first_name , last_name) values(201,'Lisa','Tom');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|
|
由于其他session已经退出,当前session可以获得锁并成功插入记录:
mysql> insert into actor (actor_id , first_name , last_name) values(201,'Lisa','Tom');
Query OK, 1 row affected (13.35 sec)
|
session_1
|
session_2
|
session_3
|
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.01 sec)
|
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.01 sec)
|
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.01 sec)
|
Session_1获得for update的共享锁:
mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update;
Empty set (0.00 sec)
|
由于记录不存在,session_2也可以获得for update的共享锁:
mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update;
Empty set (0.00 sec)
|
|
Session_1可以成功插入记录:
mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom');
Query OK, 1 row affected (0.00 sec)
|
||
Session_2插入申请等待获得锁:
mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom');
等待
|
||
Session_1成功提交:
mysql> commit;
Query OK, 0 rows affected (0.04 sec)
|
||
Session_2获得锁,发现插入记录主键重,这个时候抛出了异常,但是并没有释放共享锁:
mysql> insert into actor (actor_id,first_name,last_name) values(201,'Lisa','Tom');
ERROR 1062 (23000): Duplicate entry '201' for key 'PRIMARY'
|
||
Session_3申请获得共享锁,因为session_2已经锁定该记录,所以session_3需要等待:
mysql> select actor_id, first_name,last_name from actor where actor_id = 201 for update;
等待
|
||
这个时候,如果session_2直接对记录进行更新操作,则会抛出死锁的异常:
mysql> update actor set last_name='Lan' where actor_id = 201;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|
||
Session_2释放锁后,session_3获得锁:
mysql> select first_name, last_name from actor where actor_id = 201 for update;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| Lisa | Tom |
+------------+-----------+
1 row in set (31.12 sec)
|
转 How to Find Out Who Is Locking a Table in MySQL的更多相关文章
- 查看锁信息(开启InnoDB监控)
当前mysql版本:5.6.21 一.背景 在mysql处理死锁问题时,由于show engine innodb status输出来的死锁日志无任务事务上下文,并不能很好地诊断相关事务所持有的所有锁信 ...
- MySQL 5.6 Reference Manual-14.3 InnoDB Transaction Model and Locking
14.3 InnoDB Transaction Model and Locking 14.3.1 InnoDB Lock Modes 14.3.2 InnoDB Record, Gap, and Ne ...
- SELECT ... FOR UPDATE or SELECT ... FOR SHARE Locking Reads session
小结: 1.注意使用限制 Locking reads are only possible when autocommit is disabled (either by beginning transa ...
- [MySQL Reference Manual]15. 其他存储引擎
15. 其他存储引擎 15. 其他存储引擎 15.1 设置存储引擎 15.2 MyISAM存储引擎 15.2.1 MyISAM启动选项 15.2.2 Key的空间要求 15.2.3 MyISAM表存储 ...
- hbase-site.xml 配置详解
hbase.rootdir 这个目录是region server的共享目录,用来持久化HBase.URL需要是'完全正确'的,还要包含文件系统的scheme.例如,要表示hdfs中的'/hbase'目 ...
- sql是如何执行一个查询的!
引用自:http://rusanu.com/2013/08/01/understanding-how-sql-server-executes-a-query/ Understanding how SQ ...
- MySQL备份学习
备份分类 物理和逻辑备份 物理备份直接拷贝数据库目录和文件,适合数据量大.重要且需要在出现问题时快速恢复的数据库 逻辑备份保存信息包括逻辑数据库结构(数据库表的创建脚本)和内容(插入语句或者分隔符平面 ...
- The Guide To Understanding mysqlreport
The Guide To Understanding mysqlreport This guide to understanding mysqlreport explains everything t ...
- Metadata Lock原理1
https://www.percona.com/blog/2013/02/01/implications-of-metadata-locking-changes-in-mysql-5-5/ impli ...
随机推荐
- oracle 查询月份
①:select substr(to_char(sysdate,'yyyy-mm-dd'),6,2) from dual; ②:select to_char(sysdate,'MM') from du ...
- SSMS开发利器Sql Prompt
一.前言 一个Sql Server 开发智能提示插件,方便查询表结果,避免了开发人员一个个敲查询语句.执行语句等,一起来看看吧. SQL Prompt 9.5 支持SSMS18 下载地址: 链接:ht ...
- kafka的HA机制
1.kafka的HA机制:副本机制+消息同步+leader选举. 每个topic有多个partition,每个partition又有多个副本,这些副本当中也存在不同的角色,leader.followe ...
- django 第三天 视图
今日内容 一.url路由分发之include 项目文件夹下的urls.py文件中的url写法: from django.conf.urls import url,include from django ...
- Linux Vbox 桥接模式上网配置
1.Bridged Adapter模式(桥接模式)特点: 1)如果主机可以上网,虚拟机可以上网 2)虚拟机之间可以ping通 3)虚拟机可以ping通主机 4)主机可以ping通虚拟机以上各点基于一个 ...
- 自用ajax的json请求
function singlePriceSubmit(){ var json={}; json["area"]=areaStr1; json["goodCateIdStr ...
- 9、共享变量(Broadcast Variable和Accumulator)
一.共享变量 1.共享变量工作原理 Spark一个非常重要的特性就是共享变量. 默认情况下,如果在一个算子的函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个task中.此时每个task只能 ...
- jquery.nicescroll.js Unable to preventDefault inside passive event listener due to target being treated as passive.
解决办法就是:https://github.com/bestjhh/Plugin 下载替换. 参考: https://github.com/bestjhh/Plugin https://blog.cs ...
- Java 按字节计算字符串的长度
在<从后向前截取指定长度的字符串>中介绍了如何截取字符串,本文介绍如何统计字符串的字节长度. import org.slf4j.Logger; import org.slf4j.Lo ...
- Chrome远程调试之WebSocket
var ws = new WebSocket('ws://localhost:9222/devtools/page/3c5c05fa-80b7-4cfe-8d1d-ebe79a7a5730');ws. ...