自定义View系列教程00–推翻自己和过往,重学自定义View 探索Android软键盘的疑难杂症 Android多分辨率适配框架(1)— 核心基础 Android程序员C语言自学完备手册 版权声明
事务概述现实生活中我们经常会进行银行转账操作,该操作可分为两部分来完成:账户A转出和账户B转入, 只有这两个部分都无误完成才认为转账成功。在数据库中,可通过两条语句完成该转账操作,如果其中任意一条语句出现异常没有正常执行则会导致两个账户的金额不同步造成错误。 在数据库中使用事务时必须先开启事务,代码如下: START TRANSACTION;
事务开启之后就可以执行SQL语句,SQL语句执行成功后,需要使用相应语句提交事务,代码如下:
请注意:平常我们在MySQL中直接书写的SQL语句都是自动提交的它会立即生效;但是,事务中的操作语句都需要使用COMMIT语句手动提交,否则不会生效。 如果不想提交当前事务还可以使用相关语句取消事务(也称回滚),代码如下: ROLLBACK;
ROLLBACK语句只能针对未提交的事务执行回滚操作,已提交的事务是不能回滚的。 事务入门示例在此,创建一张account表用于表示账户信息并进行相关操作。 先来看事务的提交,代码如下:
再来看事务的回滚,代码如下: DROP TABLE IF EXISTS account;-- 创建account表CREATE TABLE account( id INT primary key auto_increment, name VARCHAR(40), money FLOAT);-- 向表中插入数据INSERT INTO account(name, money) VALUES ('A',1000);INSERT INTO account(name, money) VALUES ('B',1000);-- 开始事务START TRANSACTION;-- 转账UPDATE account SET money=money-100 WHERE NAME= 'A';UPDATE account SET money=money+100 WHERE NAME= 'B';-- 回滚事务ROLLBACK;
事务的特性事务有着非常严格的定义,它必须同时满足4个特性,即:原子性(Atomicity)、一致性 (Consistency)、隔离性(Isolation)、持久性(Durability),简称事务ACID标准。 原子性 事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功才算整个事务执行成功。如果事务中有任何一个SQL语句执行失败,则已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。 —致性 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。 隔离性 数据库为每一个用户开启的事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。例如:多个用户操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 持久性 事务一旦提交,其所做的修改就会永久保存到数据库中,即使数据库发生故障也不 应该对其有任何影响。需要注意的是,事务的持久性不能做到100%的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所提交的数据可能都会丢失。 事务的隔离级别通常情况下数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况。在该情况下和可能出现脏读、重复读以及幻读的情况,为了避免这种情况的发生,就需要为事务设置隔离级别。在此,我们分别介绍在MySQL中事务的4种隔离级别。 READ UNCOMMITTEDREAD UNCOMMITTED(读未提交)是事务中最低的级别。在该级别下的事务可以读 取到另一个事务中未提交的数据也被称为脏读(Dirty Read)。由于该级别太低,所以在实际开发中避免不了任何情况,所以极少使用。 脏读在此演示脏读,详细步骤如下: 第一步 准备数据,代码如下:
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务的隔离级别为READ UNCOMMITTE代码如下: -- 设置事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;-- 查询事务隔离级别SELECT @@tx_isolation;
图示如下: 第四步 在线程乙中开启事务并查询目前所有账户的信息。 代码如下:
图示如下: 在线程甲中开启事务并执行转账。 代码如下: -- 开启事务START TRANSACTION;-- 转账UPDATE account SET money=money-100 WHERE name='A';UPDATE account SET money=money+100 WHERE name='B';
图示如下: 第六步 在线程乙中查询所有账户信息
图示如下: 图示如下: READ COMMITTED为了避免脏读的发生,可将线程乙的隔离级别设置为READ COMMITTED,代码如下: SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
大多数的数据库管理系统(例如Oracle)的默认隔离级别都是READ COMMITTED(读提交)。该级别下的事务只能读取其它事务已经提交的内容,可以避免脏读但不能避免重复读和幻读的情况。 重复读就是在事务内重复读取别的线程已经提交的数据的时读取的结果不一致。导致该问题的原因是查询的过程中其它事务做了更新操作。 幻读是指在一个事务内两次查询中数据条数不一致。导致该问题的原因是查询的过程中其它的事务做了添加操作。 详细步骤如下: 第一步 准备数据,代码如下:
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务的隔离级别为READ COMMITTED代码如下: -- 设置事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;-- 查询事务隔离级别SELECT @@tx_isolation;
图示如下: 第四步 在线程乙中开启事务并查询目前所有账户的信息。 代码如下:
图示如下: 在线程甲中开启事务并执行转账。 代码如下: -- 开启事务START TRANSACTION;-- 转账UPDATE account SET money=money-100 WHERE name='A';UPDATE account SET money=money+100 WHERE name='B';
图示如下: 第六步 在线程乙中查询所有账户信息
图示如下: REPEATABLE READREPEATABLE READ(可重复读)是MySQL默认的事务隔离级别。它可以避免脏读和不可重复读。但理论上,该级别会出现幻读的情况。不过,MySQL的存储引擎通过多版本并发控制机制解决了该问题,因此该级别是可以避免幻读的。在学习REPEATABLE READ之前,我们先来了解不可重复读。 不可重复读不可重复读是指事务中两次査询的结果不一致,原因是在査询的过程中其它事务做了更新等操作。例如,银行在做统计报表时,第一次查询编号为9527的账户中有1000元钱,第二次查询9527账户时有900元钱;原因是银行做报表的统计期间9527账户的主人取出了100元,这样就会导致多次统计报表的结果不一致。 不可重复读和脏读有点类似,但是脏读是读取其它线程的事务未提交的脏数据,不可重复读是在该事务内重复读取其他线程已提交的数据但数据并不一致的情况。 在此演示不可重复,详细步骤如下: 第一步 准备数据,代码如下: DROP TABLE IF EXISTS account;-- 创建account表CREATE TABLE account( id INT primary key auto_increment, name VARCHAR(40), money FLOAT);-- 向表中插入数据INSERT INTO account(name, money) VALUES ('A',1000);INSERT INTO account(name, money) VALUES ('B',1000);
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务隔离级别为READ COMMITTED
图示如下: 第四步 在线程乙中开启事务并查询目前所有账户的信息。 代码如下: -- 开启事务START TRANSACTION;-- 查询所有账户信息SELECT * FROM account;
图示如下: 第五步 在线程甲中利用update语句更改账户A的money字段的值 代码如下:
图示如下: 第六步 在线程乙中在此查询账户信息 代码如下: -- 查询所有账户信息SELECT * FROM account;
图示如下: 为了避免不可重复读的发生,可将线程乙的隔离级别设置为REPEATABLE READ,代码如下:
在为线程乙设置隔离级别为REPEATABLE READ后再重复刚才示例的操作可以发现:线程乙在同一个事务中的查询结果是一致的,并没有查询到线程甲中update修改的内容,因此可以说明REPEATABLE READ隔离级别可以有效避免不可重复读。 详细步骤如下: 第一步 准备数据,代码如下: DROP TABLE IF EXISTS account;-- 创建account表CREATE TABLE account( id INT primary key auto_increment, name VARCHAR(40), money FLOAT);-- 向表中插入数据INSERT INTO account(name, money) VALUES ('A',1000);INSERT INTO account(name, money) VALUES ('B',1000);
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务隔离级别为REPEATABLE READ
图示如下: 第四步 在线程乙中开启事务并查询目前所有账户的信息。 代码如下: -- 开启事务START TRANSACTION;-- 查询所有账户信息SELECT * FROM account;
图示如下: 第五步 在线程甲中利用update语句更改账户A的money字段的值 代码如下:
图示如下: 第六步 在线程乙中在此查询账户信息 代码如下: -- 查询所有账户信息SELECT * FROM account;
图示如下: 幻读幻读(PHANTOM READ)又被称为虚读,是指在一个事务内两次查询中数据条数不一致。例如,银行在做统计报表时统计account表中所有用户的总额时,总共有三个账户总共金额有3000。此时,新增了一个账户并且存人了 1000元;在该情况下,银行再统计时发现账户的总金额变为4000造成了幻读。 在此演示幻读,步骤如下: 第一步 准备数据,代码如下:
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务隔离级别为READ COMMITTED -- 设置事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;-- 查询事务隔离级别SELECT @@tx_isolation;
图示如下: 在线程乙中开启事务并查询目前所有账户的信息。 代码如下:
图示如下: 第五步 在线程甲中插入新的账户C 代码如下: -- 查询账户信息SELECT * FROM account;-- 插入新用户CINSERT INTO account(name, money) VALUES ('C',1000);-- 查询账户信息SELECT * FROM account;
图示如下: 第六步 在线程乙查询所有账户信息 代码如下:
图示如下:
为了防止出现幻读,可以将线程乙的事务隔离级别设置为REPEATABLE READ,代码如下: SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
详细步骤如下: 第一步 准备数据,代码如下:
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务隔离级别为REPEATABLE READ -- 设置事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;-- 查询事务隔离级别SELECT @@tx_isolation;
图示如下: 在线程乙中开启事务并查询目前所有账户的信息。 代码如下:
图示如下: 第五步 在线程甲中插入新的账户C 代码如下: -- 查询账户信息SELECT * FROM account;-- 插入新用户CINSERT INTO account(name, money) VALUES ('C',1000);-- 查询账户信息SELECT * FROM account;
图示如下: 第六步 在线程乙查询所有账户信息 代码如下:
图示如下: SERIALIZABLESERIALIZABLE(可串行化)是事务的最高隔离级别,它会强制对事务进行排序,使之不会发生冲突,从而解决脏读、幻读、重复读的问题。但是,该级别可能导致大量的超时现象和锁竞争,实际应用中很少使用。 在此介绍该隔离级别的使用,步骤如下: 第一步 准备数据,代码如下: DROP TABLE IF EXISTS account;-- 创建account表CREATE TABLE account( id INT primary key auto_increment, name VARCHAR(40), money FLOAT);-- 向表中插入数据INSERT INTO account(name, money) VALUES ('A',1000);INSERT INTO account(name, money) VALUES ('B',1000);
第二步 打开两个Dos中模拟两个操作数据的不同线程甲和乙,图示如下: 第三步 在线程乙中设置事务隔离级别为SERIALIZABLE
图示如下: 在线程乙中开启事务并查询目前所有账户的信息。 代码如下: -- 开启事务START TRANSACTION;-- 查询所有账户信息SELECT * FROM account;
图示如下: 在线程甲中开启事务并插入新的账户C 代码如下:
图示如下: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
图示如下: 图示如下: |
|
来自: 佬总图书馆 > 《区块链、物联网、虚拟数字货币》