IT修炼手册之SQL事务

事务
基本概念: MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在信息管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,一系列的数据库的操作就构成了事务!
(1)在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
(2)事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
(3) 事务用来管理 insert,update,delete 语句
(4)默认事务是自动提交的,改为手动提交的方式是:
start transaction;
update t_user set psw=11 where id =1; Commit;

事务的 4 个特性:ACID
原子性(Atomicity)、一致性(Consistency)、隔离 性(Isolation)、持久性(Durability)以银行汇款为例,张三给李四转款 300 元.
(1)、原子性: 是指某几句 sql 的影响,要么都发生,要么都不发生.
即:张三减 300, 李四+300 , insert 银行流水, 这 3 个操作,必须都完成,或都不产 生效果.
(2)、 一致性: 事务前后的数据,保持业务上的合理一致.
(汇款前)张三的余额+李四的余额 ====== (汇款后) 张三的余额+李四余额
比如: 张三只有 280 元, 280-300=-20,储蓄卡不是信用卡,不能为负,因此张三余 0 元. 将导致, 汇款后,2 者余额,汇款前,差了 20 元.
(3)、隔离性: 在事务进行过程中, 其他事务,看不到此事务的任何效果.

(4)、持久性: 事务一旦发生,不能取消. 只能通过补偿性事务,来抵消效果. 事务与引擎:

注意点

(1)myisam 引擎不支持事务, innodb 和 BDB 引擎支持. 因此我们的实验用 innodb 表来做;
(2)开启事务 start transaction;
(3)执行查询 xxxx
(4)提交事务/回滚事务. commit / rollback

并发事务处理带来的问题
(1)更新丢失:更新的数据被别的事务所覆盖
(2)脏读:一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的 数据处于有不一致的状态;这时另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了 这些‘脏’数据,并据此做进一步的处理就会产生未提交的数据依赖关系。称之为脏读。
事务 A 读取到了事务 B 已修改但是未提交的数据,并在这个数据基础上做了操作。如果 B 回滚,A 读取的数据无效,不符合一致性要求。
(3)不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现独处的数据已经发生了改变,或者某些记录以及被删除了这种现象称之为 ‘不可重复读’
事务 A 读取到了事务 B 已经提交的修改数据,不符合隔离型。
(4)幻读:一个事务按照相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足查询条件的新数据,这种现象称之为‘幻读’;
事务 A 读取到了事务 B 提交的新数据,不符合隔离性。幻读与脏读比较类似: 脏读是事务 B 里面修改了数据
幻读是事务 B 里面新增了数据。

查看数据库隔离级别
show variables like ‘%iso%’;

设置隔离级别
set tx_isolation=’READ-UNCOMMITTED’

隔离级别
read uncommitted:读未提交的事务内容,显然不符原子性,称为”脏读”. 在业务中,没人这么用.
read commited: 在一个事务进行过程中, 读不到另一个进行事务的操作,但是,可以读到另一个结束事务的操作影响。即当第一个事务结束时候,第二个事务仍然在进行中的时候可以读到第一个结束事务产生的数据。故当一个没结束的事务操作不能够外界所看到,并且一个没结束的事务也不能看到外界的变化。
repeatable read: 可重复读,即在一个事务过程中,所有信息都来自事务开始那一瞬间的信息, 不受其他已提交事务的影响. (大多数的系统,用此隔离级别)。
事务锁级别扩展:
1、如两个事务 A 和 B;当 A 事务对某一行数据做写操作时,B 数据先与 A 也在对同一行数据做写操作,那么 A 事务将被挂起,等待 B 事务 commit 之后执行。这是针对 innodb 引擎来讲的,如果是 myisam 的话如果 A 对某个表的某个记录做写操作,那么 B 对该表的任何行做操作都会被挂起,因为 myisam 锁的是整个表。
2、如果 AB 两个事务同时对同一行数据做操作,A 事务对数据做写操作并且提交,等 A 事务提交完之后 B 事务在对改行做写操作会依赖与 A 事务的结果,但是 B 事务做读操作不依赖于 A 的结果。
(4)serializeable 串行化, 所有的事务,必须编号,按顺序一个一个来执行,也就取消了冲突的可能. 这样隔离级别最高,但事务相互等待的等待长. 在实用,也不是很多。
这里写图片描述
默认是可重复读的级别


锁的划分
锁分为:行锁、表锁和页锁

按照数据的操作来分:lock tables table_name read(write) unlock tables
读锁:针对同一份数据,多个读操作可以同时进行而不会相互形象,也叫共享锁
写锁:当前写操作没有完成之前,他会阻断其他写锁和读锁。也叫排他锁
按照操作粒度来分:行锁、表锁

表锁
对于一些非事务性行的表(如:myisam )操作可以采用锁的方式来处理,常用的两类,读锁定和写锁定;读锁定是指所有的回话只能读取数据不能写数据,写锁定是当前加锁的回话可 以读写,但是其他的回话不能读也不能写。
这里写图片描述
在sesion1 中为表加写锁
Sesion1 为表加写锁 Sesion2
当前sesion 对表增删查都可执行 其他sesion 对锁表查询被阻塞,需要等待释放锁
释放锁 session2 获得锁完成查询/插入/更新操作
这里写图片描述
简而言之,就是读锁会阻塞写、但是不会阻塞读。而写锁会把读和写都阻塞。

行锁
这里写图片描述
使用 innodb,提交模式改为手动提交
Sesion1 Sesion2

更新id 为 4 的记录,没有手写commit 更新id 为 4 的记录,阻塞只能等待(id 为非 4 不
影响)
更新提交 接触阻塞,正常更新

更新id 为 4 的记录,没有手写commit select id 为 4 的记录,查询不到 session 的更新值
如果想锁定一行的话:
begin
Select * from test where id =4 for update
//在没有 commit 之前如果别的 session 更新这行记录会被阻塞。

行锁升级为表锁
如果在 update 的时候 where 后面的字段如果没加索引或者索引失效的话,行锁会升级成表锁,大幅度降低系统性能。

间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于建值在条件范围内但并不存在的记录叫做间隙
(GAP),INNODB 也会对这个间隙加锁,这种所机制就是间隙锁。

因为 Query 在执行过程中通过范围查找的话,他会锁定整个范围内所有索引建值,即使这个建值并不存在。间隙锁有一个比较致命的弱点,就是当锁定一个范围建值之后,即使 某些不存在的建值也会被无辜的锁定,而造成锁定的时候无法插入锁定建值范围内的任何数 据。在某些场景下可能会对性能造成很大的危害。

悲观锁
悲观锁体现的场景是当我们开启一个事务或者是让提交模式编程手动提交的时候才会体现出来,如果是自动提交的话表示这每一个 sql 都会是一个事务,当 sql 执行完也就意味着锁释放,感觉不出锁的存在。对于悲观锁主要应用的是数据库的锁机制即为排他锁(写锁),当加上该所之后根据不同的数据库引擎表现出来的不一样,如果是 innodb 的话体现出来的是锁中该行,也就是说在当前事务没有 commit 之前,其他的事务读写都要等待。如果是
mysiam 的话其他的事务如果对该表进行操作的时候要等待,范围比 innodb 要大。

悲观锁的使用释放是在 select 的时候在后面加上 for update 表示把这一行给锁住了,可以保证外界对该行的数据毫无干扰,保证了数据的完整性。当该线程对这行数据写操作完之后,提交事务。其他因为也要去锁改行而被挂起的事务在进行 select 的时候会获取最新的数据。
乐观锁
乐观锁是由应用程序提供的一种机制, 通过版本控制和时间戳的方式来实现。比如往数据库中的某个表更新一条记录时,首先 SELECT 出来一条记录(v0.1),这是开启两个事务 A 和 B 对其进行更新操作,假设 A 事务对其进行了更新并提交了事务,那么数据库中的数据发生了变化,于此同时版本也变成 v0.2;此时B 事务跟新提交时,如果叫上版本校验的话会出现错误,因为数据库中的版本比B 事务要新,所以要先SELECT 一次是版本变成和数据库同步,在进行更新操作。
对于实际应用中实现版本控制,可以在数据库中加一个普通的字段为 version.每次更新或者其他操作时检查 version 字段的内容和本次查找出来的内容是否一样,一样执行不一样再次查找后执行。
Eg:Mybatis 对乐观所得应用(在原由的实体类中加入 version 字段属性值,并在数据库中加入version 这个clumn):
这里写图片描述
UPDATE T_USER u SET u.address = #address#, u.version = u.version + 1
WHERE u.username = #username# AND u.version = #version#
UPDATE 会返回一个更新结果的行数,如果更新执行返回的数量是 0 表示产生并发修改了, 需要重新获得最新的数据后再进行更新操作。
本节完后续更新

猜你喜欢

转载自blog.csdn.net/qq_37779333/article/details/81489186