- 2023-11-20 11:05:03
- 3388 热度
- 0 评论
春节之前,無名和大家说要开一个分布式事务的系列,年前发了一篇,这两天闲在家里,又撸了一篇,这是我们这个系列的第二篇。
第一篇参见:
本文依然是一个铺垫,本文我们先来看看 Jdbc 事务。
环境:MySQL5.7
注意,MySQL5.x 和 MySQL8 在驱动类的路径以及数据库连接地址上写法有一些差别。
1. Jdbc事务
1.1 事务提交
Jdbc 默认开启了事务,并且开启了事务的自动提交,开发者可以通过如下代码修改这种默认的策略:
1 |
con.setAutoCommit(false); |
然后通过如下代码手动提交或者回滚一个事务:
1 |
con.commit(); |
1.2 事务隔离级别
Jdbc 中默认的事务隔离级别和数据库保持一致,即如果开发者没有在 Jdbc 中设置数据库事务的隔离级别,默认的事务隔离级别就数据库的事务隔离级别,当然开发者在 Jdbc 中也可以通过如下代码修改事务隔离级别:
1 |
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); |
其中最后一个 NONE 表示不支持事务,另外四个则和数据库四个隔离级别一一对应。
1.2.1 READ COMMITTED
事务隔离级别的验证方式很简单,读者可以参考上篇文章,因为验证过程细节繁琐,本文主要向读者展示 READ COMMITTED 和 REPEATABLE READ 两种隔离级别。READ COMMITTED 具体操作步骤如下(数据源依然使用上文的数据源):
1.创建普通的 Maven 项目,添加数据库依赖:
1 |
<dependency> |
2.创建 DBUtils 类,提供数据库连接,如下:
1 |
public class DBUtils { |
3.接下来创建两个类 Main 和 Main2,相当于两个不同的 SQL 操作 session:
Main:
1 |
public class Main { |
Main2:
1 |
public class Main2 { |
注意,Main2 中有一个 for 循环,将同一个查询操作执行了 3 次,这个循环主要用来验证不可重复读问题和脏读问题。另外在 Main2 中将数据库隔离级别设置为 READ_COMMITTED。
4.在 Main2 类中 for 循环中的第一行代码处打断点,然后以 debug 方式启动 Main2,如图:
由上图可以看到,目前数据库中 zhangsan 账户下有 999 块钱。接下来 Main2 类保持此状态不动。
5.在 Main 类中,提交事务的方法上打断点,然后以 debug 的方式启动 Main 类中的 main 方法,如下:
6.接下来回到 Main2 类中,执行 for 循环的第二次遍历,结果如下:
可以看到,第二次查询结果与第一次是一样的,说明脏读问题已经不存在了。
7.回到 Main 类中,提交事务。
8.回到 Main2,执行第三次 for 循环,发现数据已经发生了变化,即这里发生了不可重复读问题。如下:
1.2.2 REPEATABLE READ
REPEATABLE READ 这种隔离级别很好测试,将 Main2 中如下一行注释掉,使用 MySQL 默认的隔离级别就行了。测试步骤与上文一致,这里就不再赘述。
1 |
con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); |
其他隔离级别的测试也基本一致,这里不再赘述了。
1.3 共享锁和排他锁
1.3.1 共享锁
共享锁又称读锁,是读取操作创建的锁。加了读锁的数据,可以被其他事务继续加读锁并发读取,但任何事务都不能对数据进行修改(也不能给事务加排他锁),直到已释放所有共享锁。
用法:
1 |
SELECT ... LOCK IN SHARE MODE; |
在查询语句后面增加 LOCK IN SHARE MODE
,MySQL 就会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
1.3.2 排他锁
排他锁又称写锁、独占锁,如果一条数据被加上了排他锁,其他事务无法再给该数据加任何锁,而获取排他锁的事务既能读数据,又能修改数据。
用法:
1 |
SELECT ... FOR UPDATE; |
在查询语句后面增加 FOR UPDATE,MySQL 就会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。加了排他锁的操作,有点类似 SERIALIZABLE 的隔离级别。
小结
OK,本文和大家回顾一下 Jdbc 事务,下篇文章我们再来看 Spring 中的事务机制。
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Java(82)
- Cloud(82)
- Spring Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- MVC(22)
- Web(21)
- CLI(20)
- Alibaba(19)
- SpringMVC(19)
- SpringBoot(17)
- Docker(17)
- Eclipse(16)
- Vue(16)
- Git(16)
- JPA(15)
- Apache(15)
- ORA(15)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Linux(14)
- HTTP(14)
- Mybatis(14)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Pro(13)
- JSON(12)
- OAuth2(12)
- Data(12)
- stream(11)
- int(11)
- Myeclipse(11)
- not(10)
- Bug(10)
- maven(9)
- Map(9)
- Hystrix(9)
- ast(9)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- Swagger(8)
- JavaMail(7)
- Cache(7)
- File(7)
- IntelliJ(7)
- mail(7)
- windows(7)
- too(7)
- HTML(7)
- Github(7)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- Server(6)
- nginx(6)
- ueditor(6)
- jar(6)
- ehcache(6)
- UDP(6)
- RabbitMQ(6)
- star(6)
- and(6)
- Struts(5)
- string(5)
- script(5)
- Syntaxhighlighter(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)
- Servlet(5)
- Config(5)
- discuz(5)