国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

聊聊JDBC事務(wù)隔離級(jí)別(修正)

phpmatt / 1313人閱讀

摘要:在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持所有數(shù)據(jù)的完整性。隔離性一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)所影響。比如,事務(wù)在事務(wù)提交前讀到的結(jié)果,和提交后讀到的結(jié)果可能不同。

重要

由于之前代碼的不嚴(yán)謹(jǐn),導(dǎo)致結(jié)果和結(jié)論的錯(cuò)誤,深表歉意,現(xiàn)在對(duì)其進(jìn)行修正

摘要

事務(wù)在日常開發(fā)中是不可避免碰到的問題,JDBC中的事務(wù)隔離級(jí)別到底會(huì)如何影響事務(wù)的并發(fā),臟讀(dirty reads), 不可重復(fù)讀(non-repeatable reads),幻讀(phantom reads)到底是什么概念

事務(wù)

原子性(atomicity) 事務(wù)是數(shù)據(jù)庫的邏輯工作單位,而且是必須是原子工作單位,對(duì)于其數(shù)據(jù)修改,要么全部執(zhí)行,要么全部不執(zhí)行。

一致性(consistency) 事務(wù)在完成時(shí),必須是所有的數(shù)據(jù)都保持一致狀態(tài)。在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持所有數(shù)據(jù)的完整性。

隔離性(isolation) 一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)所影響。

持久性(durability) 一個(gè)事務(wù)一旦提交,事物的操作便永久性的保存在數(shù)據(jù)庫中,即使此時(shí)再執(zhí)行回滾操作也不能撤消所做的更改。

隔離性

以上是數(shù)據(jù)庫事務(wù)-ACID原則,在JDBC的事務(wù)編程中已經(jīng)為了我們解決了原子性,持久性的問題,唯一可配置的選項(xiàng)是事務(wù)隔離級(jí)別,根據(jù)com.mysql.jdbc.Connection的定義有5個(gè)級(jí)別:

TRANSACTION_NONE(不支持事務(wù))

TRANSACTION_READ_UNCOMMITTED

TRANSACTION_READ_COMMITTED

TRANSACTION_REPEATABLE_READ

TRANSACTION_SERIALIZABLE

讀不提交(TRANSACTION_READ_UNCOMMITTED)

不能避免dirty reads,non-repeatable reads,phantom reads

讀提交(TRANSACTION_READ_COMMITTED)

可以避免dirty reads,但是不能避免non-repeatable reads,phantom reads

重復(fù)讀(TRANSACTION_REPEATABLE_READ)

可以避免dirty reads,non-repeatable reads,但不能避免phantom reads

序列化(TRANSACTION_SERIALIZABLE)

可以避免dirty reads,non-repeatable reads,phantom reads

創(chuàng)建一個(gè)簡單的表來測試一下隔離性對(duì)事務(wù)的影響

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `balance` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
臟讀(dirty reads)

事務(wù)A修改了一個(gè)數(shù)據(jù),但未提交,事務(wù)B讀到了事務(wù)A未提交的更新結(jié)果,如果事務(wù)A提交失敗,事務(wù)B讀到的就是臟數(shù)據(jù)。

TEST:
事務(wù)A: update account += 1000, 然后回滾
事務(wù)B: 嘗試讀取 account 的值
期望結(jié)果:
當(dāng)設(shè)置隔離級(jí)別為TRANSACTION_READ_UNCOMMITTED時(shí),事務(wù)B讀取到的值不一致
當(dāng)設(shè)置隔離級(jí)別大于TRANSACTION_READ_UNCOMMITTED時(shí),事務(wù)B讀取到的值一致

先創(chuàng)建一個(gè)read任務(wù)

class ReadTask implements Runnable {
    int level = 0;
    
    public ReadTask(int level) {
        super();
        this.level = level;
    }

    @Override
    public void run() {
        Db.tx(level, new IAtom() {
            @Override
            public boolean run() throws SQLException {
                AccountService service = new AccountService();
                System.out.println(Thread.currentThread().getId() + ":" + service.audit());
                return true;
            }
        });
    }
}

其中AccountService代碼(提供了讀和寫balance的方法)

public class AccountService {
    
    // 貌似這個(gè)方法有執(zhí)行了行鎖
    public void deposit(int num) throws Exception {
        int index = Db.update("update account set balance = balance + " + num + " where user_id = 1");
        if(index != 1)
            throw new Exception("Oop! deposit fail.");
    }
    
    public int audit() {
        return Db.findFirst("select balance from account where user_id = 1").getInt("balance");
    }
}

PS: 上述代碼所使用的框架為JFinal(非常優(yōu)秀的國產(chǎn)開源框架)

對(duì)于Db.findFirst和Db.update這2個(gè)方法就是對(duì)JDBC操作的一個(gè)簡單的封裝

然后再創(chuàng)建一個(gè)writer任務(wù)

class WriterTask implements Runnable {
    int level = 0;
    
    public WriterTask(int level) {
        super();
        this.level = level;
    }
    
    @Override
    public void run() {
        Db.tx(level, new IAtom() {
            @Override
            public boolean run() throws SQLException {
                AccountService service = new AccountService();
                try {
                    service.deposit(1000);
                    System.out.println("Writer 1000.");
                    Thread.sleep(1000);
                    System.out.println("Writer complete.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return false;
            }
        });
    }
}

然后執(zhí)行主線程

public static void main(String[] args) throws Exception {
    int level = Connection.TRANSACTION_READ_UNCOMMITTED;
    for(int j = 0; j < 10; j++) {
        if(j == 3) new Thread(new WriterTask(level)).start();
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new ReadTask(level)).start();
    }    
}

上訴代碼開啟ReadTask和WriterTask對(duì)balance的值進(jìn)行并發(fā)的寫入和讀取,并且WriterTask最終會(huì)回滾事務(wù)

當(dāng)隔離級(jí)別為TRANSACTION_READ_UNCOMMITTED時(shí),發(fā)現(xiàn)在WriterTask-commit事務(wù)前后讀取到的值不一樣

13:0
14:0
15:0
Writer 1000.
17:1000
18:1000
19:1000
Writer complete.
20:0
21:0
22:0
23:0

然后修改代碼的隔離級(jí)別為TRANSACTION_READ_COMMITTED,發(fā)現(xiàn)前后讀取的值一致,但是值得注意的是的,數(shù)據(jù)一致是建立在WriterTask事務(wù)回滾的情況下,如果事務(wù)正確的提交了,還是有出現(xiàn)數(shù)據(jù)不一致的問題,關(guān)于數(shù)據(jù)的一致性就不能簡單的使用事務(wù)隔離來解決了,需要lock,關(guān)于數(shù)據(jù)一致的問題不在本文章討論內(nèi)

13:0
14:0
15:0
Writer 1000.
17:0
18:0
19:0
Writer complete.
20:0
21:0
22:0
23:0
不可重復(fù)讀(non-repeatable reads)

在同一個(gè)事務(wù)中,對(duì)于同一份數(shù)據(jù)讀取到的結(jié)果不一致。比如,事務(wù)B在事務(wù)A提交前讀到的結(jié)果,和提交后讀到的結(jié)果可能不同。

TEST:
事務(wù)A: update account += 1000, 然后commit
事務(wù)B: 嘗試讀取 account 的值(間隔2秒),再次嘗試讀取

為了滿足不可重復(fù)讀的測試對(duì)ReadTask作一些小改動(dòng)

class ReadTask2 implements Runnable {
    int level = 0;
    
    public ReadTask2(int level) {
        super();
        this.level = level;
    }

    @Override
    public void run() {
        Db.tx(level, new IAtom() {
            @Override
            public boolean run() throws SQLException {
                AccountService service = new AccountService();
                System.out.println(Thread.currentThread().getId() + ":" + service.audit());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getId() + ":" + service.audit());
                return true;
            }
        });
    }
}

在代碼中間隔2s,然后重復(fù)訪問同一個(gè)balance字段

主線程代碼

public static void main(String[] args) throws Exception {
    int level = Connection.TRANSACTION_REPEATABLE_READ;
    new Thread(new ReadTask2(level)).start();
    Thread.sleep(1500);
    new Thread(new WriterTask2(level)).start();
    Thread.sleep(1500);
}

設(shè)置隔離界別為TRANSACTION_READ_UNCOMMITTED

10:17000
Writer 1000.
10:18000

設(shè)置隔離界別為TRANSACTION_REPEATABLE_READ

10:18000
Writer 1000.
10:18000

讀取到的1800是WriterTask事務(wù)未提交之前的值,假如要實(shí)時(shí)的獲取balance的最新值,WriterTask很顯然還是需要加lock,所以無可重復(fù)讀的隔離級(jí)別只是避免了在同一個(gè)事務(wù)中數(shù)據(jù)讀取的一致性,而不保證最終的數(shù)據(jù)一致性

幻讀(phantom reads)

在同一個(gè)事務(wù)中,同一個(gè)查詢多次返回的結(jié)果不一致。

ReadTask和WriterTask分別進(jìn)行insert的sql與select的操作(select count(*) from account)

TEST:
事務(wù)A: insert account 然后commit
事務(wù)B: 嘗試讀取 account 的數(shù)量(間隔2秒),再次嘗試讀取

設(shè)置隔離界別為TRANSACTION_READ_COMMITTED

12:0
create account.
12:1

設(shè)置隔離界別為TRANSACTION_REPEATABLE_READ

12:1
create account.
12:1

設(shè)置隔離界別為TRANSACTION_SERIALIZABLE

12:2
create account.
12:2

關(guān)于最高級(jí)別序列化是只有當(dāng)一個(gè)事務(wù)完成后才會(huì)執(zhí)行下一個(gè)事務(wù),但是這里我測試使用TRANSACTION_REPEATABLE_READ級(jí)別是還是避免了幻讀,不知道是程序的問題還是JDBC的問題,這里我可能還需要進(jìn)一步的測試和研究,但是根據(jù)官方對(duì)TRANSACTION_REPEATABLE_READ的說明

A constant indicating that dirty reads, non-repeatable reads and phantom reads are prevented. This level includes the prohibitions in TRANSACTION_REPEATABLE_READ and further prohibits the situation where one transaction reads all rows that satisfy a WHERE condition, a second transaction inserts a row that satisfies that WHERE condition, and the first transaction rereads for the same condition, retrieving the additional "phantom" row in the second read.

表示幻讀的定義是在同一個(gè)事務(wù)中,讀取2次的值是不一樣的,因?yàn)橛衅渌聞?wù)添加了一行,并且這行數(shù)據(jù)是滿足第一個(gè)事務(wù)的where查詢條件的數(shù)據(jù)

總結(jié)

本次測試使用JFinal框架(它對(duì)JDBC進(jìn)行了很簡易的封裝),使用不同的隔離級(jí)別對(duì)3種并發(fā)情況進(jìn)行測試,但是在幻讀的測試中TRANSACTION_REPEATABLE_READ級(jí)別同樣也避免了幻讀的情況,這個(gè)有待進(jìn)一步測試和研究

補(bǔ)充說明

同一個(gè)事務(wù): 在JDBC編程中同一個(gè)事務(wù)意味著擁有相同的Connection,也就是說如果想保證事務(wù)的原子性所有的執(zhí)行必須使用同一個(gè)Connection,事務(wù)的代表就是Connection

commit和rollback:在JDBC編程中一旦代碼commit成功就無法rollback,所以一般rollback是發(fā)生在commit出現(xiàn)異常的情況下

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/65096.html

相關(guān)文章

  • Java知識(shí)點(diǎn)總結(jié)(JDBC-事務(wù)

    摘要:隔離級(jí)別個(gè)等級(jí)的事務(wù)隔離級(jí)別,在相同的數(shù)據(jù)環(huán)境下,使用相同的輸入,執(zhí)行相同的工作,根據(jù)不同的隔離級(jí)別,可以導(dǎo)致不同的結(jié)果。不同事務(wù)隔離級(jí)別能夠解決的數(shù)據(jù)并發(fā)問題的能力是不同的。 Java知識(shí)點(diǎn)總結(jié)(JDBC-事務(wù)) @(Java知識(shí)點(diǎn)總結(jié))[Java, JDBC] 事務(wù) 事務(wù)基本概念 一組要么同時(shí)執(zhí)行成功,要么同時(shí)執(zhí)行失敗的 SQL 語句。是數(shù)據(jù)庫操作的一個(gè)執(zhí)行單元! 事務(wù)開始于:...

    Zachary 評(píng)論0 收藏0
  • JavaWEB開發(fā)13——事務(wù)與連接池

    摘要:一致性一個(gè)事務(wù)中,事務(wù)前后數(shù)據(jù)的完整性必須保持一致。持久性持久性是指一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對(duì)其有任何影響。 一、事務(wù)概述1.什么是事務(wù)一件事情有n個(gè)組成單元 要不這n個(gè)組成單元同時(shí)成功 要不n個(gè)單元就同時(shí)失敗就是將n個(gè)組成單元放到一個(gè)事務(wù)中2.mysql的事務(wù)默認(rèn)的事務(wù):一條sql語句就是一個(gè)事務(wù) 默認(rèn)就開啟事務(wù)并提交事...

    13651657101 評(píng)論0 收藏0
  • Spring事務(wù)整理

    摘要:使用需要使用作為事務(wù)管理器。兩個(gè)事務(wù)互不影響。這是默認(rèn)的隔離級(jí)別,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級(jí)別下邊的四個(gè)與的隔離級(jí)別相對(duì)應(yīng)這是事務(wù)最低的隔離級(jí)別,它充許另外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)。這種事務(wù)隔離級(jí)別可 Spring事務(wù)整理 工作了幾年了,今天抽時(shí)間整理一下spring的事務(wù),說起spring的事務(wù)是面試的時(shí)候面試官經(jīng)常提及的問題,接下來結(jié)合網(wǎng)上資料再總結(jié)下spring的事務(wù)...

    stackvoid 評(píng)論0 收藏0
  • JDBC常見面試題

    摘要:常見面試題操作數(shù)據(jù)庫的步驟操作數(shù)據(jù)庫的步驟注冊數(shù)據(jù)庫驅(qū)動(dòng)??梢苑乐棺⑷?,安全性高于。只有隔離級(jí)別才能防止產(chǎn)生幻讀。對(duì)象維護(hù)了一個(gè)游標(biāo),指向當(dāng)前的數(shù)據(jù)行。一共有三種對(duì)象。 以下我是歸納的JDBC知識(shí)點(diǎn)圖: showImg(https://segmentfault.com/img/remote/1460000013312769); 圖上的知識(shí)點(diǎn)都可以在我其他的文章內(nèi)找到相應(yīng)內(nèi)容。 JDBC...

    Yuqi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<