知識點六:隔離級別,表鎖,行鎖,死鎖,更新丟失,索引

Mysql 隔離級別

  1. 讀未提交,讀已提交,可重複讀:事務過程中看到的數據和事務啓動時看到的數據是一致的;串行化:讀會加讀鎖,寫會加寫鎖,讀寫鎖,寫寫鎖衝突時,後一個事務需要前一個事務執行完;

  2. 隔離級別的實現:

	讀未提交:每次返回記錄上的最新值;
	讀已提交:MVCC多版本視圖,在sql 執行前開始創建視圖;
	可重複讀:MVCC 多版本視圖,在事務啓動時創建視圖;
	串行化:加鎖的方式規範並行訪問;
  1. MVCC:是指每條記錄在更新時都會同時記錄一條回滾操作,通過回滾段就可以生成同一條記錄的多個版本,每個版本和更新的 transaction_id 綁定,transation_id 是嚴格遞增的,這樣開啓事務時,通過一個數組記錄當前時刻啓動了但未提交的 transactionId,那麼每條記錄的可見性,就通過比較是否小於該數組記錄來判斷,由此就生成了 mvcc 的視圖;
	mvcc 分析可見性,通過:1. 自己的更新總是可見;2. 版本未提交,不可見;3. 版本已提交,在視圖創建後提交的,不可見;4. 版本已提交,而且是在視圖創建前提交的,可見;
  1. 長事務需要記錄大量的回滾段,且佔用鎖資源時間長,要儘量避免;

  2. 建議生產中開啓自動提交,當需要手動定義一個事務邊界時,使用 begin-commit/rollback;

全局鎖和表鎖:

  1. 全局鎖就是對整個數據庫實例加鎖,主要用在數據庫備份的場景中,加鎖後整個數據庫實例處於只讀狀態;

  2. 表鎖是限制對單表的讀寫操作,語法是 lock tables table_name read/write,用在不支持行鎖的存儲引擎上控制併發;

  3. 另一個表級鎖是 MDL,當對一個表做增刪改查操作時,會加 MDL 讀鎖;當對錶結構做變更操作時,會加 MDL 寫鎖;並且是系統默認加鎖,且讀鎖和讀鎖不衝突,其他鎖之間是相互衝突的;

  4. 要特別注意,給表加字段,修改字段,加索引,都是需要掃描全表的,並且加 MDL 寫鎖,會阻塞後面的所有增刪改查操作請求,如果加 mdl 寫鎖也需要阻塞等待之前線程釋放 mdl 讀鎖,所以可以在sql 中添加 wait n,nowait 來限制等待 mdl 寫鎖時間,避免長時間阻塞後續操作;

行鎖

  1. innodb 引擎支持實現,用來控制併發;

  2. 兩階段鎖協議,事務中,行鎖是在需要的時候才加上,要等到事務結束時才釋放;因此事務中,要把最有可能衝突的記錄儘量放在事務後半段執行,這樣可以減少鎖時間;

死鎖

  1. 死鎖,事務A update id =1,事務B update id = 2,事務B update id = 1,事務A udpate id = 2,此時兩個事務就發生了死鎖,解決方案,一種是設置鎖等待超時,太長,如果發生死鎖,業務需要阻塞長時間,太短,會將正常的鎖等待當作死鎖處理;

  2. 另一種是死鎖檢測,每一個等待鎖的線程,都判斷是否因爲自己的加入造成了死鎖,需要判斷更新同一記錄的所有線程,如果發現死鎖,主動回滾自己的事務,推薦使用這種策略,因爲第一種鎖超時策略的超時時間難把握;

  3. 死鎖檢測有一定成本,特別對於併發競爭激烈的數據庫行記錄,因爲更新操作都需要加鎖,需要阻塞進行,並且死鎖檢測的成本會急劇增加,此時可以考慮將單行競爭記錄的行記錄,分爲多行來記錄,減少單行的併發修改操作;

當前讀和更新丟失

  1. update 操作,既需要加鎖,同時都是先讀後寫的,並且是當前讀,只能讀當前的值,這樣就不會發生更新丟失,除非業務代碼中是先查詢,然後根據查詢結果來更新的,因爲這個查詢就不是當前讀了,只會讀到 mvcc 視圖中的值;

  2. 普通的 select 語句是一致性讀,根據 mvcc 視圖來確定可見性,如果 select 語句加上 lock in share mode 或 for update,就是是當前讀,能讀到最新的值;

  3. 回滾丟失,任何隔離級別都不會出現;

  4. 示例參考:https://time.geekbang.org/column/article/70562?utm_source=pinpaizhuanqu&utm_medium=geektime&utm_campaign=guanwang&utm_term=guanwang&utm_content=0511;

數據庫索引結構

  1. 哈希索引,適用於只有等值查詢,不適合範圍查找,因爲無序存放,需要將範圍區間全部掃描一遍;

  2. 有序數組索引,等值查找,使用二分查找就可以,範圍查找,也只需要先找到大於等於區間的最小值,然後依次遍歷,直到第一個大於區間最大值的即可,但這種結構索引的修改成本很高,因此適合靜態數據;

  3. 二叉平衡樹,查找和插入的時間複雜度都是 O(logN),樹高 20,可以存儲的節點是 100 萬,通常每一層放入一個數據塊中,那麼一次查詢可能就需要 20 次訪問數據塊,假設每次 io 訪問數據塊 10 ms,那麼就需要 200ms,相當耗時;

  4. n 叉平衡樹,假設 n = 1200 時,樹高 3,就可以存儲 17 億的記錄了,那麼最多三次 io 操作訪問數據塊,就可以查找到索引值,比較適合做數據庫索引結構;

  5. n 階 b+ 樹和 b 樹,表示每個節點最多有 n -1 個關鍵字,n 個孩子節點,插入新的關鍵字時,如果節點關鍵字大於 n -1,就需要進行分裂,提取中間節點到父節點,b 樹就是普通的 n 階多叉樹;

  6. b+ 樹,每個父節點都會在其右子節點冗餘存儲一份,所以樹中所有關鍵字在葉子節點都會冗餘存儲,其非葉子節點就只會存儲索引值,葉子節點會存儲索引值和主鍵值,並且每個葉子節點都有相鄰葉子節點的指針,通過指針,葉子節點就是按照關鍵字從小到大順序連接的;

  7. b+ 樹這種結構,非葉子節點存儲數據更多,每次查找都需要找到葉子節點,查詢穩定性更高,並且葉子節點形成有序結構,更利於範圍查找;

  8. innodb 引擎中,使用的是 b+ 樹的索引結構,表中數據是按主鍵的索引結構存放的,每一個索引都是一棵 b+ 樹,其中主鍵索引,聚簇索引,其葉子節點是整行數據,非主鍵索引,葉子節點是主鍵值,所以非主鍵索引查找,通常需要回表;

	主鍵通常選擇自增,因爲自增有序,很方便的維護索引樹,如果無序的話,可能涉及數據頁的分裂,合併,並且自增主鍵使得非主鍵索引的佔用空間最小;
  1. 索引範圍查找,是先找到區間最小值的索引值,然後按順序遍歷,直到找到第一個不滿足範圍區間的值即可;

  2. 使用聯合索引是一個常用的性能優化手段,索引中包含所有要查詢的字段列,聯合索引有最左匹配的原則,有了 (a,b) 的聯合索引,通常就不需要單獨的 a 索引了;

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