【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, 并且没有宕机时间 ... 
随机推荐
- java中线程安全,线程死锁,线程通信快速入门
			一:多线程安全问题 ###1 引入 /* * 多线程并发访问同一个数据资源 * 3个线程,对一个票资源,出售 */ public class ThreadDemo { public static vo ... 
- 基于tp3.2的腾讯云短信验证码的实现
			新手小白在公司要完成短信验证码注册功能,最初由于没有经验,网上的教程又不是很全,便参考着官方API文档,进行开发 直接进入正题:使用composer下载腾讯云短信接口(记得添加依赖).在项目目录下新建 ... 
- WebService—— IDEA创建WebServices
			一.File–>New–>Project 弹出这个对话框后,照下图的勾选然后点击Next,然后填写项目名和项目路径后,点击finish. 二.生成目录如下 需要注意的有HelloWorld ... 
- LeetCode——409. Longest Palindrome
			题目: Given a string which consists of lowercase or uppercase letters, find the length of the longest ... 
- Ubuntu : apt-get 命令
			apt-get 命令是 Ubuntu 系统中的包管理工具,可以用来安装.卸载包,也可以用来升级包,还可以用来把系统升级到新的版本.本文介绍 apt-get 命令的基本用法,演示环境为 Ubuntu 1 ... 
- CodeForces 1129C Morse Code
			洛谷题目页面传送门 & CodeForces题目页面传送门 题意见洛谷里的翻译. 首先我们可以用区间DP算出对于每个子01串,能表示的字母串的个数. 设\(dp_{i,j}\)表示长度为\(i ... 
- CMake入门-02-HelloWorld扩展
			工作环境 系统:macOS Mojave 10.14.6 CMake: Version 3.15.0-rc4 Hello,World! 扩展-同一目录,多个源文件 (1) 新建 hello 目录,创建 ... 
- .net软件开发脚本规范-SVN标准
			一. SVN标准 1) 提交代码前先获取最新代码 2) 提交时需要填写信息,填写任务Excel中的修改内容列,如以下任务填写“业绩考核-工作量管理”,如果发生修改再次提交,在其后加上修改原因,例“业绩 ... 
- 5G标准公布,你很快又要换手机了
			通常,在4G网络环境下,下载一部1G的电影只需要30秒时间,对于经历过2G和3G网络的我们来说已经非常快了. 但是听说,5G环境中下载一部同样的电影,根本不是用秒来计算的,甚至有外媒说,5G的速率会是 ... 
- 阿里注册中心Nacos生产部署方案
			一.说明 生产环境中部署nacos首先肯定是使用集群模式cluster保证高可用,本文主要详细介绍最佳的集群方案怎样搭建与spring cloud程序怎样集成 二.集群方案 下图是官方推荐的集群方 ... 
