Search code examples
mysqltransactionsinnodblocks

mysql innodb table lock - when update one line block update on another line


When create a deadlock in mysql(innodb) as the the example on Page 10 of "High performance MySQL". But if I update one line of the test in one transaction, update another line will be blocked and finally timeout. It is like innodb use the table level lock rather than row level lock when update with where condition. This situation is not accord with the row level locks of innodb.

The Mysql Version:

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.6.26, for Linux (x86_64) using  EditLine wrapper

Connection id:          2
Current database:       test
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.6.26 MySQL Community Server (GPL)
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    latin1
Db     characterset:    latin1
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 4 hours 52 min 1 sec

Threads: 3  Questions: 107  Slow queries: 0  Opens: 69  Flush tables: 1  Open tables: 62  Queries per second avg: 0.006
--------------

mysql> show variables like '%isolation%';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)

Create test table:

mysql> show create table t\G;
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a1` int(11) DEFAULT NULL,
  `b` varchar(10) DEFAULT NULL,
  `c` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> select * from t;
+------+------+------+
| a1   | b    | c    |
+------+------+------+
|    1 | a    | b    |
|    2 | aa   | bb   |
+------+------+------+
2 rows in set (0.00 sec)

Then open two independent sessions to create two transactions

Session 1

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update t set b='x' where a1=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

In sessions 2, update will be blocked and finally timeout

mysql> start transaction
    -> ;
Query OK, 0 rows affected (0.00 sec)

mysql> update t set c='yy' where a1=1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

During block, use InnoDB plugin to get the locking relationship

mysql> SELECT r.trx_id waiting_trx_id,  r.trx_mysql_thread_id waiting_thread,
    ->       r.trx_query waiting_query,
    ->       b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread,
    ->       b.trx_query blocking_query
    -> FROM       information_schema.innodb_lock_waits w
    -> INNER JOIN information_schema.innodb_trx b  ON  b.trx_id = w.blocking_trx_id
    -> INNER JOIN information_schema.innodb_trx r  ON  r.trx_id = w.requesting_trx_id;
+----------------+----------------+--------------------------------+-----------------+-----------------+----------------+
| waiting_trx_id | waiting_thread | waiting_query                  | blocking_trx_id | blocking_thread | blocking_query |
+----------------+----------------+--------------------------------+-----------------+-----------------+----------------+
| 5933           |              6 | update t set c='yy' where a1=1 | 5932            |               5 | NULL           |
+----------------+----------------+--------------------------------+-----------------+-----------------+----------------+
1 row in set (0.00 sec)

In principle, the row lock in session 1 does not block updates in session 2.

If you have encountered such a problem, will you please help explain why session 2 was blocked.


Solution

  • Do not consider using transactions in InnoDB without a PRIMARY KEY.

    And seriously consider using at least INDEX(a1) when doing UPDATE ... WHERE ai = constant.

    Otherwise, InnoDB gets rather sloppy with "row level locking" -- probably because it is having trouble getting a grip on each "row" without indexing.