【MySQL】浅谈事务

哈喽,大家好~我是你们的老朋友:保护小周ღ  


谈起 “事务”,可能大家都会在心中出现一个大大的 ?,博主的理解——事务就是解决 MySql数据库在应对多线程环境下针对同一存储空间的数据修改引起的数据安全问题的一种机制,本质是将多个 sql 语句打包一个整体,要么全部执行成功,要么都不执行,当出现 sql 语句执行到中间的时候 “服务”,从而造成数据安全问题。


本期收录于博主的专栏JavaEE_保护小周ღ的博客-CSDN博客

适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaEE基础知识”。
更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘


一、事务

MySql 本质是客户端、服务器交互的一种机制,双方建立连接后,客户端,通过用 sql 语句,向MySql 服务器发出请求,服务器收到并解析请求,然后在硬盘上“拿出”相关数据,作为响应返回客户端,客户端将数据以临时表的形式展现给用户。MySql 使用硬盘作为存储介质。

以上机制,如果多个客户端同时针对服务器存储的数据进行增删查改,有可能造成数据安全问题

举个例子1: 

举个例子2 :

MySql的事务就是为了解决上述问题。 

事务的本质就是把多个 sql 语句打包一个整体,要么全部执行成功,要么都不执行,当出现 sql 语句执行到中间的时候 “宕机”,造成数据安全问题,事务会将已修改的数据进行 “回滚”(rollback),将数据还原成没有执行之前的状态,主观上看起来就像没有执行过。

以及解决多线程(多个执行流)针对同一存储空间的数据进行修改的造成的数据安全问题。

1.1 事务的使用

1. 开启事务: start  transaction

2. 执行多条 SQL 语句

3. 回滚事务: rollback  /  提交事务:commit

rollback 代表全部执行失败 ,commit 代表全部执行成功

start transaction; // 开启事务

update balance set balance = balance - 100 where name = "张三";
update balance set balance = balance + 100 where name = "李四";

commit; //提交事务

 如此,多条 sql 语句就被打包成一个整体,可以理解为:一条比较 “粗” 的指令。


1.2 事务的特性

关于数据库的事务,具有四个关键的特性:  敲敲黑板,经典面试题哦~

  1. 原子性:原子,我们第一次听到这个名词应该是从初中化学课中了解到的,指化学反应不可再分的基本微粒。所以事务原子性,事务所包含的 sql 语句是一个整体,要么全部被执行,要么全部不执行,在执行的过程中如果发送意外,导致被事务修饰的整体sql 无法全部被执行,就会触发回滚,将数据还原成没有被修改之前的状态。
  2. 一致性事务执行的前后,数据是靠谱的(跟预计的结果一样)。(主要涉及到事务的原子性)
  • 不一致的例子
  • 付款多次,只扣款1次(或者付款1次,扣款多次)。
  • 转账成功了,但是付款的人余额没扣,或者收款的人余额没有增加。
  • 转账成功,我只给你转账了500,结果你的账户多了5000

    3.持久性:事务修改的内容是写到硬盘上的(mysql 是采用硬盘作为存储介质),数据是持久性的存在,不会因为重启而丢失。

    4.隔离性:所谓隔离性就是为了解决,多个执行流,“并发的”  执行事务所引起的问题,这一点就是涉及到多线程安全问题,多个客户端对同一服务器上的 “事务” 调用处理这种情况——转账。

举个例子:一个餐厅,只有一个窗口,但是此时需要给多个客户提供用餐服务,站在服务员的角度如果客户一窝蜂的来发出请求,服务员指定是手忙脚乱,就有可能在将客户的用餐信息写错(少写,漏写,错写等)。如果是客户排好队一个一个来,服务员的工作质量也会大大提高。多线程(执行流)的情况下,服务器同时处理读个客户端的请求,这个操作就称之为 “并发”。

事务的隔离性,存在的意义就是为了在数据库并发处理事务的时候不会有问题。


1.3 并发执行事务可能产生的问题

1.3.1 脏读问题

脏读问题,在的意思的 “无效” 的意思,不是贬义词。

一个执行流 A 正在对 事务S修饰的 sql 对应的数据进行修改(读到内存),还没来得及将数据写回硬盘,执行流 B 也对 事务S 修饰的 sql 对应的数据 进行了读取,此时执行流 B 读取的操作就称之为“脏读”,读到的数据也被称之为 “脏数据 ”——无效数据,因为,当执行流A 将数据写回硬盘后,执行流 B 在此之前读取的数据就是没有任何意义的。举例上文转账场景二。

为了解决脏读问题,mysql 就引入了 “写加锁” 这样的机制。

当我在写的时候(对数据进行修改),其他人不能读取数据(避免了读取的数据无效),“写操作“ 和 “读操作” 不能同时执行了(并发)。

加锁当然有利也有弊,给 “写操作” 加锁,就降低了并发程度(执行的效率),我写的时候,其他人需要等待我写完,提高了隔离性——多个执行流对数据准确性的带来的影响就越来越小。


1.3.2 不可重复读

针对写操作,约定,我写的时候,不许看,等我提交了,你才能看(这是约定好的写加锁)

我写的时候,你在等待,然后我提交了版本 1 的数据,然后你就可以读取数据,此时我又对该数据进行修改,提交了版本 2 的数据,你刷新一下,重新读取一下,发现,第一次读取到的内容与第二次读取内容并不一样,这个问题就叫做 “不可重复读”。

针对同一存储空间的数据的情况下,执行流 A 提交了数据,此时 执行流 B 开始读取数据,在读取数据的后,执行流 C 也提交了数据,这就意味着执行流 B 如果多次读取数据,读取出来的结果是不相同的,预期在一个读取周期内(事务修饰的读操作),多次读取的结果是一样的,这种情况就叫做”不可重复读“。

为了解决 “不可重复读” 这样的问题,mysql 就引入了 “读加锁” 这样的机制。

约定:一个执行流读取数据的时候,在提交之前,其他执行流不可以进行修改。

通过“读加锁”,又进一步降低了事务并发处理的能力,同时也提高了事务的隔离级别。


1.3.3 幻读

根据上文的讲述,mysql 为了保证在并发处理事务时带来的数据准确性的问题,约定了“读加锁”和“写加锁”,解决了不可重复读和脏读问题。

不可重复读,针对的是同一数据,在一个读取周期内(未提交之前),多次读取的数据是相同的。

幻读问题,针对的是数据的结果集的,在一个读取周期内(未提交之前),第一次读到一条数据,第二次读到了二条数据,第一次读取的数据也在第二次的结果集中存在,也没有改变,区别在于结果集的条数发生了改变。

在读加锁和写加锁的前提下,一个事务两次读取同一个数据,发现读取的数据值是一样的,但是结果集不一样。这种情况称之为 “幻读” 问题。

针对幻读问题,数据库提供的解决方案是使用”串行化“,放弃多个执行流并发处理事务,而是一个一个串行的处理事务——想处理事务的排好队,一个一个来。


1.4 事务的隔离级别

针对上文的三个问题,mysql 数据库提供了不同的解决方案,同时也带来一个概念,隔离级别,隔离性可以看作,一个事务内部的操作及使用的数据对并发的其他执行流是隔离的,某些操作需要排队执行。

1. read uncommitted 没有任何锁限制,执行流并发程度拉满,但是也意味着隔离性很低,数据的准确性也难以保证。

2. 脏读问题,采用”写加锁“,约定写数据的时候,其他执行流不可以读取数据。read committed 给写加锁,降低了多个执行流并发的程度,提高了多个执行流的隔离性。

3. repeatable read 给写和读都加锁,并发程度进一步降低,隔离性提高,数据的准确性也高。

4. serializable 串行化,彻底放弃并行处理,执行流一个一个处理事务,隔离级别是最高的。

以上操作是 mysql 内部的机制。


好了,到这里,网络编程中的 博主已经分享完了,希望对大家有所帮助,如有不妥之处欢迎批评指正。 

感谢每一个观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……