mysql锁

面试练习

表锁底层原理,
行锁底层原理:RC/RR级别用id主键列单列与范围操作?RC/RR级别用二级唯一索引列单列/范围操作?RC和RR用二级非唯一索引单列/范围操作?RC和RR用非索引单列/范围操作

锁的分类?

数据操作类型:读锁&写锁
按细粒度划分

  • 表锁:表锁又细分为元数据锁,意向锁,自增锁,表级读写锁
  • 行锁:只有innodb引擎才有行锁,又细分为记录锁,间隙锁,临间锁,插入意向锁
  • 页锁
    对待锁的态度上:悲观锁&乐观锁

按细粒度划分,表锁,元数据锁,行锁

  • 行锁按细粒度划分记录锁,间隙锁、临间隔
  • 表锁又分为自增锁、

按是否共享,行锁又细分为共享锁与排他锁,读锁,写锁

mysql事务各个隔离级别的底层实现原理

锁定读与非锁定读

在MySQL中,读操作可以分为非锁定读和锁定读两种:

非锁定读(Non-locking Read):非锁定读又称为一致性读(Consistent Read),它不会设置任何锁。非锁定读操作允许其他会话继续访问读取的行,即使当前会话正在读取这些行。在非锁定读操作下,读取的是记录的快照,而不是最新版本的记录。这种方式可以避免读操作被写操作阻塞,提高了数据库的并发处理能力。在MySQL中,非锁定读是默认的读操作。

锁定读(Locking Read):锁定读会对读取的数据加锁,直到事务结束时才释放。这可以防止其他事务在当前事务完成之前修改或删除读取的数据。在MySQL中,使用SELECT … FOR UPDATE或者SELECT … LOCK IN SHARE MODE语句可以进行锁定读操作。这时,其他事务如果要对这些被锁定的行进行修改(对于FOR UPDATE)或者修改和删除(对于LOCK IN SHARE MODE)操作,就需要等待锁定读的事务结束,锁被释放。

四种隔离级别的实现原理

读未提交,相当于没有做任何隔离
串行化,即无论读写全部加排他锁,彻底解决了并发性问题,但并发度也随之为0,性能很差

以上两种极端情况在数据库实际应用中都是不会考虑的

mysql默认隔离级别是Repeatable Read(RR),该隔离级别下,默认情况下,MySQL会在进行写操作(如UPDATE、DELETE)时使用行锁(Row Lock)来保证数据的一致性。这样可以防止在当前事务修改数据的同时,其他事务也修改同一条数据,造成数据不一致的问题。

对于读操作,MySQL默认使用的是非锁定读,也就是说,它不会对读取的数据加锁。但是,如果你希望在读取数据时也加锁,可以使用SELECT … FOR UPDATE或者SELECT … LOCK IN SHARE MODE语句,这样MySQL就会对读取的数据加锁,直到事务结束时才释放。

此外,为了防止幻读问题,MySQL在RR隔离级别下还会使用Next-Key Locks。Next-Key Locks是一种组合锁,它包括了记录锁(锁定索引记录)和间隙锁(锁定索引记录之间的间隙)。这样,即使在当前事务读取数据的同时,其他事务也不能在这些数据的前后插入新的数据,从而避免了幻读问题。

从mysql数据不一致角度看锁的隔离级别对锁的影响

相比于更新缓存,我们更倾向于直接删除缓存,但如果是先删除缓存,再更新数据,就会存在一个严重的问题,假如这个更新操作由A线程执行耗时比较长,那么再次期间,其它线程来读取到这行数据,cache miss,然后去查库,查出来修改前的旧数据,然后存入缓存,那么当A线程更新完毕后,缓存中的数据依然还是旧的数据

mysql默认可重复读的隔离级别下,对于被行锁保护的数据行,其他事务的读操作是可以进行的,这种行为被称为“非锁定读(Non-Locking Read)。这意味着,即使一行数据正在被一个事务修改,其他事务仍然可以读取这行数据的原始值。在“可串行化(Serializable)”隔离级别下,所有的读操作都会被转换为锁定读,也就是说,一旦一行数据被锁定,其他事务就无法读取这行数据,直到该行锁被释放。

mysql默认可重复读的隔离级别下,非锁定读的实现是依赖于MVCC技术的,它是InnoDB存储引擎实现的一种多版本并发控制技术。在MVCC中,对于每一行数据,InnoDB都会保存两个隐藏的系统列,一个记录该行的创建时间,一个记录该行的过期时间(或者说删除时间)。这里的“时间”其实是系统的逻辑时间,即事务的版本号。当一个事务要读取一行数据时,InnoDB会检查这行数据的创建时间和过期时间,来决定是否向该事务返回该行。因此,即使一行数据正在被其他事务修改,当前事务依然可以读取到这行数据之前的版本,从而实现非锁定读,MVCC允许多个读取事务并发执行,同时还可以和其他写事务并发执行,提高了数据库的并发性能

在这个隔离级别下,可能出现一个问题,即使一行数据正在被一个事务修改,其他事务仍然可以读取这行数据的原始值,缓存双删策略适用于对实时性要求比较高的缓存数据,先删除缓存,然后修改数据,假如修改数据过程耗时比较久,这时候有一个查询请求,缓存中没有这条数据,所以要去mysql中取数据,但此时这条数据正在修改中,在mysql默认隔离级别下,读出来的依然是修改前的数据,所以这种情况下

  • 使用分布式锁,这个锁可以基于你正在更新的数据条目的主键或者其他唯一标识。执行更新操作的线程首先获取锁,而其它做读请求的线程只能等待更新操作完毕释放了锁才能去做读操作

mysql锁的意义

增加事务隔离级别会降低并发性能,所以我们不可能为了解决某个sql场景的数据一致性问题或者并发安全问题而去强行提升数据库的事务隔离级别,在当前事务隔离级别下出现的数据一致性问题可以使用mysql锁去解决,当然,使用分布式锁也可以达成同样的目的

如果是先删缓存再操作mysql可能会出现问题,是因为在一个写线程更新mysql数据的,我们首先删掉旧缓存,防止别的读线程再读出旧缓存数据,但如果先删掉了缓存,写线程去读数据可能写线程还没有修改完数据,然后读线程从mysql中读出了旧数据,然后写入了缓存,后面的读线程读到都是旧数据,导致脏读问题,所以通常我们使用缓存延时双删来解决这个问题,但是,如果不使用缓存双删的话,我通过给读线程的查询sql加锁的方式来做当前读也是可以达到缓存双删的效果

也就是说加锁的主要目的是确保读取数据的实时性,防止读到正在更新的数据,这是由MVCC的特征决定的

为何只有 InnoDB 引擎才能支持行锁,而myIsam不可以呢?是因为他们底层数据结构不同吗?

InnoDB和MyISAM虽然都使用B+树作为索引结构,但是它们的数据存储方式有所不同。

在InnoDB中,主键索引的叶子节点直接包含了行数据,这就是所谓的聚集索引。由于数据存放在叶子节点上,一次树的搜索就能够找到需要的数据,效率较高。

而在MyISAM中,无论是主键索引还是辅助索引,叶子节点上存储的都是数据记录的地址,即数据文件的偏移量。也就是说,即使你已经通过索引找到了叶子节点,还需要通过叶子节点中的地址去数据文件中读取数据,这就需要额外的磁盘I/O操作,效率相对较低。

这就是为什么InnoDB的查询效率通常比MyISAM要高,而且InnoDB还支持事务处理和行级锁定等高级功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值