lock字段mysql_MySQL中的锁
MySQL常⽤存储引擎的锁机制
MyISAM和MEMORY采⽤表级锁(table-level locking)
BDB采⽤页⾯锁(page-level locking)或表级锁,默认为页⾯锁
InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁
InnoDB和MyISAM的最⼤不同点有两个
InnoDB⽀持事务(transaction);
默认采⽤⾏级锁。加锁可以保证事务的⼀致性,有锁的地⽅,就有事务;
锁的划分
按锁的粒度划分,可分为表级锁、⾏级锁、页级锁(mysql)
按锁级别划分,可分为共享锁、排他锁
按加锁⽅式划分,可分为⾃动锁、显⽰锁
按使⽤⽅式划分,可分为乐观锁、悲观锁
按锁的粒度划分
⾏级锁
⾏级锁是MySQL中粒度最⼩的⼀种锁,只对当前操作的⾏进⾏加锁。
特点:粒度最⼩,⾏级锁分为 共享锁和排它锁(下⾯会分析这两种锁)
优点:数据库锁冲突最⼩
缺点:因为粒度最⼩所以开销最⼤,加锁速度最慢。
加锁的⽅式:⾃动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会⾃动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁;当然我们也可以显⽰的加锁:
共享锁:select * from tableName where ... + lock in share more
排他锁:select * from tableName where ... + for update
⾏锁优化
尽可能让所有数据检索都通过索引来完成,避免⽆索引⾏或索引失效导致⾏锁升级为表锁。
尽可能避免间隙锁带来的性能下降,减少或使⽤合理的检索范围。
尽可能减少事务的粒度,⽐如控制事务⼤⼩,⽽从减少锁定资源量和时间长度,从⽽减少锁的竞争等,提供性能。
尽可能低级别事务隔离,隔离级别越⾼,并发的处理能⼒越低。
注意:InnoDB的⾏锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从⾏锁升级为表锁。
表级锁
表级锁是MySQL中粒度最⼤的⼀种锁,对当前操作的整张表加锁。
特点:粒度最⼤,表级锁分为 共享锁和排它锁(下⾯会分析这两种锁)
优点:因为粒度最⼤所以开销最⼩,加锁速度最快。
缺点:发⽣锁冲突的概率最⾼,并法度最低。
加锁的⽅式:⾃动加锁。查询操作(SELECT),会⾃动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会⾃动给涉及的表加写锁。也可以显⽰加锁:
共享读锁:lock table tableName read;
独占写锁:lock table tableName write;
批量解锁:unlock tables;
什么场景下⽤表锁
InnoDB默认采⽤⾏锁,在未使⽤索引字段查询时升级为表锁。MySQL这样设计并不是给你挖坑。它有⾃⼰的设计⽬的。
即便你在条件中使⽤了索引字段,MySQL会根据⾃⾝的执⾏计划,考虑是否使⽤索引(所以explain命令中会有possible_key 和 key)。如果MySQL认为全表扫描效率更⾼,它就不会使⽤索引,这种情况下InnoDB将使⽤表锁,⽽不是⾏锁。因此,在分析锁冲突时,别忘了检查SQL的执⾏计划,以确认是否真正使⽤了索引。
第⼀种情况:全表更新。事务需要更新⼤部分或全部数据,且表⼜⽐较⼤。若使⽤⾏锁,会导致事务执⾏效率低,从⽽可能造成其他事务长时间锁等待和更多的锁冲突。
第⼆种情况:多表查询。事务涉及多个表,⽐较复杂的关联查询,很可能引起死锁,造成⼤量事务回滚。这种情况若能⼀次性锁定事务涉及的表,从⽽可以避免死锁、减少数据库因事务回滚带来的开销。
页级锁
页级锁是MySQL中锁粒度结余⾏级锁和表级锁之间的⼀种锁。
特点:以上两种的折衷。
总结
锁的粒度越⼤,加锁速度越快,越不容易出现死锁,但锁冲突的概率会上升,并发会下降。
InnoDB ⽀持表锁和⾏锁,使⽤索引作为检索条件修改数据时采⽤⾏锁,否则采⽤表锁。
按锁的级别划分
共享锁(Share Lock)
共享锁⼜称读锁,是读取操作创建的锁。其他⽤户可以并发读取数据,但任何事务都不能对数据进⾏修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
⽤法
SELECT ... LOCK IN SHARE MODE;
在查询语句后⾯增加LOCK IN SHARE MODE,Mysql会对查询结果中的每⾏都加共享锁,当没有其他线程对查询结果集中的任何⼀⾏使⽤排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使⽤了共享锁的表,⽽且这些线程读取的是同⼀个版本的数据。
排他锁(eXclusive Lock)
排他锁⼜称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,⼜能修改数据。
⽤法
SELECT ... FOR UPDATE;
在查询语句后⾯增加FOR UPDATE,Mysql会对查询结果中的每⾏都加排他锁,当没有其他线程对查询结果集中的任何⼀⾏使⽤排他锁时,可以成功申请排他锁,否则会被阻塞。
使⽤⽅法总结:
共享锁:
SELECT ... LOCK IN SHARE MODE;
排他锁:
SELECT ... FOR UPDATE;
使⽤⽅式划分
乐观锁(Optimistic Lock)
乐观锁,也叫乐观并发控制,它假设多⽤户并发的事务在处理时不会彼此互相影响,各事务能够在不产⽣锁的情况下处理各⾃影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务⼜修改了该数据。如果其他事务有更新的话,那么当前正在提交的事务会进⾏回滚。
乐观锁的特点先进⾏业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进⾏完业务操作需要实际更新数据的最后⼀步再去拿⼀下锁就好。
乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的⽀持。⼀般的做法是在需要锁的数据上增加⼀个版本号,或者时间戳,然后按照如下⽅式实现:
乐观锁(给表加⼀个版本号字段)这个并不是乐观锁的定义,给表加版本号,是数据库实现乐观锁的⼀种⽅式。
1. SELECT data AS old_data, version ASold_version FROM …;
2. 根据获取的数据进⾏业务操作,得到new_data和new_version
3. UPDATE SET data = new_data, version =new_version WHERE version = old_version
if (updated row > 0) {
// 乐观锁获取成功,操作完成
} else {
// 乐观锁获取失败,回滚并重试
}
乐观锁在不发⽣取锁失败的情况下开销⽐悲观锁⼩,但是⼀旦发⽣失败回销则⽐较⼤,因此适合⽤在取锁失败概率⽐较⼩的场景,可以提升系统并发性能
乐观锁还适⽤于⼀些⽐较特殊的场景,例如在业务操作过程中⽆法和数据库保持连接等悲观锁⽆法适⽤的地⽅。
悲观锁(Pessimistic Lock)
mysql下载失败怎么办悲观锁的特点是先获取锁,再进⾏业务操作,即“悲观”的认为获取锁是⾮常有可能失败的,因此要先确保获取锁成功再进⾏业务操作。通常所说的“⼀锁⼆查三更新”即指的是使⽤悲观锁。通常来讲在数据库上的悲观锁需要数据库本⾝提供⽀持,即通过常⽤的select … for update操作来实现悲观锁。当数据库执⾏select forupdate时会获取被select中的数据⾏的⾏锁,因此其他并发执⾏的select for update 如果
试图选中同⼀⾏则会发⽣排斥(需要等待⾏锁被释放),因此达到锁的效果。select for update获取的⾏锁会在当前事务结束时⾃动释放,因此必须在事务中使⽤。
这⾥需要注意的⼀点是不同的数据库对select for update的实现和⽀持都是有所区别的,例如oracle⽀持select for update no wait,表⽰如果拿不到锁⽴刻报错,⽽不是等待,MySQL就没有no wait这个选项。另外MySQL还有个问题是selectfor update语句执⾏中所有扫描过的⾏都会被锁上,这⼀点很容易造成问题。因此如果在MySQL中⽤悲观锁务必要确定⾛了索引,⽽不是全表扫描。
总结
悲观锁和乐观锁是数据库⽤来保证数据并发安全防⽌更新丢失的两种⽅法,例⼦在 for update前加个事务就可以防⽌更新丢失。
乐观锁、悲观锁使⽤场景
响应速度:如果需要⾮常⾼的响应速度,建议采⽤乐观锁⽅案,成功就执⾏,不成功就失败,不需要等待其他并发去释放锁。
冲突频率:如果冲突频率⾮常⾼,建议采⽤悲观锁,保证成功率,如果冲突频率⼤,乐观锁会需要多次重试才能成功,代价⽐较⼤。
重试代价:如果重试代价⼤,建议采⽤悲观锁。
死锁
MyISAM中是不会产⽣死锁的,因为MyISAM总是⼀次性获得所需的全部锁,要么全部满⾜,要么全部等待。⽽在InnoDB中,锁是逐步获得的,就造成了死锁的可能。
有多种⽅法可以避免死锁,这⾥只介绍常见的三种
1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以⼤⼤降低死锁机会。
2、在同⼀个事务中,尽可能做到⼀次锁定所需要的所有资源,减少死锁产⽣概率;
3、对于⾮常容易产⽣死锁的业务部分,可以尝试使⽤升级锁定颗粒度,通过表级锁定来减少死锁产⽣的概率;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论