MySQL 事务从入门到精通(上):概念、操作、特性、隔离级别全解析
一、事务的概述事务是数据库提供的一个非常重要的特性我们可以把事务理解为组成各个数据操作的执行单元这个单元有一个原则要么都成功要么都不成功不允许出现一部分成功一部分失败的情况。用一个经典的转账例子来说明比如小帅给小美转 1000 元钱这个业务操作必须放在一个事务中执行。第一步先给小帅扣除掉 1000 元第二步再给小美加上 1000 元这两步操作必须同时成功或者同时失败事务才算正常结束。如果中间出现异常就需要把数据恢复到最初的状态。二、在 MySQL 中操作事务先准备好测试用的数据库表和数据以便后续的演示1. 准备数据库信息首先我们创建一张账户表用于存储用户的姓名和余额信息SQL 语句如下create table t_account( id int primary key auto_increment, username varchar(20), money double );表创建完成之后我们插入几条测试数据方便后续演示转账业务insert into t_account values (null,美美,10000); insert into t_account values (null,冠希,10000); insert into t_account values (null,小凤,10000); insert into t_account values (null,熊大,10000); insert into t_account values (null,熊二,10000);2. 在 MySQL 数据库中使用事务的两种方式MySQL 给我们提供了两种操作事务的方式。第一种方式使用命令的方式这种方式是最常用的通过命令手动开启、提交和回滚事务我们还是用冠希给美美转账的例子来演示start transaction; -- 开启事务 update t_account set money money - 1000 where username 冠希; update t_account set money money 1000 where username 美美; commit; -- 提交事务事务已经结束了数据永久的保存到数据库中了 -- rollback; -- 回滚事务事务已经结束了数据回滚到最初始化的状态第二种方式设置 MySQL 事务不默认提交的方式MySQL 数据库的事务是默认提交的。也就是说单独执行一条 update 语句它会默认使用一个事务执行完自动提交。如果我们想把多个 SQL 放在一个事务中就可以设置 MySQL 事务不默认提交具体操作如下set autocommit off或者0 -- 设置MySQL事务不默认提交 -- 编写SQL语句执行完之后都没有提交 update t_account set money money - 1000 where username 冠希; update t_account set money money 1000 where username 美美; -- 需要手动提交或者回滚 commit; -- rollback;我们也可以先查看事务是否默认提交再进行设置完整演示语句show variables like %commit%; -- 查看事务是否是默认提交 set autocommit off; -- 设置不默认提交 update t_account set money money - 1000 where username 冠希; update t_account set money money 1000 where username 美美; commit; -- 或者rollback;三、在 JDBC 中操作事务学会了 MySQL 端的事务操作在 Java 代码中通过 JDBC 操作事务也是我们开发中常用的技能JDBC 中操作事务依靠的是 Connection 接口。1. Connection 接口操作事务的核心方法Connection 接口给我们提供了三个操作事务的方法void setAutoCommit (boolean autoCommit)如果传入 false就设置 MySQL 数据库的事务不默认提交void commit ()提交事务void rollback ()回滚事务2. Connection 接口的两个作用在 JDBC 中Connection 接口有两个重要的作用第一我们操作数据库都需要使用 Connection 接口第二Connection 接口可以用来管理事务。四、事务的特性事务有四大核心特性我们通常称为 ACID 特性这是数据库本身提供的特性1. 事务四大特性详细说明原子性Atomicity事务中所有操作是不可再分割的原子单位事务中所有操作要么全部执行成功要么全部执行失败一致性Consistency事务执行后数据库状态与其它业务规则保持一致例如转账业务无论事务执行成功与否参与转账的两个账号余额之和应该是不变的隔离性Isolation是指在并发操作中不同事务之间应该隔离开来使每个并发中的事务不会相互干扰持久性Durability指的是一旦事务提交成功事务中所有的数据操作都必须被持久化到数据库中即使提交事务后数据库马上崩溃重启后也必须能保证通过某种机制恢复数据2. 四大特性侧重点总结原子性强调的是事务的不可分割的特性一致性强调的是事务执行前后数据需要保证一致隔离性强调的是多个事务同时操作一条记录事务之间不能互相干扰持久性强调的是事务一旦结束了数据将永久的保存到数据库中这里需要特别注意这些特性都是数据库提供的不是我们代码手动实现的。五、不考虑事务的隔离性会引发的问题事务的隔离性是为了解决并发操作的问题如果我们不考虑事务的隔离性在多个事务同时运行的时候就会出现三种读取数据的问题我们需要清楚每种问题的含义。脏读一个事务读取到了另一个事务未提交的数据不可重复读一个事务读取到了另一个事务提交的数据导致了多次查询的结果不一致强调的是 update修改记录的数据幻读虚读一个事务读取到了另一个事务提交的数据导致了多次查询的结果不一致强调是 insert向表中添加一条数据六、设置事务的隔离级别解决读的问题为了解决上面提到的脏读、不可重复读、幻读问题数据库给我们提供了四种事务隔离级别不同的隔离级别能解决不同的问题同时安全性和效率也不一样。1. 四种事务隔离级别Read uncommitted什么问题都解决不了Read committed避免脏读但是不可重复读和幻读有可能产生Repeatable read避免脏读和不可重复读幻读有可能产生Serializable避免各种读问题2. 隔离级别安全性与效率对比安全性Serializable Repeatable read Read committed Read uncommitted效率Serializable Repeatable read Read committed Read uncommitted3. 数据库默认隔离级别不同的数据库有自己默认的隔离级别其中 MySQL 数据库的默认隔离级别是Repeatable read可以避免脏读和不可重复读。七、事务隔离级别相关演示为了让大家更直观理解不同隔离级别解决的问题我们可以通过两个 MySQL 窗口进行测试演示。1. 演示脏读脏读就是一个事务读取到了另一个事务未提交的数据测试步骤开启两个窗口A 窗口和 B 窗口在 A 窗口查询隔离级别select tx_isolation;设置 A 窗口隔离级别为最低set session transaction isolation level read uncommitted;两个窗口都开启事务start transaction;B 窗口执行转账操作不提交事务A 窗口查询数据能读到 B 窗口未提交的数据B 窗口执行 rollback 回滚事务A 窗口读到的数据就是无效的脏数据2. 避免脏读和演示不可重复读想要避免脏读我们需要提高隔离级别设置隔离级别为 Read committedset session transaction isolation level read committed;两个窗口开启事务B 窗口执行转账不提交事务A 窗口查询不到未提交数据避免了脏读B 窗口提交事务后A 窗口再次查询结果不一致出现不可重复读3. 避免不可重复读想要解决不可重复读设置隔离级别为 Repeatable readset session transaction isolation level repeatable read;按照同样的步骤测试就能避免不可重复读问题。4. 避免各种读问题如果想要完全避免脏读、不可重复读、幻读所有问题设置最高隔离级别 Serializableset session transaction isolation level serializable;这种级别会串行执行事务所有读问题都能解决但是效率最低。总结事务是数据库操作的核心尤其是在金融、订单、支付等业务中事务的使用直接决定了数据是否安全可靠。下一篇文章我们会接着学习数据库连接池的相关知识包括连接池概念、常用连接池以及 Druid 连接池的使用和工具类封装。