欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mysql事务和锁机制详解

程序员文章站 2022-06-02 12:15:43
...

一、什么是数据库事务?

事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。

事务具有 4 个特性吗,这 4 个特性通常简称为 ACID。

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

二、原子性

事务是一个完整的操作。事务的各元素是不可分的(原子的)。事务中的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。

以支付宝转账为例,当转账这个事务提交后,两个账号内的数据将会得到更新,如果出于某些原因事务在更新之前终止了,那么不会对两个账号内的数据进行更新。

三、一致性

当事务成功完成时,数据必须再次回到已知的一致状态。通过事务对数据所做的修改不能损坏数据。

比如两个账号内一共200块钱,不管这两个账号之间如何转账,总的资金只能是200块。

四、隔离性

对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。修改数据的事务可以在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。

五、持久性

一个事务成功完成之后,它对数据库所作的改变是永久性的,即使系统出现故障也是如此。也就是说,一旦事务被提交,事务对数据所做的任何变动都会被永久地保留在数据库中。

六、执行事务

  • Mysql中只有InnoDB支持事务,而且事务是自动提交的。也可以通过BEGIN START TRANSACTION 这个语句显式地标记一个事务的起始点。
  • 使用COMMIT将事务中所有对数据库的更新都写到磁盘上的物理数据库中,事务正常结束。
  • 当事务执行过程中遇到错误时,使用 ROLLBACK 语句使事务回滚到起点或指定的保持点处。同时,系统将清除自事务起点或到某个保存点所做的所有的数据修改,并且释放由事务控制的资源。

七、数据库事务的隔离级别

先看一下,因为隔离级别而可能引发的问题:

  1. 脏读:指一个事务读取了另外一个事务未提交的数据。
  2. 不可重复读:指在一个事务内读取表中的某一行数据,多次读取结果不同。
  3. 幻读:指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致
     

数据库标准提出了四类隔离级别:

  • 未提交读 :允许一个事务读取到另一个事务未提交的数据。未提交读的有点在于并发能力高,适合对数据一致性没有要求而追求高并发的场景,但他最大的坏处就是会出现脏读。
  • 读已提交:就是一个事务要等另一个事务提交后才能读取数据。可以解决脏读问题,但是会造成不可重复读。 举个例子,程序员卡里有1w块去买东西,刷卡时读卡器检测出卡中有1w,但是此时他媳妇用卡里的1w块补贴家用了,当读卡器扣款时发现卡里居然没钱了(第二次检测金额当然要等待媳妇转出金额事务提交完)。这里就出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读
  • 可重复读 :就是在开始读取数据(事务开启)时,不再允许修改操作。可重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
  • 串行化:是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

什么时候会出现幻读?

事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

注意:

大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是可重复读

Mysql事务和锁机制详解

八、修改事务的隔离级别

MySQL 提供了 SET TRANSACTION 语句,该语句可以改变单个会话或全局的事务隔离级别。语法格式如下:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

 其中,SESSION 和 GLOBAL 关键字用来指定修改的事务隔离级别的范围:

  • SESSION:表示修改的事务隔离级别将应用于当前 session(当前 cmd 窗口)内的所有事务;
  • GLOBAL:表示修改的事务隔离级别将应用于所有 session(全局)中的所有事务,且当前已经存在的 session 不受影响;
  • 如果省略 SESSION 和 GLOBAL,表示修改的事务隔离级别将应用于当前 session 内的下一个还未开始的事务。

任何用户都能改变会话的事务隔离级别,但是只有拥有 SUPER 权限的用户才能改变全局的事务隔离级别。

九、Mysql锁机制

锁的分类     

①从对数据操作的类型(读\写)分       

  • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。  
  • 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。

②从对数据操作的粒度分

  • 表锁
  • 行锁
  • 页面锁

表级锁定(table-level)  

特点:偏向MyISM存储引擎,开销小,加锁块;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁定(row-level)

特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率最低,并发度最高。 InnoDB与MyISAM最大不不同有两点,一是支持事务,二是采用了行级锁。

页面锁

开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 适用:从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用; 而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
 

9.1InnoDB行锁实现方式

InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁 在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。下面通过一些实际例子来加以说明。

  • 在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。
  • 由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。
  • 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
  • 即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,    比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。