InnoDB存储引擎实现了自己的行锁,通过next-key锁(记录锁和间隙锁的组合)来锁住记录本身和记录之间的间隙,防止其他事务在这个记录之间插入新的记录,从而避免了幻读的现象。

当我们执行update语句的时候,实际上会对记录加独占锁(X锁)。另外其他事务对持有独占锁的记录进行修改的时候会被阻塞。这个锁并不是执行完update语句才会释放,而是会等事务结束时才会释放。

InnoDB事务中,对记录加锁的基本单位是next-key锁。但是会因为一些条件会降级成间隙锁,或者记录锁。加锁的位置准确的说是加在索引上,而不是行上。

在 update 语句的 where 条件使用了唯一索引,那么 next-key 锁会降级成记录锁,也就是只会给一行记录加锁。

注意1:InnoDB中,在读已提交隔离级别下,行级锁的种类只有记录锁(也叫独占锁,x锁,分类方式不一样而已),也就是仅仅把一条记录锁上。

注意2:在 MySQL 中,默认情况下会使用行级锁(记录锁),而不是间隙锁。因此,如果要使用间隙锁,必须在事务中显式地设置锁级别。

update 语句的 where 条件没有使用索引,就会全表扫描,于是就会对所有记录加上 next-key 锁(记录锁 + 间隙锁),相当于把整个表锁住了。如果存在索引的话,则对索引进行加锁。

因此,当在数据量非常大的数据库表执行 update 语句时,如果没有使用索引,就会给全表的加上 next-key 锁, 那么锁就会持续很长一段时间,直到事务结束。而这期间除了 select ... from 语句,其他语句都会被锁住不能执行。

**那 update 语句的 where 带上索引就能避免全表记录加锁了吗?并不是。 ** 关键还得看这条语句在执行过程中,优化器最终选择的是索引扫描,还是全表扫描,如果走了全表扫描,就会对全表的记录加锁了。我们可以将 MySQL 里的 sql_safe_updates 参数设置为 1,开启安全更新模式。

大致的意思是,当 sql_safe_updates 设置为 1 时,update 语句必须满足如下条件之一才能执行成功:

使用 where,并且 where 条件中必须有索引列。 使用 limit。 同时使用 where 和 limit,此时 where 条件中可以没有索引列。 delete 语句必须满足如下条件之一才能执行成功:

使用 where,并且 where 条件中必须有索引列。 同时使用 where 和 limit,此时 where 条件中可以没有索引列。 如果 where 条件带上了索引列,但是优化器最终扫描选择的是全表,而不是索引的话,我们可以使用 force index([index_name]) 可以告诉优化器使用哪个索引,以此避免有几率锁全表带来的隐患。

总结:当我们要执行 update 语句的时候,确保 where 条件中带上了索引列,并且在测试机确认该语句是否走的是索引扫描,防止因为扫描全表,而对表中的所有记录加上锁。我们可以打开 MySQL 里的 sql_safe_updates 参数,这样可以预防 update 操作时 where 条件没有带上索引列。如果发现即使在 where 条件中带上了列索引列,优化器走的还是全标扫描,这时我们就要使用 force index([index_name]) 可以告诉优化器使用哪个索引。

1.mysql存储引擎:

Innodb: 支持事务,更新时采用行级锁,并发性高 MyISAM: 不支持事务,更新时表锁,并发性差 因此使用Innodb才会发生死锁,从mysql5.6开始默认引擎Innodb

2.update更新过程

行级锁并不是直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,mysql会锁住主键索引;如果一条语句操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引。反之, 如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁。 如果没有用到索引,则进行全表扫描,锁表。 当where条件为非主键索引,执行update时,会经过一下步骤: 1)先获取非主键索引的行级锁; 2)由数据库基本原理可知,where条件为非主键索引时,会发生回表查询,进而再获得主键索引的行级锁; 3)更新完毕,进行事务提交。

mysql中锁和加锁机制