Spring学习事务的时候看到很多只读事务 和事务隔离和事务传播和事务挂起,为了更好的理解学习了mysql这块的知识。

本文用的SQL命令

#------ 设置事务隔离登记-----#读已提交
set session transaction isolation level read committed ;
#读未提交
set session transaction isolation level read uncommitted ;
#可重复度 mysql默认级别
set session transaction isolation level repeatable read  ;
#串行化
set session transaction isolation level serializable  ;#------ 开启事务-----#开启可读写事务
start transaction;
#开启只读事务
start transaction read only;
#开启读写事务 与start transaction;等价
start transaction read write;# 提交事务
commit;# 事务回滚
rollback;#------事务挂起-----#记录事务保存点 mm为保存点自定义的id
savepoint mm;
#回滚事务到mm 保存点.直接运行rollback全部回滚
ROLLBACK TO mm;
#删除保存点
RELEASE SAVEPOINT mm;# 获取事务隔离类 如 read committed等
SHOW VARIABLES LIKE 'transaction_isolation';

事务

事务个人定义:某一任务序列的集合。

举例:
小明在银行取钱的一个序列

小明
插卡
输入密码
取款金额
ATM吐纸钞退卡

上面5个步骤合在一起我们就称为一个事务。

在Mysql我们可以理解多个Sql语句的合集。

如下Sql:

# 小明给小张转账
update User set money=money-23 where username='小明';
update User set money=money+23 where username='小张';

上述两个sql语句根据固定顺序构成了一事务。

我们之所以把一定任务序列构成事务,是因为我们希望任务序列要么全部执行成功,要么不执行。

举个例子:

# 小明给小张转账
update User set money=money-23 where username='小明';
#假设只执行上面的小明的 money=money-23 但是还没执行
#小张的money=money+23语句,此时出现了异常不能执行(比如断电) money=money+23
#那么我们希望 money=money-23 语句就行回滚,就像没发生过一样,小明
#和小张金额一起没有任何变化
update User set money=money+23 where username='小张';

未解决上面的问题可以采用mysql 为我们提供事务语句解决


# 开启事务
start transaction ;
# start transaction read only; 只读事务 后续讲解#事务开启后如果没有执行 commit 那么数据并不会真正写入数据库中,
# 但sql语句会生产临时数据(临时数据后面在引用问题,当然不会扩展到mvvc内容,主要我太菜)。# 小明给小张转账
update User set money=money-23 where username='小明';update User set money=money+23 where username='小张';#提交事务
commit ;#根据需要回滚
# rollback;

数据库只有一个,但是数据库操却并发着。比如12306有无数个请求(无数个事务)在修改数据库,那么这么多事务并发操作必然存在问题。

我们知道在事务中SQL语句在没有提交事务的时候,虽然不会最终写入数据库,但会产生临时数据,这些临时数据会被其他事务读取。

问题1: 脏读

事务读取到其他事务未提交的临时数据。

问题2: 不可重复读

事务两次读取同一记录的数据不一致。

问题3:幻读

事务A 修改/删除/更新 了一些在其他事务已经删除或者插入的数据


上图中某个事务没有id=2的数据(此时其他事务插入的),但是更新id=2为的数据居然成功。

参考:
维基百科 Isolation https://en.wikipedia.org/wiki/Isolation_(database_systems)

针对事务并发导致的几个问题,数据库innodb提供几个事务隔离策略让我们针对上诉的问题针对性解决。read committed、read uncommitted、 repeatable read、serializable。

read uncommitted 读未提交

此隔离模式会导致事务可以读取其他未提交的事务的临时数据。
假设B事务修改了User表的小李的Age=20字段 但未提交事务,此时A事务将读取到小李的Age字段等于20。

模拟环境如下:
A事务、 B事务。
表结构:

-- auto-generated definition
create table User
(id       int auto_incrementprimary key,username varchar(255) null,birthday date         null,sex      varchar(255) null,address  varchar(255) null,money    int          null
);

A事务执行任务如下:


可见此隔离无法阻止脏读,同理可证明无法阻止幻读和不可重复度问题。

这在注意一个问题,事务B此时对User表id为1的数据进行了行锁。其他事物如果需要修改id为1的数据需要事务B提交事务才可。

read committed 读已提交

可以读取其他事务已提交的数据,对于其他未提交的事务的临时数据是无法查询。

此处可证明read committed能解决数据脏读问题。但对于幻读可重复读无法解决。

repeatable read 可重复读

多次重复读取可保持一致性

事务A修改id=1的数据时会自动同步B事务提交id=1新数据(假设事务B同时修改id=2id=1的数据,此时也不会刷新id=2的新数据,只是刷新id=1)



再次提一个小心点可重复读,修改一个数据时,此数据行若被其他事务修改,那么以其他事物已提交的数据为数据源做修改。

同理事务B插入新的数据行,事务A同样无法查询到。除非事务A使用update更新整个表。

但可重复读无法避免幻读现象

事务A中查询不到id=2的数据,然后执行更新id=2操作居然成功了

serializable 串行执行

一个事务只能等候另一个事务执行完成了才可以使用。

此隔离等级可解决幻读 重复读 脏读问题,但效率过差

只读事务

只读事务是指建立在四种隔离机制之上的。表示当前事务只能对表进行读取操作,其他删除/更新/插入会导致锁异常。

如执行以下下SQL语句

set session transaction isolation level read committed ;start transaction read only ;#不能执行
#insert User(id,username) values (2,'小帅');#不能执行
#update  User set username=concat(username,' 放学') ;#不能执行
#delete from User;#因为设置了 read committed ; 查询其他事务已经提交的数据,和原来无差别
select *
from User;commit ;

start transaction read only ;开启可读事务,mysql会对其进行优化处理 。但事务内部查询可见性依然由事务隔离级别决定。

事务的保存点

有时候我们希望在事务执行时可以控制部分区域回滚,可以让我们更细腻化的控制的。

其他命令这里直接给出结论读者可以自己进行验证。存在多个存储点时,执行rollback 将回滚全部,而rollback to XX 会回滚到指定点。RELEASE SAVEPOINT xx; 删除指定存储点位置 后面无法执行此idrollback to XX

Tip:Spring事务传播NESTED类型就是使用Savepoint

事务嵌套问题

事务开启后无法变更隔离级别,包含事务保存点savepoint之后调用set session transaction isolation level xxxx ;也无法更改

执行以下sql文件

set session transaction isolation level read uncommitted;
start transaction;SHOW VARIABLES LIKE 'transaction_isolation';#返回read uncommittedselect *
from User;#想修改为 读已提交
set session transaction isolation level read committed ;SHOW VARIABLES LIKE 'transaction_isolation'; #虽然返回read committed 但是依然是read uncommitted;#结果还是read uncommitted;
select *
from User;#想修改为 读已提交
#并且在一次开始 依然没用
set session transaction isolation level read committed ;
start transaction;#结果还是read uncommitted;
select *
from User;commit;

效果图

Spring事务传播

事务类型 说明
PROPAGATION_REQUIRED 如果当前存在事务,那么使用当前事务,自身事务配置将 不生效,如果不存在使用自身配置信息创新事务
PROPAGATION_SUPPORTS 如果当前存在事务,那么使用当前事务,如果没有那么以非事务运行
PROPAGATION_MANDATORY 支持(使用)当前事务,如果不存在事务抛出异常
PROPAGATION_REQUIRES_NEW 如果当前存在事务,则挂起当前事务,然后开启自身配置事务
PROPAGATION_NOT_SUPPORTED 以非事务的方式运行,如果有事务存在,则挂起当前事务
PROPAGATION_NEVER 已非事务的方式运行,如果有事务存在,则抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行 使用savePoint(保存点)技术

介绍几个关键API:

 DataSourceUtils.getConnection(dataSources)

获取当前线程的数据源的Connection对象

PROPAGATION_REQUIRED和PROPAGATION_REQUIRES_NEW

//TestTx.java@Component
public class TestTx {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;/*** 事务隔离级别为 读取已提交事务的数据* 传播级别:PROPAGATION_REQUIRED 如果当前事务存在事务那么自身事务配置不生效,直接加入到现有事务中* 如果没有事务那么使用自身的事务隔离级别创建事务*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public void query() {List<User> query = jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));System.out.println("TestTx ::" + query);print("TestTx.query", dataSource);}public void print(String prefix, DataSource dataSources) {try {System.out.println("\n\n" + prefix + " : \n" +"getTransactionIsolation : " + DataSourceUtils.getConnection(dataSources).getTransactionIsolation() +"\nconn : " + DataSourceUtils.getConnection(dataSources) + "\n\n");} catch (SQLException e) {e.printStackTrace();}}
}
//TestTx2.java
@Component
public class TestTx2 {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;/*** 事务隔离级别:读取已经提交事务的数据* 传播:如果存在事务那么挂起,开启自己的新事务*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)public void query() {List<User> query = jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));System.out.println("TestTx2::" + query);print("TestTx2.query", dataSource);}public void print(String prefix, DataSource dataSources) {try {System.out.println("\n\n" + prefix + " : \n" +"getTransactionIsolation : " + DataSourceUtils.getConnection(dataSources).getTransactionIsolation() +"\nconn : " + DataSourceUtils.getConnection(dataSources) + "\n\n");} catch (SQLException e) {e.printStackTrace();}}

测试一

//TestBean.java
@Component
public class TestBean {@AutowiredTestTx testTx;@AutowiredTestTx2 testTx2;/*** 开启事务 可以读取一些未提交的事务数据*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testOne() {try {//#22行testTx.query();//#24行testTx.query();//#26行testTx2.query();} catch (Exception e) {e.printStackTrace();}}

testOne方法输出:

TestTx ::[User{id=688022, userName='新插入未提交', birthday=null, sex='null', address='null'}, User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx.query :
getTransactionIsolation : 1
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@7d32c62e [wrapping: com.mysql.cj.jdbc.ConnectionImpl@59ffa66d]TestTx ::[User{id=688022, userName='新插入未提交', birthday=null, sex='null', address='null'}, User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx.query :
getTransactionIsolation : 1
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@7d32c62e [wrapping: com.mysql.cj.jdbc.ConnectionImpl@59ffa66d]TestTx2::[User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx2.query :
getTransactionIsolation : 2
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@73f75fec [wrapping: com.mysql.cj.jdbc.ConnectionImpl@4d021f9e]

输出分析:
{id=688022, userName='新插入未提交', birthday=null, sex='null', address='null'}为其他事务未提交的脏数据

结论一:
TestBean.java22行24行testTx.query();打印出相同的查询结果,且能读取到脏数据。证明自己的事务TestTxquery方法配置Isolation.READ_COMMITTED没有生效,而是采用TestBeantestOne方法的READ_UNCOMMITTED

结论二:
TestBean.java22行24行testTx.query();打印出相同Connection对象。加入其他事务的时候会使用同一Connection对象。

结论三:
TestBean.java的26行testTx2.query();没有打印出{id=688022, userName='新插入未提交', birthday=null, sex='null', address='null'}证明自身的Isolation.READ_COMMITTED生效。

结论四:
TestBean.java的26行testTx2.query();打印的Connection对象与 22行24行testTx.query();不相同。证明不同一个事务Connection对象不同.


测试二

@Component
public class TestBean {@AutowiredTestTx testTx;@AutowiredTestTx2 testTx2;/*** 开启事务 可以读取一些未提交的事务数据*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testOne() {try {testTx.query();testTx.query();testTx2.query();} catch (Exception e) {e.printStackTrace();}}/*** 与测试一不同的是 自身没有开启事务*/public void testTwo() {try {testTx.query();testTx.query();testTx2.query();} catch (Exception e) {e.printStackTrace();}}}

testTwo输出:

TestTx ::[User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx.query :
getTransactionIsolation : 2
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@7d32c62e [wrapping: com.mysql.cj.jdbc.ConnectionImpl@59ffa66d]TestTx ::[User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx.query :
getTransactionIsolation : 2
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@7577863f [wrapping: com.mysql.cj.jdbc.ConnectionImpl@73f75fec]TestTx2::[User{id=688024, userName='张三', birthday=null, sex='null', address='null'}, User{id=688025, userName='张三', birthday=null, sex='null', address='null'}]TestTx2.query :
getTransactionIsolation : 2
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@2a4e842f [wrapping: com.mysql.cj.jdbc.ConnectionImpl@73f75fec]

结论一:
三者使用不同Connection对象,且使用自己的隔离级别创建事务。

测试三

关于PROPAGATION_REQUIRES_NEW会把原事务挂起,PROPAGATION_REQUIRED会加入现有事务的测试说明。

@Component
public class TestTx {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;/*** 添加用户* 传播级别:PROPAGATION_REQUIRED 如果当前事务存在事务那么自身事务配置不生效(readonly=true也会失效 一样可以插入,* isolation跟随当前事务 自身也失效,此处可以参考 之前查询测试),直接加入到现有事务中.* 如果没有事务那么使用自身的事务隔离级别创建事务** @param user*/@Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED, readOnly = true)public void addUser(User user) {jdbcTemplate.update("insert into User( username)  values (?) ", user.getUserName());print("TestTx.addUser", dataSource);}@Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED)public void addUserWithExeption(User user) {jdbcTemplate.update("insert into User(username)  values (?) ", user.getUserName());print("TestTx.addUserWithExeption", dataSource);int i = 1 / 0;}/*** 事务隔离级别为 读取已提交事务的数据* 传播级别:PROPAGATION_REQUIRED 如果当前事务存在事务那么自身事务配置不生效,直接加入到现有事务中* 如果没有事务那么使用自身的事务隔离级别创建事务*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public void query() {List<User> query = jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));System.out.println("TestTx ::" + query);print("TestTx.query", dataSource);}public void print(String prefix, DataSource dataSources) {try {System.out.println("\n\n" + prefix + " : \n" +"getTransactionIsolation : " + DataSourceUtils.getConnection(dataSources).getTransactionIsolation() +"\nconn : " + DataSourceUtils.getConnection(dataSources) + "\n\n");} catch (SQLException e) {e.printStackTrace();}}}

测试类

//TestBean.java
@Component
public class TestBean {@AutowiredTestTx testTx;/*** 与测试一不同的是 自身没有开启事务*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testThree() throws Exception {testTx.addUser(new User("李四"));testTx.addUserWithExeption(new User("王五"));}

这里直接说结论 李四 王五都没有插入。两种使用同一个Connection

捕获回滚异常问题,请看下图

//TestBean.java
@Component
public class TestBean {@AutowiredTestTx testTx;/*** 与测试一不同的是 自身没有开启事务*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testThree() throws Exception {testTx.addUser(new User("李四"));try {testTx.addUserWithExeption(new User("张珊"));} catch (Exception e) {//addUserWithExeption因为传播机制是REQUIRED//所以和当前方法为同一事物,当addUserWithExeption发生//异常时会标记以前当前事物为回滚,并需要获取addUserWithExeption返回的异常//自己捕获后,testThree的AOP after advice代码(在testThree方法最后切入的代码)无法捕获到异常,而标记了回滚,此时会在        System.out.println("====再次插入小明后=====");//之后抛出异常Transaction rolled back because it has been marked as rollback-onlySystem.out.println("异常:::"+e.getMessage());}System.out.println("=====再次插入小明前====");testTx.addUser(new User("小明"));System.out.println("====再次插入小明后=====");}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:bean.xml"})
@ComponentScan({"com.fmy"})
public class Demo {@AutowiredTestTx testTx;@AutowiredTestBean testBean;@Testpublic void main() {try {testBean.testThree();} catch (Exception e) {System.out.println("最外层调用testThree 捕获到异常::"+e.getMessage());}}
}

以上运行结果 ()


TestTx.addUserWithExeption :
getTransactionIsolation : 1
conn : com.mchange.v2.c3p0.impl.NewProxyConnection@4bc88ff5 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@424d86ce]异常:::/ by zero
=====再次插入小明前====
====再次插入小明后=====
最外层调用testThree 捕获到异常::Transaction rolled back because it has been marked as rollback-only

虽然小明 李四和张珊都没插入 但是不建议这么捕获异常

如果addUserWithExeption传播机制改为REQUIRES_NEW即可避免,但事务不同请开发者自行注意。

PROPAGATION_REQUIRED 测试用例


@Component
public class TestTx2 {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)public void addUser(User user) {jdbcTemplate.update("insert into User( username)  values (?) ", user.getUserName());print("TestTx2.addUser", dataSource);}
 /*** 与测试一不同的是 自身没有开启事务*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testFour() throws Exception {testTx.addUser(new User("李四"));//注意testTx2的addUser处于不同事务中testTx2.addUser(new User("张三"));int i = 1 / 0;}

结果:
张三插入
李四失败

testTx和testTx2使用不同connection 且不再同一个事务。当执行1/0发生异常,只发生在testTx事务中,testTx2事务不受影响。

PROPAGATION_NESTED

事务嵌套,此处采用采用SavePoint技术。

//TestTx2.java
@Component
public class TestTx2 {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED)public void addUser(User user) {jdbcTemplate.update("insert into User( username)  values (?) ", user.getUserName());int i = 1 / 0;print("TestTx2.addUser", dataSource);}/*** 事务隔离级别:读取已经提交事务的数据,但是当前当前事务isolation跟随已存在事务隔离机制* 传播: 如果当前存在事务,则在嵌套事务内执行 使用 数据库savepoit技术,。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED)public void query() {List<User> query = jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));System.out.println("TestTx2::" + query);print("TestTx2.query", dataSource);}

@Component
public class TestTx {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredDataSource dataSource;/*** 添加用户* 传播级别:PROPAGATION_REQUIRED 如果当前事务存在事务那么自身事务配置不生效(readonly=true也会失效 一样可以插入,* isolation跟随当前事务 自身也失效,此处可以参考 之前查询测试),直接加入到现有事务中.* 如果没有事务那么使用自身的事务隔离级别创建事务** @param user*/@Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED, readOnly = true)public void addUser(User user) {jdbcTemplate.update("insert into User( username)  values (?) ", user.getUserName());//        print("TestTx.addUser", dataSource);}/*** 事务隔离级别为 读取已提交事务的数据* 传播级别:PROPAGATION_REQUIRED 如果当前事务存在事务那么自身事务配置不生效,直接加入到现有事务中* 如果没有事务那么使用自身的事务隔离级别创建事务*/@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public void query() {List<User> query = jdbcTemplate.query("select * from User", new BeanPropertyRowMapper(User.class));System.out.println("TestTx ::" + query);print("TestTx.query", dataSource);}
}
@Component
public class TestBean {@AutowiredTestTx testTx;@AutowiredTestTx2 testTx2;/*** 与测试一不同的是 自身没有开启事务*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testFive() throws Exception {testTx.query();testTx2.query();}}

结果:
testTx和testTx2在同一个事务,并且使用同一个connection,查询数据相同。读取的数据全部是READ_UNCOMMITTED级别的,虽然testTx2设置READ_COMMITTED了,但是记住同一个事务开启后不可以更换隔离级别。

继续测试最后一个


@Component
public class TestBean {@AutowiredTestTx testTx;@AutowiredTestTx2 testTx2;/*** 与测试一不同的是 自身没有开启事务*/@Transactional(isolation = Isolation.READ_UNCOMMITTED)public void testSix() throws Exception {testTx.addUser(new User("张三"));try {testTx2.addUser(new User("李四"));} catch (Exception e) {e.printStackTrace();}}}

结果:

结果插入张三,
李四插入失败。
虽然 testTx2和testTx在同一个事务,但是testTx2使用savepoint技术,只会回滚此方法的数据.(记得捕获testTx2的异常,不然testTx会一起回滚,事务异常会把异常向上抛出,NESTED和REQUIRES_NEW可以捕获,其他隔离级别请勿捕获)

Mysql事务隔离与Spring相关推荐

  1. MySQL事务隔离级别和实现原理

    经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下. MySQL 事务 本文所说的 MySQL 事务都是指在 I ...

  2. 5、MySQL事务隔离级别详解

    事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰.如果事务没有隔离性,就容易出现脏读.不可重复读和幻读等情况. 为了保证并发时操作数据的正确性,数据库都会有事务隔离级别的概念. ...

  3. Mysql事务隔离级别及MVCC(多版本并发控制)

    一.MySQL事务隔离级别 先注明一点:以下讨论都是在多事务并发的情境下讨论的 事务的特性(InnoDB引擎才有事务): ACID 原子性:一个事务不可再分割,要么都执行要么都不执行 一致性:一个事务 ...

  4. 面试刷题29:mysql事务隔离实现原理?

    mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务. 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败.是为了保证高并发场景下数据的正确性 ...

  5. mysql 事务 隔离级别_MySQL的四种事务隔离级别

    https://www.cnblogs.com/huanongying/p/7021555.html 本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的 ...

  6. mysql事务隔离级别 花_mysql事务隔离级别

    很多PHP开发者在面试的时候遇到这个问题都会卡壳.这是因为理解得不够透彻,今天让我带领大家梳理一下mysql事务隔离级别 数据库有四种隔离级别,分别是Read uncommitted,Read com ...

  7. mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚

    mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 参考文章: (1)mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 (2)https://www.cnblog ...

  8. MySQL事务隔离与行锁的关系

    MySQL事务隔离与行锁的关系 1. MySQL事务隔离 2. MySQL行锁 3. 事务隔离问题 1. MySQL事务隔离 MySQL事务详解 MySQL事务详解中介绍了事务隔离相关概念原理,如果是 ...

  9. 【MySQL进阶】MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!)

    [MySQL进阶]MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!) 参考资料: 美团技术团队:Innodb中事务隔离级别和锁的关系 数据库的锁,到底锁的是什么? 阿里面试:说说一致性读实现原 ...

最新文章

  1. 中职计算机php学啥,计算机专业都学什么主要课程有什么_中职中专网
  2. 在Ubuntu 14.04 64bit上安装OpenResty 1.9.7.4
  3. html标签ref,HTML: param 标签
  4. SteamVR 工具包VRTK实例解析
  5. hashmap 存取原理图_HashMap底层实现原理
  6. MVC三层架构(详解)
  7. vue-router嵌套路由,默认子路由设置
  8. 演练 影视演员简介 0929
  9. java 自定义编译注解 进行代码检查
  10. Android多媒体学习八:调用Android自带的音频录制程序,实现录制
  11. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题
  12. 国内最火的 HTML、CSS、JavaScript 开源项目 Top 榜,你知多少?
  13. Linux操作系统Ifconfig命令详细解析
  14. python内置数据结构和stl_python里有C++ STL中的set和map吗?
  15. 计算机网络之万维网WWW
  16. 数据库原理——图书馆管理系统
  17. div+css静态网页设计 web网页设计实例作业 ——中国水墨风的小学学校网站(6页) 专题网页设计作业模板 学校物静态HTML网页模板下载
  18. 高德地图API:如何根据经纬度获取位置信息
  19. Pull request 团队合作开发使用详解
  20. 如何清除/删除最近的文档历史记录?

热门文章

  1. 中国一百大姓,您排第几?
  2. 思科模拟器8.1版本身份验证失败禁用网络解决
  3. 第4次作业类测试代码+105032014070+胡阳洋
  4. python鲜花水果商城毕业设计开题报告
  5. MPEG-2 PS流
  6. 关于element plus 插件 e-table的用法
  7. 开发那些坑之使用百川趣拍sd集成真实项目
  8. 哪些工具可以让嵌入式开发事半功倍?详细盘点嵌入式工程师必备工具!
  9. 与 Hadoop 对比,如何看待 Spark 技术
  10. hazelcast的坑爹事