按粒度大小从大到小分为 全局锁
全局锁是对整个数据库的锁,最常用的全局锁就是读写锁
flush tables with read lock
unlock tables
全局锁的典型应用场景是,进行一些需要确保整个数据库一致性的操作,比如全库的备份和导出等。
表级锁是MySQL中最基本的锁策略,是MySQL最早采用的锁策略。表级锁的特点是开销小,加锁快,不会出现死锁。
锁的力度大,发生锁冲突的概率最高,并发度最低
表锁有两种模式:
在MySQL中,InnoDB引擎在必要情况下会使用表锁,但主要是使用行锁来实现多版本并发控制(mvcc),它能提供更好的并发性能和更少的锁冲突
总的来说,表锁适用于读操作多、写操作少且并发争用不是很激烈的情况。在并发度高,或者写操作较多的情况下,表锁可能会成为瓶颈
但要注意,虽然表级锁的开销较小,但由于其锁定粒度大,会导致并发度下降,特别是在写操作频繁场景下,使用行锁更为合适
行级锁是MySQL中的一种锁定机制,他可以对数据库表中的单独一行进行锁定,相比于表级锁和页锁,行级锁的粒度更小,因此在处理高并发事务时,能够提供更好的并发性能和更少的所冲突。然而,行级锁也需要更多的内存和CPU资源,因为需要对每一行进行管理
在MySQL中,行级锁主要由InnoDB存储引擎提供。InnoDB支持两种类型的行锁:共享锁(S锁)和排他锁(X锁)
在实际使用中,InnoDB还提供了一种名为"间隙锁"(Gap Lock)的特性。间隙锁不仅锁定一个具体的行,还锁定它前后的间隙,即这一行之前的行和之后的行之间的空间。间隙锁可以防止其他事务插入新的行到已锁定行的前后,从而可以解决一些并发问题
值得注意的是,行级锁只在事务中有效,也就是说,只有在一个事务开始(BEGIN)后并在事务提交(COMMIT) 或回滚(ROLLBACK) 之前,才能对数据行进行锁定。如果在非事务环境中执行SQL语句,那么InnoDB会在语句执行结束后立即释放所有的锁
MySQL的行级锁(Row Level Locks)通常在以下几种场景中被使用:
使用行级锁需要注意,由于行级锁的锁定粒度小,它会消耗更多的系统资源,特别是处理大量数据时。此外,使用行级锁也可能导致死锁
尽管行级锁可以提供高并发性并减少锁冲突,但在使用过程中也可能遇到一些风险和问题
乐观锁是一种并发控制机制,它的核心思想是乐观地认为数据不会发生冲突,因此在大部分时间里不对数据进行加锁,而是在更新数据时检查数据是否被其他事务修改过。乐观锁通常适用于并发写入较少的场景,可以提高系统的并发性能。
在实现乐观锁时,一般会引入版本号(Version)或时间戳(Timestamp)等字段来标识数据的版本信息。当一个事务要更新数据时,首先会读取数据的版本信息,然后在写入数据时将版本信息一起提交。在提交时,系统会检查提交的版本信息与当前数据库中的版本信息是否一致,如果一致则表示数据未被其他事务修改,允许更新;如果不一致则表示数据已经被其他事务修改,此时需要根据具体业务场景选择合适的处理方式,比如放弃更新、重新读取数据后再次尝试更新等。
乐观锁的优点是不会引入额外的锁竞争,适用于读多写少的场景,并且可以减少数据库锁的使用,提高系统的并发性能。但是乐观锁也有一些缺点,比如在并发写入较多的场景下可能会导致更新冲突的频率较高,需要进行重试等,这会增加系统的复杂度。
总的来说,乐观锁在合适的场景下可以提供良好的性能和并发控制效果,但在具体应用时需要根据业务场景和性能需求进行综合考量。
悲观锁是一种并发控制机制,它的核心思想是在对数据进行操作之前先获取锁,以确保数据操作的独占性。悲观锁通常适用于并发写入较多的场景,通过加锁来避免数据的并发修改,确保数据的一致性。
在实现悲观锁时,一般会使用数据库提供的锁机制,比如行级锁或表级锁。当一个事务要对数据进行操作时,会先获取相应的锁,其他事务需要等待该锁释放后才能对数据进行操作。悲观锁的典型代表是数据库中的SELECT … FOR UPDATE语句,它可以在读取数据的同时给数据行加上排他锁,阻止其他事务对该数据行进行修改。
悲观锁的优点是能够确保数据的一致性,避免了数据的并发修改,适用于高并发写入的场景。但是悲观锁也有一些缺点,比如可能会引起锁竞争和死锁问题,降低系统的并发性能;另外,长时间持有锁也会影响系统的吞吐量。
总的来说,悲观锁适用于对数据一致性要求较高的场景,但需要注意合理使用锁粒度和锁持有的时间,避免锁竞争和性能问题。在具体应用时,需要根据业务场景和性能需求进行综合考量,选择合适的并发控制策略。
乐观锁适用于以下场景:
MyBatisPlus有乐观锁插件,在表字段中加入Version字段
@Version
@TableField(value = "version")
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
悲观锁适用于以下场景:
总的来说,乐观锁适合于读多写少、数据冲突较少的场景,可以提高系统的并发性能;而悲观锁适合于高并发写入、数据一致性要求高以及长事务场景,可以确保数据的一致性和独占性。在实际应用中,需要根据具体业务场景和性能需求选择合适的并发控制策略。
SELECT ... FOR UPDATE #排它锁 不可读不可更新
SELECT ... LOCK IN SHARE MODE #共享锁 可读不可更新
悲观锁虽然能够确保数据的一致性和避免并发修改的问题,但是它也存在一些缺点,包括:
基于以上缺点,开发者在使用悲观锁时需要谨慎考虑,并且根据具体的业务场景和并发需求选择合适的并发控制策略。在一些情况下,可以考虑使用乐观锁、分布式锁等替代方案来解决并发控制的问题,以减少悲观锁所带来的性能和复杂性开销。
也就是,当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。
而普通的 select 是不会加行级锁的,普通的 select 语句是利用 MVCC 实现一致性读,是无锁的。
不过,select 也是可以对记录加共享锁和独占锁的,具体方式如下:
//先在表上加上意向共享锁,然后对读取的记录加共享锁
select ... lock in share mode;
//先表上加上意向独占锁,然后对读取的记录加独占锁
select ... for update;
意向共享锁和意向独占锁是表级锁,不会和行级的共享锁和独占锁发生冲突,而且意向锁之间也不会发生冲突,只会和共享表锁(*lock tables … read*)和独占表锁(*lock tables … write*)发生冲突。
表锁和行锁是满足读读共享、读写互斥、写写互斥的。
如果没有「意向锁」,那么加「独占表锁」时,就需要遍历表里所有记录,查看是否有记录存在独占锁,这样效率会很慢。
那么有了「意向锁」,由于在对记录加独占锁前,先会加上表级别的意向独占锁,那么在加「独占表锁」时,直接查该表是否有意向独占锁,如果有就意味着表里已经有记录被加了独占锁,这样就不用去遍历表里的记录。
所以,意向锁的目的是为了快速判断表里是否有记录被加锁。
就是,当我对整个表加锁时,lock tables your_table write,我不需要逐行判断是否加了行锁,因为表上又意向排它锁,直接互斥。
但是,共享锁和意向共享锁是不互斥的,都是读,读读不互斥。
Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法。通过临键锁可以解决幻读问题。每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事物持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的是,InnoDB中行级锁时基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列上不存在临键锁
age在(24,32]会锁住,当我们尝试:
就会阻塞,直到事务提交。
在MySQL中,当一个事务对数据行进行修改时,会对这些数据行进行加锁,以确保操作的原子性和一致性。这种锁被称为记录锁(Record Lock)。记录锁是一种悲观锁,用于在事务中对数据行进行读取和修改时保护数据的完整性。
记录锁在MySQL中的工作方式如下:
需要注意的是,MySQL中的记录锁是基于行级别的,因此只会锁定涉及到的具体数据行,而不会锁定整个数据表。这样可以最大程度地减少并发操作的冲突,但也可能会导致大量的锁竞争和死锁问题,因此在使用记录锁时需要谨慎考虑并发访问的情况。
总之,记录锁是MySQL中用于保护数据行完整性的重要机制,但在实际应用中需要注意合理使用,避免造成性能问题和并发冲突。
在 MySQL 中,间隙锁(Gap Lock)是一种特殊的锁类型,用于防止其他事务在一个范围内插入新的记录,从而保证范围查询的一致性。间隙锁通常用于解决幻读(Phantom Read)的问题。
当一个事务执行范围查询(例如使用范围条件的 SELECT 语句)时,MySQL 可能会使用间隙锁来锁定范围内的间隙,而不仅仅是已经存在的行。这样做可以防止其他事务在这个范围内插入新的记录,确保了范围查询的一致性。
举个例子,如果一个事务执行如下的范围查询语句:
sqlCopy CodeSELECT * FROM your_table WHERE id > 100 FOR UPDATE;
那么MySQL可能会使用间隙锁来锁定所有 id 大于 100 的间隙,防止其他事务在这个范围内插入新的记录。这样可以避免在后续操作中出现新的符合范围条件的记录,导致幻读的问题。
需要注意的是,间隙锁可能会导致一些性能上的影响,因为它会在范围查询时锁定额外的间隙。因此,在使用范围查询和间隙锁时需要谨慎考虑,并确保它们符合业务需求。
总之,间隙锁在 MySQL 中是用来保证范围查询一致性的重要机制,但在实际应用中需要注意潜在的性能影响,并结合具体业务场景合理选择使用。
综上所述,间隙锁在 MySQL 中的使用需要谨慎权衡,尤其是在高并发、大数据量的场景下,需要仔细评估其带来的潜在问题,并采取相应的优化措施来减轻其可能的负面影响。