一、数据库事务是什么?
我们先不说数据库中的事务,我们看下百度对事务这个词汇的解释:
事务:
也就是说,事务就是,要做或者所做的事情
好的我们再联系一下生活中平常做的一些事情
例如 小 x 去 爱存不存的银行ATM 存 软妹币,拿了 200 元现金准备去存,那么存钱这个事情就可以叫做事务,再具体一点可以说 向 银行卡号为 xxxxxxxx 的银行卡 存 200 元,这个便称为事务,如果和数据库联系起来,那么就是将 小 x 的银行卡余额 + 200,假设原来有 800 ,那么存入成功后的银行卡余额就是 1000
类似于:
UPDATE account //更新银行卡表
SET balance = 1000 //更新银行卡余额
WHERE account_number = 123456; //银行卡号
这里我们思考一个问题,假如说 ATM 把 x 的钱吞进去了,但是银行卡余额并没有 + 200 ,那么这样肯定是需要找银行人员帮忙协调处理,要么重新去存,要么就是把钱退给你,等这台ATM修好再来存,这时候其实就已经体现出事务的一个特性了,要么钱存成功、要么就把钱退给你,叫做原子性
二、事务的四大特性
2.1 原子性
我们先来看下原子是什么意思
那么原子性就是代表事务 的操作要么全部完成要么就全部撤回,不允许出现某个事务、事情只做了一半,就好比上面讲到的,(去ATM存纸币,银行卡余额更新) 这是一个完整的事务,存钱和余额更新这两个步骤必须都成功的完成、要么就纸币退回,银行卡余额回到存钱之前。保证该操作不能被分割,也就是所说的原子性。
2.2一致性
一致性是指在事务开始前和结束后,数据库的完整性约束不能被破坏,即数据库从一个一致性状态转移到另一个一致性状态。在这个转账操作中,如果A账户减少500元后,B账户增加500元之前,数据库发生了宕机或其他错误,导致操作没有完成,那么数据库必须回滚到操作前的一致性状态,保证操作的一致性。
2.3 隔离性
隔离性是指每个事务的操作被隔离开来,不会相互干扰,即每个事务都像运行在独立的系统中,不会看到其他事务的中间状态。在这个转账操作中,如果同时有另一个事务也在对A账户或B账户进行操作,那么这个操作必须要保证隔离性,即两个事务的操作不能相互干扰。例如,如果另一个事务在这个转账操作中向A账户中增加100元,那么这个转账操作必须在另一个事务完成后才能开始,否则可能会导致数据的不一致性和错误的结果。
2.4 永久性
持久性是指一旦一个事务结束,其结果就应该被永久保存在数据库中,即使出现系统故障或宕机也不会丢失。在这个转账操作中,如果这个操作提交成功,那么数据库必须保证对A账户和B账户的余额修改已经被永久保存,即使出现系统故障或宕机,也可以从数据库中恢复这个操作的结果。
三、事务的隔离级别
数据库事务的隔离级别是指多个事务之间相互隔离的程度,不同隔离级别对应不同的并发控制机制和资源消耗。常见的隔离级别有以下四种:
为什么会有这些隔离级别呢?原因是在多个事务同时进行,或者说两个人同时去对一个数据库中的值进行修改,导致会出现以下问题,有点像我们的多线程下对共享资源的访问不加锁的话会出现一些问题
3.1 脏读、不可重复读、幻读
3.1.1脏读:
脏读指一个事务可以读取另一个事务未提交的数据。例如,一个事务读取了另一个事务修改但未提交的数据,如果这个事务最终回滚了,那么读取的数据就是无效的,这就是脏读。说白了就是读完之后这个值已经被还原了,压根就不是数据库中实际的值,像不存在的鬼、脏东西一样 哈哈。
3.1.2 不可重复读:
不可重复读指一个事务在同一个事务中多次读取同一数据,但是每次读取的结果不同。例如,一个事务读取了一条数据,然后另一个事务修改了这条数据并提交了,那么第一个事务再次读取这条数据时,读取的结果就不同了,这就是不可重复读。
感觉和脏读没啥区别么,其实还是有的:
不可重复读和脏读的区别在于:
1.不可重复读是在一个事务内部多次读取同一行数据时出现的问题,而脏读则是在一个事务读取另一个事务未提交的数据时出现的问题。
2.不可重复读会导致读取到的数据不一致,而脏读可能会导致读取到的数据是不正确的。
3.不可重复读的解决方法是设置事务的隔离级别,如可重复读或串行化,而脏读的解决方法是使用锁或者MVCC(多版本并发控制)等机制来保证事务的隔离性。
3.1.3 幻读
幻读指一个事务在同一个事务中多次执行同一个查询,但是每次查询的结果不同。例如,一个事务执行了一个范围查询,然后另一个事务插入了一个新的符合条件的数据并提交了,那么第一个事务再次执行这个范围查询时,查询的结果就不同了,这就是幻读。
怎么又感觉幻读和不可重复读一样呢?当然也是有的
不可重复读是在一个事务内多次读取同一条数据时出现的问题,而幻读则是在一个事务内多次查询同一条件的数据时出现的问题。
不可重复读会导致读取到的数据不一致,而幻读则会导致查询结果的行数不一致。
不可重复读的解决方法是设置事务的隔离级别,如可重复读或串行化,而幻读的解决方法是使用锁或者MVCC(多版本并发控制)等机制来保证事务的隔离性。
3.1.4 示例说明
下面通过一个简单的示例来说明脏读、不可重复读和幻读的概念和产生原因:
假设有两个事务T1和T2,T1执行了一次查询操作,T2执行了一次更新操作,查询和更新的数据如下:
id | name | age |
---|---|---|
1 | Tom | 25 |
2 | Jack | 30 |
3.1.4.1 脏读:
T1读取了T2未提交的数据,即T2更新后但未提交的数据。例如,T2执行了如下的SQL语句:
UPDATE user SET age = 35 WHERE id = 1;
然后T1执行了如下的SQL语句:
START TRANSACTION;
SELECT age FROM user WHERE id = 1;
这时T1读取到的age值是35,但是T2最终回滚了,这就是脏读。
3.1.4.2 不可重复读:
T1多次读取同一数据,但是每次读取的结果不同,即T2修改了T1读取的数据。例如,T1执行了如下的SQL语句:
START TRANSACTION;
SELECT age FROM user WHERE id = 1;
然后T2执行了如下的SQL语句:
UPDATE user SET age = 35 WHERE id = 1;
COMMIT;
然后T1再次执行了上面的SQL语句,这时读取到的age值就是35,与第一次读取的结果不同,这就是不可重复读。
3.1.4.3 幻读:
T1多次执行同一个查询操作,但是每次查询的结果不同,即T2插入了新的符合条件的数据。例如,T1执行了如下的SQL语句:
START TRANSACTION;
SELECT * FROM user WHERE age > 20 AND age < 30;
然后T2执行了如下的SQL语句:
INSERT INTO user (name, age) VALUES ('Lucy', 25);
COMMIT;
然后T1再次执行了上面的SQL语句,这时查询到的结果中出现了一条新的数据,这就是幻读。
这些问题的产生是因为多个事务并发执行时,可能会产生冲突和竞争,导致数据的不一致性和错误的结果。
为了避免这些问题,数据库引入了事务和隔离级别的概念,通过锁机制和版本控制等方法来控制并发访问,保证数据的正确性和一致性。
3.2 事务的隔离级别
3.2.1 读未提交
读没有提交的数据,也就是刚才说的读到的数据,后来该数据回滚到之前的数据,那么就会出现、脏读、不可重复读、脏读等问题
3.2.2 读已提交
那么解决方法来了,我就只读取已经提交的数据
一个事务只能读取另一个事务已经提交的数据,可以避免脏读,但是可能会出现不可重复读和幻读等问题。
3.2.3 可重复读
原理:快照和版本数据,就像我们的屏幕快照一样,或者就只读取某一个版本的数据,这样可以避免读到别人更新的数据,因为别人再更新,这个是其他的版本了
一个事务在执行过程中多次读取同一数据,结果是一致的,可以避免脏读和不可重复读,但是可能会出现幻读等问题,因为可重复读是读取某一个数据,保证这个数据是其他事务提交之前的版本、但是当进行范围查询,会把其他的事务更新的数据也会查询出来。
3.2.4 序列化
最高的隔离级别,一个事务完全独立于其他事务,所有的操作按照顺序执行(所有的操作全局排序),可以避免脏读、不可重复读和幻读等所有并发问题,但是会对性能带来较大的影响。
意思就是不允许同时去访问或者修改某一个数据,必须按照顺序来执行
序列化的原理是:
1.全局排序:在序列化隔离级别下,所有的事务都被排序成一个全局的序列,每个事务都必须按照这个序列执行。这个序列保证了每个事务的访问顺序,从而避免了并发访问的问题。
2. 阻塞:在序列化隔离级别下,如果一个事务需要访问另一个事务正在访问的数据,那么这个事务就必须等待另一个事务完成。这样可以避免并发访问的问题,但是可能会导致死锁等问题,因此需要谨慎使用。
注意:
需要注意的是,在实际应用中,序列化隔离级别很少被使用,因为它会对性能带来较大的影响。通常情况下,采用更加灵活的隔离级别,如可重复读、读已提交、读未提交等级别,根据实际情况选择合适的隔离级别,并采用锁机制、MVCC等并发控制手段来保证数据的正确性和一致性。只有在需要绝对保证数据的完整性和一致性时,才需要使用序列化隔离级别。