MySQL事务和锁

事务

  • 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
  • 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
  • 事务用来管理 insert,update,delete 语句

一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  • **原子性:**一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • **一致性:**在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • **隔离性:**数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • **持久性:**事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务控制语句:

  • BEGIN 或 START TRANSACTION 显式地开启一个事务;
  • COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
  • ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
  • RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier 把事务回滚到标记点;
  • SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ U NCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

MYSQL 事务处理主要有两种方法:

1、用 BEGIN, ROLLBACK, COMMIT来实现

  • BEGIN 开始一个事务
  • ROLLBACK 事务回滚
  • COMMIT 事务确认

2、直接用 SET 来改变 MySQL 的自动提交模式:

  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交

数据库的锁

乐观锁

​ 通过CAS机制实现(Compare and Swap)

​CAS可以理解为CAS(V,O,N)V当前内存实际存放的值
​ O预期值(旧值)
​ N更新的新值

通过一直去比较如果当前存放的值和预期的值一样,那么进行更新。

但这样存在一个问题,ABA问题

事务1想要将数据A改成B

事务2把数据A改成C

事务3把数据C改为A

此时事务1CAS成功,将数据A改为B

这就是ABA问题,数据被改又改回来可能引发程序错误

那么怎么改进呢,解决办法可以给数据库中增加一个version字段

或者增加一个字段使用时间戳timestamp。

这样在更新的时候检查数据的时间戳和自己之前的对比,如果一致就ODF,否则就是版本冲突。

​ 乐观锁相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现

​ 优点:乐观锁机制避免了长事务中数据库的开销增大,大大提高并发量

悲观锁

意向锁

1在MySQL中有表锁,读锁锁表,会阻塞其他事务修改数据。写锁锁表,会阻塞其他事务读写。

2innerdb引擎支持行锁,行锁分也为共享锁,一个事务对一行的共享只读锁。排它锁,一个事务对一行的排它读写锁。

3这两种类型的锁共享的问题:事务A锁住一行,让一行只读,事务B申请整个表的写锁。如果B申请成功,那么理论上它能修改表中任意一行,这与A持有的行锁是冲突的。数据库避免这个冲突,就是让B申请被阻塞,直到A释放了行锁。

那么B怎么去判断这个冲突呢?

  • step1:判断这张表是否被其他事务用表锁锁住
  • step2:判断表中每一行是否被行锁锁住

step2的效率不高,因为要遍历每一行,于是就有了意向锁,上述判断改为

  • step1:不变
  • step2:发现表上有意向锁,说明表中有些行被共享锁锁住了,因此事务B申请的写锁被阻塞

申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁时候,数据库会自动申请表的意向锁,不需要代码去申请。

行锁

共享锁

​ 加锁与解锁:当一个事务执行select语句时候,数据库会为这个事务分配一把共享锁,来锁定被查的数据。在默认情况下,数据被读取后,数据库会立即解除共享锁。。例如,当一个事务执行查询“SELECT * FROM accounts”语句时,数据库系统首先锁定第一行,读取之后,解除对第一行的锁定,然后锁定第二行。这样,在一个事务读操作过程中,允许其他事务同时更新accounts表中未锁定的行。

​ 兼容性:如果数据资源上放置了共享锁,还可以再放置共享锁或者更新锁。

​ 并发性:具有良好的并发性

排他锁

​ 加锁与解锁:当一个事务执行insert,update,delete语句时候,数据库会自动对SQL语句操作的数据资源使用独占锁。如果该数据资源已经有其他任何锁存在,就无法再放置独占锁了。

​ 兼容性: 独占锁不能和其他锁兼容,如果数据资源上已经加了独占锁,就不能再放置其他的锁了。同样,如果数据资源上已经放置了其他锁,那么也就不能再放置独占锁了。

​ 并发性:最差。只允许一个事务访问锁定的数据,如果其他事务也需要访问该数据,就必须等待。

更新锁

更新锁在初始化阶段用来锁定可能被修改的资源,这可以避免使用共享锁造成死锁的现象。

例如:UPDATE person SET age=18 WHERE id=1;

更新操作需要两步:读取person表中id为1的记录,然后执行更新操作

更新锁以下特点:

​ 1加锁与解锁,当一个事务执行update语句时,数据库会先为事务分配一把更新锁,当数据读取完毕,执行跟新操作时,把更新所升级为独占锁

​ 2兼容性:更新锁与共享锁兼容的,也就是说,一个资源可以同时放置跟新锁和共享锁,但最多只能放置一把更新锁。这样,多个事务跟新相同数据时,只有一个事务可以获取更新锁,然后把更新锁升级为独占锁,其他事务必须等到前一个事务结束后,才能获取更细锁,这就避免了死锁。

数据库的隔离级别

了解了数据库的锁机制,理解数据库的隔离级别也就好多了,每一种隔离级别满足不同的数据需求,使用不同程度的锁。

读未提交:读写均不使用锁,诗句一致性最差,也会出现许多逻辑错误。

读已提交: 使用写锁,但是读会出现不一致,不可重复读。

可重复读:使用读写锁,解决了不可重复读的问题,但会有幻读。

可串行化:使用事务串行化调度,避免出现因为插入数据没法加锁导致的不一致情况

读未提交,造成脏读

一个事务中 的读操作,可能读到聆听事务中未提交修改的数据,如果事务发生回滚可能造成错误。

例子:A打100给B,B看账户,这是两个操作,针对同一个数据库,两个事务,如果B读到了A事务的100块,认为钱打过来了但是A事务回滚了,造成损失。

避免这些事情的发生就需要我们在写操作的时候加锁,使读写分离,保证读数据的时候,数据不被修改,写数据的时候,数据不被读取。从而保证写的同时不能被另个事务写和读。

读提交(Read Committed)

加了写锁,就保证不出现脏读,也就是保证读的都是提交之后的数据,但是会造成不可重读读,读的时候不加锁,一个读的事务过程中,如果读取两次,在两次之间有写事务修改了数据,将导致两次读取结果不一致,从而导致逻辑错误。

可重复读(Repeatable Read)

这就牵涉到事务中是否加读锁,并且读操作加锁后是否在事务commit之前持有锁的问题,如果不加读锁,必然出现不可重复读,如果加锁读完立即释放,不持有,那么就可能在其他事务中被修改,若其他事务已经执行完成,此时该事务中再次读取就会出现不可重复读

所以读锁在事务中持有保证不出现不可重复读,写的时候必须加锁且持有,可重复读是MySQL默认的事务隔离级别,上面的意思是读的时候加锁并且保持

可串行化(Serializable)

解决幻读问题,同一个查询多次返回的结果不一致,事务A增加一条记录,事务B在事务A提交前后各执行了一次查询操作,发现前后多了一条数据,幻读是由于并发事务增加记录导致的,这个不能像可重复读一样加锁解决,因此对于增加的记录无法加锁,只能将事务串行化,才能避免幻读。

这是最高级别的,他通过强制将事务串行化,使其不可能相互冲突,从而解决幻读问题,简言之,就是在每个读的数据上加上共享锁,这个级别,可能导致大量的超时现象和锁竞争

使用实例

MySQL中的隔离级别和悲观锁以及乐观锁实例

MySQL的事务支持

MySQL的事务不是绑定在MySQL服务器本身上,而是与存储引擎相关

1MyISAM:不支持事务,用只于读程序提高性能

2InnoDB:支持ACID事务,行级锁,并发

3Berkeley BD :支持事务

文章参考:http://www.runoob.com/mysql/mysql-transaction.html
https://blog.csdn.net/C_J33/article/details/79487941

你可能感兴趣的:(MySQL)