關於數據庫事務併發的理解和處理

關於數據庫事務併發的理解和處理

  • 併發的概念:在操做系統中,併發是指一個時間段中有幾個程序都處於已啓動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任一個時刻點上只有一個程序在處理機上運行。php

    在關係數據庫中,容許多個用戶同時訪問和更改共享數據的進程。mysql

  • 理解事務的概念程序員

    1. 概念:sql

      MySQL 事務主要用於處理操做量大,複雜度高的數據, 好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!數據庫

    2. 事務的四個重要特徵apache

      • 原子性windows

        一個事務(transaction)中的全部操做,要麼所有完成,要麼所有失敗,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。也就是說事務執行成功所有會應用到數據庫,失敗的話對數據庫沒用任何影響。安全

      • 一致性bash

        在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。好比說A帳戶有1000塊,B帳戶有100塊,A轉帳500給B,那麼A變成500,B變成600,也就是說二者之間不管如何轉帳都好,在事務結束以後加起來都是1100,這就是一致性。併發

      • 隔離性 : (這一點很重要,直接相關事務併發)

        數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。事務隔離分爲不一樣級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。

      • 持久性

        事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失。

    3. 重點講解事務的隔離性

      隔離性是如何防止多個事務併發執行時因爲交叉執行而致使數據的不一致的安全問題的呢?答案是經過設置隔離級別來解決的。

      • mysql四種隔離級別
      隔離級別 髒讀 不可重複讀 幻讀
      讀未提交(read-uncommitted)
      不可重複讀(read-committed) ×
      可重複讀(repeatable-read) × ×
      串行化(serializable) × × ×
    • 其中須要注意的是:

      • 可重複讀的隔離級別下使用了MVCC機制,select操做不會更新版本號,是快照讀(歷史版本);insert、update和delete會更新版本號,是當前讀(當前版本)。因此這也會帶來「髒寫」問題

      • mysql中事務隔離級別爲serializable時會鎖表,所以不會出現幻讀的狀況,這種隔離級別併發性極低,開發中不多會用到。

    • 在mysql中查看當前隔離事務級別語句:select @@tx_isolation;

    • 在mysql中設置事務的隔離級別語句: set tx_isolation='隔離級別名稱';

      那麼既然能夠經過設置隔離級別爲串行來解決併發時帶來的數據不一致問題,那爲何不直接把數據庫隔離級別改成serializable就好?的確,這樣的確能夠解決數據在多事務併發處理下數據不一致問題,可是每每帶來的是更大的性能開銷,這些性能的開銷每每是大於結合事務隔離級別和其它併發機制來處理事務的併發的開銷的。正確性和效率不可兼得,不少小公司在涉及到錢的業務代碼上都會啓用事務把,可是實際上是隻有小數量的公司纔會趕上高併發的狀況,多數都只是處理好併發就行了,那麼問題來了,若是隻是處理很小量的併發,並且是時有時沒有的時候,那就應該把級別改成串行?並非的,改了是整個數據庫受到影響的,包括全部的表,因此其餘的查詢和更改都會上鎖了,效率是極其低下的。

  • 數據庫併發控制解決方案

    鎖的分類

    • 從數據庫系統角度分爲三種:排他鎖(x)、共享鎖(s)、更新鎖(u)。

    • 從程序員角度分爲兩種:一種是悲觀鎖,一種樂觀鎖。

    這裏只討論悲觀鎖樂觀鎖

    • 悲觀鎖:

      須要數據庫自己的支持,經過SQL語句「select for update」鎖住select出的那批數據,老是假設最壞的狀況,每次取數據時都認爲其餘線程會修改,當其餘線程想要訪問數據時,都須要阻塞掛起。悲觀併發控制其實是「先取鎖再訪問」的保守策略,爲數據處理的安全提供了保證。可是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增長產生死鎖的機會;另外,在只讀型事務處理中因爲不會產生衝突,也不必使用鎖,這樣作只能增長系統負載;還有會下降了並行性,一個事務若是鎖定了某行數據,其餘事務就必須等待該事務處理完才能夠處理那行數據。

    • 樂觀鎖

      樂觀鎖,雖然名字中帶「鎖」,可是樂觀鎖並不鎖住任何東西,而是在提交事務時檢查這條記錄是否被其餘事務進行了修改:若是沒有,則提交;不然,進行回滾。相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。若是併發的可能性並不大,那麼樂觀鎖定策略帶來的性能消耗是很是小的。樂觀鎖採用的實現方式通常是記錄數據版本。

      數據版本是爲數據增長的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次同時對版本標識進行更新。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,若是數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,不然認爲是過時數據。通常地,實現數據版本有兩種方式,一種是使用版本號,另外一種是使用時間戳

  • 併發測試工具ab

    這是apache自帶的壓測工具,也能夠拿來作併發測試

    使用方法 進入ab工具默認安裝目錄(注意這是windows環境下)

    V~J7722)6O9BS}I%)K~}1VG.png

執行命令 ./ab.exe -n 1000 -c 1000 http://localhost/tp5/public/

0_}27`@@E9Y1WMH8}62ERE8.png

下面是測試代碼,用的是tp5

<?php
namespace app\index\controller;

use think\Controller;
use think\Db;

class Index extends Controller
{
    public function index()
    {
        Db::startTrans();
        try{
            $db = Db::table('test');
            $res = $db->where('id',1)->value('stock');//這樣庫存有可能會變負數
            //$res = $db->where('id',1)->lock(true)->value('stock');//這句解決併發帶來的問題,好比庫存負數
            if ($res >= 1){
                Db::table('test')->where('id', 1)->setDec('stock');
            }

            // 提交事務
            Db::commit();

        } catch (\Exception $e) {
            // 回滾事務
            Db::rollback();
            echo $e->getMessage();
        }
    }
}

複製代碼

部分參考自: blog.csdn.net/justloveyou…

相關文章
相關標籤/搜索