MySQL事務

問題的提出 數據庫是要被廣大客戶所共享訪問的,那麼在數據庫操作過程中很可能出現以下幾種不確定情況。 更新丟失 兩個事務都同時更新一行數據,一個事務對數據的更新把另一個事務對數據的更新覆蓋了。這是因爲系統沒有執行任何的鎖操作,因此併發事務並沒有被隔離開來。 髒讀 一個事務讀取到了另一個事務未提交的數據操作結果。這是相當危險的,因爲很可能所有的操作都被回滾。 不可重複讀 不可重複讀(Non-repeatable Reads):一個事務對同一行數據重複讀取兩次,但是卻得到了不同的結果。 包括以下情況: (1) 虛讀:事務T1讀取某一數據後,事務T2對其做了修改,當事務T1再次讀該數據時得到與前一次不同的值。 (2) 幻讀(Phantom Reads):事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺少了第一次查詢中出現的數據(這裏並不要求兩次查詢的SQL語句相同)。這是因爲在兩次查詢過程中有另外一個事務插入數據造成的。 解決方案 爲了避免上面出現的幾種情況,在標準SQL規範中,定義了4個事務隔離級別,不同的隔離級別對事務的處理不同。 未授權讀取 也稱爲讀未提交(Read Uncommitted):允許髒讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。 授權讀取 也稱爲讀提交(Read Committed):允許不可重複讀取,但不允許髒讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。 可重複讀取(Repeatable Read) 可重複讀取(Repeatable Read):禁止不可重複讀取和髒讀取,但是有時可能出現幻讀數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。 序列化(Serializable) 序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,不能併發執行。僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。 隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設爲Read Committed。它能夠避免髒讀取,而且具有較好的併發性能。儘管它會導致不可重複讀、幻讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,可以由應用程序採用悲觀鎖或樂觀鎖來控制。

 

事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。

如果這一組的業務都能成功處理,我們就可以把這個事務提交來保存你已做的行爲結果。但如果一組中有任何的差錯出現的話,我們就認爲這事務不成功,需要回滾來撤消之前的操作。

 

事務的四大特性(ACID):

 

1.原子性(Atomicity) 
原子性是指事務是一個不可分割的工作單位,事務中的操作要麼全部成功,要麼全部失敗。比如在同一個事務中的SQL語句,要麼全部執行成功,要麼全部執行失敗。

 

2.一致性(Consistency) 
官網上事務一致性的概念是:事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。還要一種說法是事務前後數據的完整性必須保持一致。以轉賬爲例子,A向B轉賬,假設轉賬之前這兩個用戶的錢加起來總共是2000,那麼A向B轉賬之後,不管這兩個賬戶怎麼轉,A用戶的錢和B用戶的錢加起來的總額還是2000,這個就是事務的一致性。

 

3.隔離性(Isolation) 
事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每一個用戶開啓的事務,不能被其他事務的操作數據所幹擾,多個併發事務之間要相互隔離。

 

4.持久性(Durability) 
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。

 

事務隔離性問題:

 

如果不考慮事務的隔離性,會出現以下問題:

  • 髒讀:指一個事務讀取到了另外一個事務中未提交的數據。
  • 不可重複讀:指一個事務讀取到了另外一個事務提交的數據。
  • 幻讀:指一個事務讀取到了另外一個事務修改表後的數據。

髒讀:

b欠a 100元,所以b向a轉賬100元

update from account set money = money +100where name = ‘a’

update from account set money = money - 100where name = ‘b’

 

事務A爲b向a轉賬100元,事務B爲a查詢自己賬戶餘額

如果事務不隔離,事務A正在操作,那麼在執行第一條SQL語句後,a的賬戶多了100塊,這時事務B開始查詢操作,a去查詢自己的賬戶,那麼會查到自己的賬戶多了100塊,認爲b已經還了100塊。但如果事務A回滾,一切操作都沒有進行,那麼b並沒有向a轉賬100元,a卻認爲已經收到了b的轉賬,造成a損失100元。

 

不可重複讀:

不可重複讀,是指在數據庫訪問中,一個事務範圍內兩個相同的查詢卻返回了不同數據。這是由於查詢時系統中其他事務修改的提交而引起的。比如事務A對於a用戶的餘額進行了兩次查詢,第一次查詢結果爲100塊,在執行第二次查詢之前,事務B修改了a用戶的餘額,那麼事務A第二次查詢時,便查詢到了與第一次查詢不同的值。

 

幻讀:

事務A爲查詢表中工資大於5000的人,事務A會進行兩次查詢。第一次查詢到共有10人工資大於5000,在進行第二次查詢之前,事務B修改了表中數據,增加或者刪除了一條記錄,那麼事務A在進行第二次查詢時,查詢到的人數會多或者少,就跟幻覺一樣。

使用pymysql的commit即可達到事務的效果,增刪改操作,在沒有執行commit語句之前都不會寫入數據庫。

import pymysql

config = {
        'host': 'localhost',
        'port': 3306,
        'user': 'username',
        'password': 'password',
        'db': 'test',
        'charset': 'utf8',
        'cursorclass': pymysql.cursors.DictCursor
    }
conn = pymysql.connect(**config)
cursor = conn.cursor()
try:
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')
    conn.commit()  # 提交修改,成功插入一條數據
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')  # 該次修改未提交
    raise Exception
    cursor.execute('INSERT INTO test(msg,times) VALUES ("eq", 22)')  # 報錯跳出try語句
    conn.commit
except:
    print('1')
finally:
    cursor.close()
    conn.close()

如果一系列數據庫的操作需要保證同步,那麼將commit語句放在所有需要更新數據庫的語句的後面,就能保證數據一致性。只要有一條語句執行失敗,該次操作就不會commit

 

題外話:

事務是解決一個線程需要執行多個操作的問題,也就是多個SQL語句,保證所有SQL執行成功才保存執行結果。

但無法解決多線程情況下,多線程同時CURD同一數據的問題,這時需要用到鎖的機制。

https://www.cnblogs.com/deliver/p/5730616.html#4047518

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章