對數據庫表數據一致性的控制方法

讀數據分爲快照讀和當前讀。java

快照讀:(不加鎖)讀到的未必最新的,普通色select語句mysql

當前讀:(加鎖) 讀到的都是最新的。有:程序員

一、select * from table ? lock in share mode;sql

二、select * from table where ? for update; (關於for update用法具體下面說)數據庫

三、insert, update, delete併發

第3類update數據的操做步驟是:mysql server根據查詢條件先讀取知足條件的第一條記錄,並加鎖,返回mysql server,mysql server收到以後會執行update操做,一條完成,再下一條。delete同理,Insert操做會稍微有些不一樣,簡單來講,就是Insert操做可能會觸發Unique Key的衝突檢查,也會進行一個當前讀。性能

mysql寫數據的時候都會進行當前讀。spa

關於每條sql如何執行的時候如何加鎖參照:http://hedengcheng.com/?p=771server

 

數據庫事物的控制,數據庫事物分四個級別從低到高分別索引

1:read uncommitted

讀未提交,髒讀,這個在應用中基本不用。

2:read committed

讀提交,會產生重複讀

事例:程序員拿着信用卡去享受生活(卡里固然是隻有3.6萬),當他埋單時(程序員事務開啓),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢所有轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額固然要等待妻子轉出金額事務提交完)。程序員就會很鬱悶,明明卡里是有錢的…

分析:這就是讀提交,如有事務對數據進行更新(UPDATE)操做時,讀操做事務要等待這個更新操做事務提交後才能讀取數據,能夠解決髒讀問題。但在這個事例中,出現了一個事務範圍內兩個相同的查詢卻返回了不一樣數據,這就是不可重複讀。

3:repeatable read

重複讀,就是在開始讀取數據(事務開啓)時,再也不容許修改操做。

可是會存在幻讀,由於幻讀是insert產生的

事例:程序員某一天去消費,花了2千元,而後他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啓),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,彷佛出現了幻覺,這就是幻讀。

四、serializable序列化

Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,能夠避免髒讀、不可重複讀與幻讀。可是這種事務隔離級別效率低下,比較耗數據庫性能,通常不使用。

 

咱們寫service的時候常常遇到先查詢再修改的操做。好比 用手機號註冊一個新用戶,首先得用手機號查詢用戶表是否存在,若是不存在則插入一條,存在就給他登陸。  問題來了,當手機號相同的兩個請求併發訪問的時候,結果是否是在用戶出現了兩個手機號相同的記錄(由於同時查都發現沒有這個手機號),但咱們要保證手機號是惟一的。(先不說數據表給手機號創建惟一約束)

一開始我使用事物的第三個等級也就是repeatable read,發現不行,爲何呢,細想一下,repeatable read只是防止一個事物開啓的時候別的事物不被修改,可是仍是能夠查呀,呵呵。

嗯,不能被修改,for update就出現了,for update會產生鎖,具體產生什麼鎖得看查詢條件,一個原則,對明確查詢結果的結果加行鎖,查詢結果若是整個表數據都有可能,就表鎖。

好比:根據主鍵id查詢,確定是一條,行鎖。

          根據name查詢(無索引),表鎖,由於都有可能

          根據name查詢(有索引),行鎖

舉個例子:
假設有個表單products ,裏面有id跟name二個欄位,id是主鍵索引。
例1: (明確指定主鍵索引,而且有此筆資料,row lock)

SELECT * FROM products WHERE id='3' FOR UPDATE;
例2: (明確指定主鍵索引,若查無此筆資料,無lock)

SELECT * FROM products WHERE id='-1' FOR UPDATE;
例2: (無主鍵索引,table lock)

SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
例3: (主鍵索引不明確,table lock)

SELECT * FROM products WHERE id<>'3' FOR UPDATE;
例4: (主鍵索引不明確,table lock)

SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;

鎖,就針對寫數據庫的操做加鎖,若是select for update固然也鎖了。

for update就有效的解決了上述的問題,注意,事物裏面不要寫時間不可控的邏輯,好比調一個第三方的接口等待返回,這樣事物的時間會變成,而表一直被鎖了。具體會出現什麼問題沒遇到。。。

 

還有一個方法就是經過程序來控制,java裏有個關鍵字synchronized,同步鎖,能解決問題,就是效率很低,

for (int k = 0; k < 10; k++) {
    long start = System.currentTimeMillis();
    for (int j = 0; j < 100000000; j++) {
        synchronized (lock) {
            int i = 1;
        }
    }
    System.out.println(System.currentTimeMillis() - start);
    start = System.currentTimeMillis();
    for (int j = 0; j < 100000000; j++) {
        int i = 1;
    }
    System.out.println(System.currentTimeMillis() - start);
}

 

2366 30 2407 33 2410 30 2371 31 2380 31 2371 30 2388 32 2402 33 2396 30 2385 33

相關文章
相關標籤/搜索