【DataBase】事务
一、事务概述
- 事务的概念:事务是指逻辑上的一组操作,这组操作要么同时完成要么同时不完成。
- 假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
- 如果你自己不去控制事务,数据库默认一条sql语句就处在自己单独的事务当中。
也可以使用命令去开启一个事务:
start transaction
:开启事务,这条语句之后的sql语句将处在一个事务当中,这些sql语句并不会立即执行Commit
:提交事务,一旦提交事务,事务中的所有sql语句才会执行。Rollback
:回滚事务,将之前所有的SQL取消。
JDBC中管理事务:
conn.setAutoCommit(false);
conn.commit();
conn.rollback();
conn.setSavePoint();
conn.rollback(sp);
二、事务的四大特性(ACID)
- 原子性:事务的一组操作是原子的不可再分割的,这组操作要么同时完成要么同时不完成。
- 一致性: 事务在执行前后数据的完整性保持不变。数据库在某个状态下符合所有的完整性约束的状态叫做数据库具有完整性。在解散一个部门时应该同时处理员工表中的员工保证这个事务结束后,仍然保证所有的员工能找到对应的部门,满足外键约束。
- 隔离性:当多个事务同时操作一个数据库时,可能存在并发问题,此时应保证各个事务要进行隔离,事务之间不能互相干扰。
- 持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,不能再回滚。
三、事务的隔离性导致的问题
(所有的问题都是在某些情况下才会导致问题)
- 将数据库设计成单线程的数据库,可以防止所有的线程安全问题,自然就保证了隔离性.但是如果数据库设计成这样,那么效率就会极其低下.
- 如果是两个线程并发修改,一定会互相捣乱,这时必须利用锁机制防止多个线程的并发修改
- 如果两个线程并发查询,没有线程安全问题
- 如果两个线程一个修改,一个查询......
(一)、脏读:一个事务读取到了另一个事务未提交的数据。
#初始账户数据
a 1000
b 1000
----------
#a: 开启事务并进行对账户数据进行修改,但并没有提交
start transaction;
update account set money=money-100 where name=a;
update account set money=money+100 where name=b;
----------
#b: 开启事务查询账户
start transaction;
select * from account;
#查询结果如下:
a : 900
b : 1100
----------
#a: 这是回滚数据
rollback;
----------
#b:再此查询
start transaction;
select* from account;
#查询结果如下
a: 1000
b: 1000
(二)、不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同. --- 行级别的问题
a: 1000 1000 1000
b: 银行职员
---------
b:start transaction;
select 活期存款 from account where name='a'; ---- 活期存款:1000
select 定期存款 from account where name='a'; ---- 定期存款:1000
select 固定资产 from account where name='a'; ---- 固定资产:1000
-------
a:
start transaction;
update accounset set 活期=活期-1000 where name='a';
commit;
-------
select 活期+定期+固定 from account where name='a'; --- 总资产:2000
commit;
----------
(三)、幻读(虚读):一个事务读取到了另一个事务插入的数据(已提交),导致前后读取不一致 --- 表级别的问题
a: 1000
b: 1000
d: 银行业务人员
-----------
d:
start transaction;
select sum(money) from account; --- 2000 元
select count(name) from account; --- 2 个
------
c:
start transaction;
insert into account values(c,4000);
commit;
------
select sum(money)/count(name) from account; --- 平均:2000元/个
commit;
------------
四、数据库的四个隔离级别
- Read uncommitted:如果将数据库设定为此隔离级别,数据库将会有脏读、不可重复度、幻读的问题。
- Read committed:如果将数据库设定为此隔离级别,数据库可以防止脏读,但有不可重复度、幻读的问题。
- Repeatable read:如果将数据库设定为此隔离级别,数据库可以防止脏读、不可重复度,但是不能防止幻读。
- Serializable:将数据库串行化,可以避免脏读、不可重复读、幻读。
比较:
- 安全性来说:Serializable>Repeatable read>Read committed>Read uncommitted
- 效率来说:Serializable<Repeatable read<Read committed<Read uncommitted
- 通常来说,一般的应用都会选择Repeatable read或Read committed作为数据库隔离级别来使用。
- 真正使用数据的时候,根据自己使用数据库的需求,综合分析对安全性和对效率的要求,选择一个隔离级别使数据库运行在这个隔离级别上.
在MySQL中设置事务的隔离级别
MySQL
默认的数据库隔离级别为:REPEATABLE-READ
Oracle
默认下就是read committed
个隔离级别如何查询当前数据库的隔离级别?
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过
SELECT @@tx_isolation
;命令来查看,MySQL 8.0 该命令改为SELECT @@transaction_isolation;
mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。
如何设置当前数据库的隔离级别?
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
此种方式设置的隔离级别只对当前连接起作用。
set transaction isolation level read uncommitted;
set session transaction isolation level read uncommitted;
此种方式设置的隔离级别是设置数据库默认的隔离级别
set global transaction isolation level read uncommitted;
五、数据库中的锁机制:
- 共享锁:在非Serializable隔离级别做查询不加任何锁,而在Serializable隔离级别下做的查询加共享锁,
- 共享锁的特点:共享锁和共享锁可以共存,但是共享锁和排他锁不能共存
排他锁:在所有隔离级别下进行增删改的操作都会加排他锁,
- 排他锁的特点:和任意其他锁都不能共存
- 在非串行化下,所有的查询都不加锁,所有的修改操作都会加排他锁。
- 在串行化下,所有的查询都加共享锁,所有的修改都加排他锁。
六、更新丢失
- 如果多个线程操作,基于同一个查询结构对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。
- Serializable可以防止更新丢失问题的发生。其他的三个隔离级别都有可能发生更新丢失问题。
Serializable虽然可以防止更新丢失,但是效率太低,通常数据库不会用这个隔离级别,所以我们需要其他的机制来防止更新丢失:
两个线程基于同一个查询结果进行修改,后修改的人会将先修改人的修改覆盖掉.
乐观锁和悲观锁不是数据库中真正存在的锁,只是人们在解决更新丢失时的不同的解决方案,体现的是人们看待事务的态度。
悲观锁:
- 悲观锁悲观的认为每一次操作都会造成更新丢失问题,在每次查询时就加上排他锁
select * from xxx for update
- 隔离级别不设置为Serializable,防止效率过低。
- 在查询时手动加上排他锁。
- 如果数据库中的数据查询比较多而更新比较少的话,悲观锁将会导致效率低下。
乐观锁:
- 乐观锁会乐观的认为每次查询都不会造成更新丢失.利用一个版本字段进行控制
- 在表中增加一个version字段,在更新数据库记录是将version加一,从而在修改数据时通过检查版本号是否改变判断出当前更新基于的查询是否已经是过时的版本。
- 如果数据库中数据的修改比较多,更新失败的次数会比较多,程序需要多次重复执行更新操作。
查询非常多,修改非常少,使用乐观锁
修改非常多,查询非常少,使用悲观锁


七、并发事务所带来的的问题
上面都讲过了,这里总结一下:
脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
【DataBase】事务的更多相关文章
- web本地存储-WebSQL
Web SQL数据库API实际上未包含在HTML 5规范之中,它是一个独立的规范,它引入了一套使用SQL操作客户端数据库的API.W3C 官方在 2011 年 11 月声明已经不再维护 Web SQL ...
- MySQL8.0新特性——支持原子DDL语句
MySQL 8.0开始支持原子数据定义语言(DDL)语句.此功能称为原子DDL.原子DDL语句将与DDL操作关联的数据字典更新,存储引擎操作和二进制日志写入组合到单个原子事务中.即使服务器在操作期间暂 ...
- MySQL8新特性(1)--原子DDL
mysql 8支持原子ddl.一个原子DDL语句包含数据字典更新.存储引擎操作.二进制日志写,事务要么被提交,应用修改被持持久化到数据字典.存储引擎和二进制日志,或者被回滚. 原子ddl是随着mysq ...
- android项目笔记整理(2)
31.利用SharedPreferences存储时间 读取时间: SharedPreferences sp=this.getSharedPreferences("actm&q ...
- Redis - 读写模式 - 缓存一致性
Cache Aside Pattern(旁路缓存模式) 读:从cache中读取数据,若读取到则直接返回:cache中不存在则去database中读取,然后更新到cache. 写:先更新database ...
- 使用 Oracle GoldenGate 在 Microsoft SQL Server 和 Oracle Database 之间复制事务
使用 Oracle GoldenGate 在 Microsoft SQL Server 和 Oracle Database 之间复制事务 作者:Nikolay Manchev 分步构建一个跨这些平台的 ...
- Oracle Database Transaction Isolation Levels 事务隔离级别
Overview of Oracle Database Transaction Isolation Levels Oracle 数据库提供如下事务隔离级别: 已提交读隔离级别 可串行化隔离级别 只读隔 ...
- ABP:在多语句事务内不允许使用 CREATE DATABASE 语句
一.问题 使用ef codefirst开发,无法创建数据库的问题,如下提示 Server Error in '/' Application. 在多语句事务内不允许使用 CREATE DATABASE ...
- Oracle database link中查询会开启事务吗?
关于oracle database link,使用database link相关的查询语句是否会开启事务呢?我们知道,在数据库中一个简单的SELECT查询语句不会产生事务(select for upd ...
- 【数据库-Azure SQL Database】如何创建事务复制将本地数据同步到 SQL Azure
Azure SQL DB 可以被配置成为 SQL Server 事务复制的一个订阅者( subscriber ). 主要应用场景有两种: 将您的数据迁移到 Azure SQL DB, 并且没有宕机时间 ...
随机推荐
- Unity经典游戏教程之:雪人兄弟
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- 记一次使用LR测试UDP和TCP的过程
背景 最近项目要做性能测试,要出要一份性能报告,让我出一个有关Tcp和Udp的功能模块的测试,流程大概是这样,先走TCP协议协商一下会话,协商成功后走Udp收发数据. 有点简单啊,自己写个功能模块测一 ...
- SpringMVC学习笔记之---数据绑定
SpringMVC数据绑定 一.基础配置 (1)pom.xml <dependencies> <dependency> <groupId>junit</gro ...
- 【原创实践】U大师启动安装windows XP
1:使用U大师3.0版制作启动U盘,拷贝windows xp或者win7的原版安装iso(zh-hans_windows_xp_professional_with_service_pack_3_x86 ...
- python 之 前端开发(基本选择器、组合选择器、 交集与并集选择器、序列选择器、属性选择器、伪类选择器、伪元素选择器)
11.3 css 11.31 基本选择器 11.311 id选择器 根据指定的id名称,在当前界面中找到对应的唯一一个的标签,然后设置属性 <!DOCTYPE html> <html ...
- Hadoop - YARN Introduce
YARN Introduce 1. MapReduce1.0缺陷 (1)存在单点故障 (2)JobTracker"大包大揽"导致任务过重(任务多时内存开销大,上限4000节点) ( ...
- 爱奇艺JAVA后台面经
链接:https://www.nowcoder.com/discuss/217425 1.volatile关键字的含义 2.Java NIO 讲一下 2.1 NIO selector,epoll的区别 ...
- 使用Tesseract-OCR 做验证码识别浅析
使用工具jTessBoxEditor-0.7(这个是在java平台下开发的,所以 它只支持java平台 ,在使用前应该先配置好java环境) tesseract 程序集(因为该程序集是在.net 2. ...
- css布局之居中
CSS布局之居中 本文主要是介绍水平居中,垂直居中,还有水平垂直居中的方法 水平居中 1.行内元素水平居中 使用text-align:center;就可以实现行内元素的水平居中,但是记得要在父元素中设 ...
- ImageNet主要网络benchmark对比
深度神经网络繁多,各自的性能指标怎样? 实际应用中,在速度.内存.准确率等各种约束下,应该尝试哪些模型作为backbone? 有paper对各个网络模型进行了对比分析,形成了一个看待所有主要模型的完整 ...