这个错误的本质发生在同一张innodb表上的两个事务的锁阻塞。并发的两个事务,一个事务持有相同表上相同记录的锁(行锁),或者元数据锁:metadata lock,以及 LOCK INSTANCE FOR BACKUP
语句获取的锁信息。元数据锁指数据库对象的数据字典信息,包括并不限于表结构信息、procedure、function定义信息、视图定义,以及 LOCK TABLES
, FLUSH TABLES WITH READ LOCK
, and HANDLER
等语句获取的锁。同时另外一个事务并发的获取相同的锁,在等待指定的时间后(innodb_lock_wait_timeout、lock_wait_timeout)如果等待锁的事务没有获得锁,就会发生ER_LOCK_WAIT_TIMEOUT,也就是文章标题的“ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction”错误。
其中,
innodb_lock_wait_timeout定义了innodb表行锁的的锁等待时间,单位是秒,默认值是50s。也就是说一个事务如果等待“同一把锁”的时间超过了50s,就会发生锁超时错误。有两点需要特别说明:1,这里“同一把锁”的含义是说,一条sql语句,可能需要获取多个行锁,如果获取第一行记录锁的等待时间是20s,获取第二把行锁的时间是40s,那总的时间是超过了innodb_lock_wait_timeout的,但是并不会抛出错误。2,如果发生了锁超时,mysql只会回滚当前导致锁超时的sql语句,并不会回滚整个事务。这是innodb的默认行为。这个时候就需要依赖应用程序代码来进行回滚事务,以确保事务的原子性。要改变这种默认行为,可以设置innodb_rollback_on_timeout参数,来让innodb在锁超时错误发生时自动rollback整个transaction。
innodb_lock_wait_timeout
Command-Line Format | --innodb-lock-wait-timeout=# |
---|---|
System Variable | innodb_lock_wait_timeout |
Scope | Global, Session |
Dynamic | Yes |
SET_VAR Hint Applies |
No |
Type | Integer |
Default Value | 50 |
Minimum Value | 1 |
Maximum Value | 1073741824 |
The length of time in seconds an InnoDB
transaction waits for a row lock before giving up. The default value is 50 seconds. A transaction that tries to access a row that is locked by another InnoDB
transaction waits at most this many seconds for write access to the row before issuing the f