锁的类型:共享锁、排他锁、意向共享锁(表级别)、意向排他锁(表级别)
锁的级别:行锁、间隙锁、next-key lock、表锁
意向锁:当要加一个低级别的锁时,为了防止锁冲突(即其他事务在更高的级别建立了与当前锁不兼容的锁),需要从最高级别一步步向下加意向锁。如把数据库分为数据库、表、页、行的四级结构,则如果要加行所,需要先在对应的数据库、表、页上加意向锁。
innodb的意向锁:innodb只在表上加意向锁,即它只有表和行两级结构。
next-key lock:innodb默认事物隔离级别是“可重复读”,这个级别会有“幻读”的问题,为了解决这个问题innodb使用next-key lock锁定select语句查找的范围,这样在该事物提交前,其他事物就无法获得该范围上的排它锁,而无法插入,也就解决了幻读的问题。如:select * from goods where id>10 lock in share mode; id是主键,这时会在[10, +∞)上加共享锁。
非唯一索引的next-key lock:在唯一索引上使用时如果where从句可以唯一的确定一条记录,则不会加范围锁。但如果是在非唯一索引上,即使指定了明确的行,因为索引非唯一,无法准确的定位一行,所以还是会在非唯一索引上加范围锁,锁定范围为(前一个值, 后一个值)
程序逻辑问题导致丢失更新:在事务中查询某个,并保存为临时变量,之后更新时用该临时变量去修改对应字段。这种情况会覆盖查询到修改期间其它事物的修改。select ... for update 加排他锁可以解决,但并发性会下降。
超卖问题:多个请求同时进入同一事务,都查询了商品的库存,之后修改库存(买商品),这些请求读到的商品库存是相同的,修改时串行的,如果在修改时不对库存再做检查,则会出现超卖的情况。解决办法一,可以先修改,之后再查询一次判断是否超卖,超卖则回滚。解决办法二,update set amount=amount-x where amount>x and id=1; 在修改时检查。
锁超时:由innodb_lock_wait_timeout参数控制,默认是50秒,超时后产生异常(不会回滚)。
死锁问题:innodb在锁等待时检查是否存在等待的回路,如果存在则判断发生死锁,innodb会选择回滚一个相对代价较小的事务,以释放资源。innodb在大多数错误时都不会主动回滚事务,如锁超时,但死锁除外,因为如果不回滚是没有办法让事务释放资源的。