Mysql事务隔离与Spring
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';
事务
事务个人定义:某一任务序列的集合。
举例:
小明在银行取钱的一个序列
上面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=2
和id=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;
删除指定存储点位置 后面无法执行此id
的rollback 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.java
的 22行
和24行
的testTx.query();
打印出相同的查询结果,且能读取到脏数据。证明自己的事务TestTx
的query
方法配置Isolation.READ_COMMITTED
没有生效,而是采用TestBean
类testOne
方法的READ_UNCOMMITTED
。
结论二:
TestBean.java
的 22行
和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相关推荐
- MySQL事务隔离级别和实现原理
经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下. MySQL 事务 本文所说的 MySQL 事务都是指在 I ...
- 5、MySQL事务隔离级别详解
事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰.如果事务没有隔离性,就容易出现脏读.不可重复读和幻读等情况. 为了保证并发时操作数据的正确性,数据库都会有事务隔离级别的概念. ...
- Mysql事务隔离级别及MVCC(多版本并发控制)
一.MySQL事务隔离级别 先注明一点:以下讨论都是在多事务并发的情境下讨论的 事务的特性(InnoDB引擎才有事务): ACID 原子性:一个事务不可再分割,要么都执行要么都不执行 一致性:一个事务 ...
- 面试刷题29:mysql事务隔离实现原理?
mysql的事务是innodb存储引擎独有的,myisam存储引擎不支持事务. 事务最经典的例子就是转账了,事务要保证的是一组数据库的操作要么全部成功,要么全部失败.是为了保证高并发场景下数据的正确性 ...
- mysql 事务 隔离级别_MySQL的四种事务隔离级别
https://www.cnblogs.com/huanongying/p/7021555.html 本文实验的测试环境:Windows 10+cmd+MySQL5.6.36+InnoDB 一.事务的 ...
- mysql事务隔离级别 花_mysql事务隔离级别
很多PHP开发者在面试的时候遇到这个问题都会卡壳.这是因为理解得不够透彻,今天让我带领大家梳理一下mysql事务隔离级别 数据库有四种隔离级别,分别是Read uncommitted,Read com ...
- mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚
mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 参考文章: (1)mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 (2)https://www.cnblog ...
- MySQL事务隔离与行锁的关系
MySQL事务隔离与行锁的关系 1. MySQL事务隔离 2. MySQL行锁 3. 事务隔离问题 1. MySQL事务隔离 MySQL事务详解 MySQL事务详解中介绍了事务隔离相关概念原理,如果是 ...
- 【MySQL进阶】MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!)
[MySQL进阶]MySQL事务隔离与锁机制底层原理万字总结(建议收藏!!) 参考资料: 美团技术团队:Innodb中事务隔离级别和锁的关系 数据库的锁,到底锁的是什么? 阿里面试:说说一致性读实现原 ...
最新文章
- 中职计算机php学啥,计算机专业都学什么主要课程有什么_中职中专网
- 在Ubuntu 14.04 64bit上安装OpenResty 1.9.7.4
- html标签ref,HTML: param 标签
- SteamVR 工具包VRTK实例解析
- hashmap 存取原理图_HashMap底层实现原理
- MVC三层架构(详解)
- vue-router嵌套路由,默认子路由设置
- 演练 影视演员简介 0929
- java 自定义编译注解 进行代码检查
- Android多媒体学习八:调用Android自带的音频录制程序,实现录制
- golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题
- 国内最火的 HTML、CSS、JavaScript 开源项目 Top 榜,你知多少?
- Linux操作系统Ifconfig命令详细解析
- python内置数据结构和stl_python里有C++ STL中的set和map吗?
- 计算机网络之万维网WWW
- 数据库原理——图书馆管理系统
- div+css静态网页设计 web网页设计实例作业 ——中国水墨风的小学学校网站(6页) 专题网页设计作业模板 学校物静态HTML网页模板下载
- 高德地图API:如何根据经纬度获取位置信息
- Pull request 团队合作开发使用详解
- 如何清除/删除最近的文档历史记录?