Blog Email GitHub

25 May 2011
mysql row lock and table lock

在《深入浅出mysql》一书中,“20.3.4 InnoDB行锁实现方式”一节中,有这样一句话

InnoDB行锁是通过给索引上的索引项加锁来实现的,这个特点意味着: 只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

尽信书不如无书,这句话我一直深信不疑,除了这句话,下面还有例子为证。可是今天 的一个bug引起的研究,我才发现这句话是错误的。

先从今天的bug说起的,mysqldb有一个bug,就是当mysql抛出error

Lock wait timeout exceeded; try restarting transaction.

mysqldb并不抛出异常,而是返回空的结果集。先放开mysqldb的bug,我想知道在我的代码中, 什么情况下会lock timeout。看了代码,我第一个猜测就是:是不是有些表项没有加索引, 导致使用了表锁,从而出项了lock timeout。接着查询资料:

  • vairable: innodb_lock_wait_timeout。When a lock wait timeout occurs, the current statement is not executed. The current transaction is not rolled back. (Until MySQL 5.0.13 InnoDB rolled back the entire transaction if a lock wait timeout happened. ) ( from InnoDB Startup Options and System Variables )
  • variable: table_lock_wait_timeout. There is no lock wait timeout in MySQL's table locks. (from Server System Variables and bug)

从以上资料,可以看出,系统变量innodb_lock_wait_timeout就等待行锁释放的过期时间, 但是table_lock_wait_timeout早已经弃用。因此可以确定:就算没有使用索引,加了表锁, 也不会引起lock timeout。因为当加了表锁后,根本就会一直等待下去, 没有过期时间。接着就开始怀疑《mysql深入浅出》中这句话的正确性了。接着再看这些资料:

  • Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking. (from InnoDB Record, Gap, and Next-Key Locks)

如果你想知道,innodb是如何确定和定义clustered index,请点击这里

现在基本明朗了:如果查询语句加锁,但是查询条件中没有索引, mysql innodb就会自动使用“隐藏的”聚集索引,当存在主键时, 这个主键就是这个表的聚集索引;如果存在NOT NULL的唯一键, 也可以作为聚集索引;但是以上情况都不存在,innodb就会组合各表项、 递增的row ID来定义一个主键。根本就不会加所谓的表锁。