数据库事务

事务

一个事务是由一个逻辑单元进行的一系列操作。一个事务的逻辑单元的操作必须满足4个属性,(ACID)原子性,一致性,独立性,持久性。

原子性(Atomictity)

一个事务必须是原子单元操作,对数据的修改必须全部执行或者全部不执行

一致性(Consistency)

事务执行结束的时候,所有的数据必须处于一致的状态。在关系型数据库中,所有的规则必须应用于事务的修改来保证数据完整性。在事务结束的时候,所有内部的数据结构,比如B树或者双边列表必须是正确的。

独立性(Isolation)

多并发下的事务修改必须保持独立性。一个事务获得的数据必须在其他事物修改之前或者完成修改以后,不应该获取任何处于中间状态的数据。这可以被理解为"串行化",因为如果我们使用相同的初始数据并且重新执行相同的一系列事务,我们最后获得的结果应该是一致的。

持久性(Durability)

当事务完成以后,在系统中的改变应该是永久的。甚至在系统宕机的情况下事务所完成的修改应该也是有效的。

说明和执行事务 Specifying and Enforcing Transactions

SQL程序员负责在开始和结束事务的时候保证数据的逻辑一致性。程序员必须根据业务要求定义一个数据修改的序列,来确保数据处于一致的状态。程序员在单个事务中执行数据修改语句,这样数据库才能保证事务执行的物理完整性。

数据库系统负责提供每个事务完整性的机制。数据库引擎提供:

  • 锁机制保证事务的独立性
  • 日志机制保证事务持久性。即使服务器硬件,操作系统或者数据库引擎本身宕机,数据库引擎可以在重启时使用事务日志自动将未完成事务回滚至系统故障点。
  • 执行事务时确保一致性和原子性事务管理功能。事务开始后必须成功完成,或者数据库引擎撤销事务开始后所有对数据修改的操作。

数据读问题

在一个事务读取另外一个事务可能修改的数据的时候可能会出现某些"读现象"

比如我们有以下user表

id name age
1 Joe 20
2 Jill 25

脏读

当一个事务允许读另外一个事务修改但是未提交的数据时,可能发生脏读。

脏读和不可重复读(non-repeatable reads)类似。事务2没有提交造成事务1的语句两次执行得到不同的结果集。

然后我们有事务1和2 以下面顺序执行。事务2修改了一行,但是没有提交,事务1读了这个没有提交的数据。现在如果事务2回滚了刚才的修改或者做了另外的修改的话,事务1中查到的数据就是不正确的了。

事务1 事务2
SELECT age FROM users WHERE id = 1;(20)
UPDATE users SET age = 21 WHERE id = 1;(没commit)
SELECT age FROM users WHERE id = 1; (21)
ROLLBACK;

不可重复读

在一次事务中,当一行数据获取两遍得到不同的结果表示发生了“不可重复读”。

基于锁的并发控制中,"不可重复读"现象发生在执行SELECT操作时没有获得读锁或者SELECT操作执行完后马上释放了读锁;当没有要求一个提交冲突的事务回滚也会发生不可重复读。

事务1 事务2
SELECT age FROM users WHERE id = 1;20)
UPDATE users SET age = 21 WHERE id = 1;
COMMIT;
SELECT age FROM users WHERE id = 1; (21)

在可序列化和可重复读的隔离级别,数据库在第二次SELECT请求的时候返回事务2更新前的值(20)。但是在提交读和未提交读级别时,返回的是21,这个现象就是不可重复读。

幻读

在事务执行过程中,当两个完全相同的查询语句执行得到不同的结果集。这种现象称为“幻读(phantom read)”。

事务没有获取范围锁的情况下执行SELECT...WHERE可能会发生幻读。幻读是不可重复读的特殊场景:当事务1两次执行SELECT...WHERE检索一定范围内数据的操作时,事务2 在表中更改了一行数据(比如INSERT),这条数据正好满足事务1的WHERE子句。

事务1 事务2
SELECT * FROM users WHERE age
BETWEEN 10 AND 30;
INSERT INTO users VALUES ( 3, 'Bob', 27 );
COMMIT;
SELECT * FROM users WHERE age
BETWEEN 10 AND 30;

事务1执行了两次相同的查询,如果设置了最高隔离级别,两次会得到相同的结果集。在较低的隔离级别上,第二次查询可能会得到不同的结果集。在可序列化隔离级别,事务1在age10到30的记录上加锁,事务2只能阻塞直到事务1提交。在可重复读级别,这个范围不会被锁定,允许记录插入。

隔离级别,读现象和锁

隔离级别 vs 锁现象

隔离级别 脏读 不可重复读 幻读
未提交读 可能发生 可能发生 可能发生
提交读 可能发生 可能发生
可重复读 可能发生
可序列化

隔离级别 vs 锁持续时间

基于锁的并发控制中,隔离级别决定锁的持有时间。"C"表示锁持续到事务提交,"S"表示持续到当前语句执行完毕。如果锁在语句执行完毕就释放则另外一个事务就可以在这个事务提交前修改锁定的数据,导致以上的读现象出现。

隔离级别 写操作 读操作 范围操作(Select...Where)
未提交读 S S S
提交读 C S S
可重复读 C C S
可序列化 C C C

JDBC设置以及 AutoCommit

在JDBC中,我们可以在Connection通过setTransactionIsolation设置事务隔离级别, 这个只对当前connection有效。

        Connection conn = null;
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            conn = DriverManager.getConnection("jdbc:xxxxxx");
            
            Statement stmt = conn.createStatement();
/*          conn.setTransactionIsolation(Connection.TRANSACTION_NONE);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);*/
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
            
            conn.setAutoCommit(false);
            stmt.executeQuery("select * from user where id = 1");
            stmt.execute("update user set age = 12 where id = 1");
            stmt.executeQuery("select * from user where id = 1");
            stmt.execute("update user set age = 15 where id = 2");
            conn.commit();
            
        } catch (SQLException | ClassNotFoundException e) {
            try {
                if(conn != null)
                    conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }

我们如果要设置事务隔离级别,首先需要把autoCommit设置为false,这样我们才可以把多条SQL Statement归并到一个事务中。当autoCommit设置为true的时候,每一条sql语句都会被认为是一条事务并且在完成时自动COMMIT。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容