Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mysql 事务更新 for update 使用场景 #62

Open
dduo518 opened this issue Jul 28, 2022 · 0 comments
Open

mysql 事务更新 for update 使用场景 #62

dduo518 opened this issue Jul 28, 2022 · 0 comments
Labels

Comments

@dduo518
Copy link
Owner

dduo518 commented Jul 28, 2022

数据库中使用锁的方式

  • 乐观锁
  • 悲观锁

使用场景

乐观锁

乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
乐观锁主要用于不常更新的时候,更新不频繁,使用版本控制进行更新,当前query出来的数据,拿到有个版本version,更新的时候带上版本条件,防止其他事务对其进行更新
当一个高并发更新频繁的事务,使用乐观锁会带来的问题就是一旦遇上高并发的时候,就只有一个事务可以修改成功,那么就会存在大量的失败,高并发是常有的事,总让用户感知到失败显然是不合理的。所以,还是要想办法减少乐观锁的粒度的。有一条比较好的建议,可以减小乐观锁力度,最大程度的提升吞吐率,提高并发能力

悲观锁

悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

悲观锁主要分为共享锁或排他锁
  • 共享锁【Shared lock】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
  • 排他锁【Exclusive lock】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。

主要使用于涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。所以需要for upate 进行数据加锁防止高并发时候数据出错。

行锁与表锁

页级:引擎 BDB。

表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行

行级:引擎 INNODB , 单独的一行记录加锁

表级,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许

行级,仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。

页级,表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁,否则是表级别。

for update的注意点:for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。要测试for update的锁表情况,可以利用MySQL的Command Mode,开启二个视窗来做测试。

  • 1、只根据主键进行查询,并且查询到数据,主键字段产生行锁;没有查询到数据,不产生锁。

  • 2、根据主键、非主键含索引(name)进行查询,并且查询到数据,主键字段产生行锁,name字段产生行锁;没有查询到数据,不产生锁。

  • 3、根据非主键含索引(name)进行查询,并且查询到数据,name字段产生行锁;没有查询到数据,不产生锁。

  • 4、只根据主键进行查询,查询条件为不等于,并且查询到数据,主键字段产生表锁;没有查询到数据,主键字段产生表锁。

  • 5、只根据主键进行查询,查询条件为 like,并且查询到数据,主键字段产生表锁;没有查询到数据,主键字段产生表锁。

  • 6、根据非主键不含索引(stock)进行查询,并且查询到数据,stock字段产生表锁。没有查询到数据,stock字段产生表锁。

总结:
  • 1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。无主键或主键不明确或无索引,表锁;明确指定主键或索引,且有数据,行锁;无数据,则无锁。

  • 2、由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

  • 3、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

  • 4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

  • 5、检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。

选择

在乐观锁与悲观锁的选择上面,主要看下两者的区别以及适用场景就可以了。
乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。
悲观锁依赖数据库锁,效率低。更新失败的概率比较低。

@dduo518 dduo518 added the 存储 label Jul 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant