元數據解決分表不可 mysql_MySQL InnoDB技術內幕:內存管理、事務和鎖

前面有多篇文章介紹過MySQL InnoDB的相關知識,今天我們要更深入一些,看看它們的內部原理和機制是如何實現的。

一、內存管理

我們知道,MySQl是一個存儲系統,數據最后都寫在磁盤上。我們以前也提到過,磁盤的速度特別是大容量的磁盤受磁頭臂的影響,速度相對內存慢很多。所以Innodb實現了自己的緩存機制。

首先我們先看下Innodb對內存是如何使用和劃分的,然后我們再看看它是如何保存熱數據的。

1、主要模塊和組成

(1) Buffer Pool

預分配的內存池

(2) Page

Buffer Pool的最小單位

(3) Free list

空閑Page組成的鏈表

(4) Flush list

臟頁鏈表

(5) Page hash 表

維護內存Page和文件Page的映射關系

(6) LRU

內存淘汰算法

以上三種鏈表LRU list、Free list、Flush list 和內存池、Page hash?以及磁盤文件之間的映射關系如下圖所示:

06821852ed1c11d6e039c31a2e6eb009.png

2、LRU算法

LRU,Least Recent Used,最近最少使用。每次將剛使用過的頁面插到LRU隊列的最前端,那么最少使用的排在尾端,當緩存不夠時,淘汰尾端的頁。

很多文件系統和開源庫的內存淘汰算法都用到了LRU,以前有不少文章都提到過。

但是LRU的缺陷是,有時會無法淘汰真正的冷數據,尾端的數據可能暫時沒使用而已,不代表不使用頻繁,不代表不是熱數據。所以很多系統對LRU進行了優化。

比如Redis加了LFU(least?frequently?used最不經常使用)配合LRU一起使用。

那么InnoDB存儲引擎是如何改進的呢?如下圖,它將LRU分成兩部分,中間的分割點叫做midpoint,新讀取的頁不再是加入到最頭部,而是midpoint后面的位置,即后半截的頭部。

那么midpoint的位置是如何計算的呢,在默認配置下,離LRU整個頭部的5/8處。當然這個比例是可以根據實際業務進行設置的。但總之,可以真正將 冷熱數據分離 ,熱數據在前,冷數據在后。

a3d832714f9f43d13ff9fe338d42963a.png

那么這兩個區的數據如何移動的呢,即冷熱數據如何切換的呢?

上面我們提到了,剛插入的頁放在old區的頭部,那么如果該頁確實訪問頻繁,不能一直呆在該位置吧。

InnoDB引入了參數innodb_old_blocks_time,如果old區的數據在該時間范圍內沒有被淘汰出去,就可以移到new區,加入到new區的頭部。這也叫做 made young 。

而如果在old呆的時間不夠innodb_old_blocks_time,而且緩存不夠時,就會面臨直接淘汰,這就叫做 made not young 。這種情況,可以發生在全表掃描的時候,保證了new區的數據才是真正的熱數據!

當然數據也有可能從 new區移動到old區 ,只是相對比較簡單了,直接移動midpoint指向的位置即可。即new區的尾巴變成了old區的頭部。

二、事務

1、 MySQL事務基本概念

事務特性

A(Atomicity原子性):全部成功或全部失敗

I(Isolation隔離性):并行事務之間互不干擾

D(Durability持久性):事務提交后,永久生效

C(Consistency一致性):通過AID保證

并發問題

臟讀(Drity Read):讀取到未提交的數據。 中間所有變化的值都可能讀到 。

不可重復讀(Non-repeatable read):兩次讀取結果不同。讀取已提交的(不一樣的值), 讀到的值變化數量比臟讀要少 。

幻讀(Phantom Read):select 操作得到的結果所表征的數據狀態 影響(無法支撐)后續的業務操作。

網上有人這樣區分,臟讀是讀取修改的數據,幻讀是讀取新提交的數據。我認為也可以,或許phantom表示 虛幻的新數據 (所以無法支撐后續操作),而drity代表了修改的意思呢?

所以,不可重復讀重點在于update和delete,而幻讀的重點在于insert。

隔離級別

Read Uncommitted(讀取未提交內容):最低隔離級別,會讀取到其他事務未提交的數據;存在 臟讀 的問題 。

Read Committed(讀取提交內容):事務過程中可以讀取到其他事務已提交的數據;存在 不可重復讀 的問題 。

Repeatable Read(可重復讀):每次讀取相同結果集,不管其他事務是否提交;存在 幻讀 的問題 。

Serializable(串行化):事務排隊,隔離級別最高,性能最差。

2、MySQL事務實現原理

從上我們可以看出事務有ACID四大特性,而“I”隔離性是通過鎖來實現的,我們下一節講述。那么其他三個特性主要通過undo/redo日志的機制來實現,這個知識點在前面有一篇文章中介紹和對比過。 現在我們站在事務實現的角度再來看看。

(1)undo log

回滾日志,顧名思義,是對事物rollback時使用。這是它核心的功能之一,但是它還有另一個非常重要的功能,MVCC。所以今天這里主要介紹它是如何在事務中發揮作用的。

MVCC

Multiversion concurrency control,多版本并發控制。當用戶讀取一行時,如果該記錄已經被其他事務占用,當前事務可以通過undo讀取之前的 行版本信息 ( 快照數據 ),以此實現 非鎖定讀 。 所以實現了非阻塞的讀操作,寫操作也只鎖定必要的行。即 解決讀-寫沖突。

快照數據就是當前行數據的歷史版本,每行記錄可能含有多個版本。那該讀取哪個版本呢?

首先,InnoDB的每行記錄或者說每條數據,除了記錄用戶定義的列之外,還有 兩個隱藏的列 :事務ID列 DB_TRX_ID 和回滾指針 DB_ROLL_PTR。 如果該表沒有定義主鍵,每行還會增加一個rowid列。 DB_TRX_ID是當時執行這條sql的事務id,DB_ROLL_PTR指向的就是undo log中修改前的行DB_ROW_ID。所以對同一條數據的修改,通過roll_pointer就形成了 undo log版本鏈 。

f5fefedbceab3dc70e3bb794718e8040.png

然后我們再來介紹下 Read View 快照讀。

一般情況下讀取數據時會生成一個Read View,對當前該行的可能正在進行的事務進行一個快照。

Read View中主要包含4個比較重要的內容:

m_ids:表示在生成Read View時當前系統中活躍的讀寫事務的事務id列表,簡稱 活躍列表 。

min_trx_id:表示在生成Read View時當前系統中活躍的讀寫事務中 最小的事務id ,也就是m_ids中的最小值。

max_trx_id:表示生成Read View時系統中應該分配給下一個事務的 id 值,,也就是m_ids中的 最大 值。

creator_trx_id:表示生成該Read View的事務的事務id。

b225daee490b6787a8d1e5c27f1d92e6.png

有了這些信息,這樣在訪問某條記錄時,只需要依次判斷undo log版本鏈中節點的事務ID 是否可見 ,如果可見即找到了所需要的行記錄。

77327534fe53a49ff9ca5c06d7b61e60.png

最后,READ COMMITTED和REPEATABLE READ兩種隔離級別對于快照數據生成的時機不一樣。

對于RC,在每次查詢語句執行的過程中,都關閉Read View, 再創建當前的一份Read View。這樣就會產生不可重復讀現象。

對于RR,創建事務trx結構的時候,就生成了當前的global Read View,一直維持到事務結束。在事務結束這段時間內每一次查詢都不會重新重建Read View,從而實現了可重復讀。

undo log分為兩種格式 ,處理不一樣。

insert undo log:用于回滾,提交即清理;不需要進行purge操作。

update undo log:用于回滾,同時實現快照讀,不能隨便刪除,所以需要等待purge線程來判斷何時刪除。它記錄的是對delete和update的操作產生的undo log。

注:以上來自書上的說法,網上有人把第一種說成delete undo log,包括insert和delete操作,供參考。

還需要補充一點的是,update undo log怎樣去清理, 應該是根據系統活躍的Read view中最小的活躍事務ID之前的即可清除。

(2)redo log

redo日志其實在《 MySQL的undo/redo日志和binlog日志,以及2PC 》文章中介紹比較多,也提到了XA事務的2PC。我們這里簡單介紹下普通事務的流程。

d8660011ec96b54e943247a962105918.png

寫入流程仍然可以分為兩步,類似二階段提交:

記錄頁的修改,狀態為prepare

事務提交,講事務記錄為commit狀態

三、鎖

1、InnoDB鎖種類

(1) 類型

共享鎖(S)

讀鎖,可以同時被多個事務獲取,阻止其他事務對記錄的修改。

排他鎖(X)

寫鎖,只能被一個事務獲取,允許獲得鎖的事務修改數據。

而讀其實又可以分為 當前讀 (鎖定讀)和快照讀(非鎖定讀),而快照讀通過上一節描述的MVCC來實現。

當前讀, 讀取的是最新版本,所以需要對讀取的記錄加鎖,阻塞其他事務改動該記錄。當前讀又分為兩種方式:

select...for update,對讀取的行加X鎖;

select...lock in share mode ,對讀取的行加S鎖。

(2)鎖粒度

行級鎖

Record Lock,單個記錄上的鎖。

鎖直接加在索引記錄上面,鎖住的是key,所以必須是 聚簇索引或者二級索引是唯一索引。

間隙鎖

Gap Lock,間隙鎖,鎖定一個范圍,單不包含記錄本身。

InnoDB存儲引擎的隔離級別默認是Repeatable Read,所以引入了間隙鎖 解決 可重復讀模式下的 幻讀問題 。

GAP鎖不是加在記錄上, 鎖住的位置是兩條記錄之間的GAP; 保證 兩次當前讀 返回一致的記錄。

所以兩次當前讀之前,其他的事務 不會插入新的 滿足條件的記錄。

我們來整理下著兩者的關系和區別。

Record Lock針對的是索引必須具備唯一性;而GAP鎖針對的是索引不具備唯一性但需要保證可重復讀,也就是說如果發現數據有被其他事務修改的可能,那就把前后間隙都加上鎖。

比如說如下圖,有個用戶表,uid為主鍵,那么就只需要103這條記錄加上行鎖即可。

85517411472e55490a6ba111c15e6af6.png

但是如果我們變化下查詢條件(phone列上建立了二級索引),則除了對于這兩條記錄加鎖外,對前后的間隙也需要加鎖。當然這種情況是針對RR的隔離級別,如果隔離級別是RC或者更低,安全性就沒有這么高,系統會自動降級到行鎖。

caf1604c1bac739750e049551820708a.png

Next-Key Lock,是Record Lock與Gap Lock的一個結合。理解了上述兩種鎖的原理,對于它而言就很容易了。

表級鎖

Table Lock,鎖定整張表。

主要用在運維的時候,對表格進行操作比如MDL或者元數據的操作 (meta data lock)等等。

當然有些情況下會觸發鎖升級: 全表掃描。全表掃描的觸發一般情況下是當前被查詢的字段沒有建立任何索引。

而表級鎖事實上是對所有記錄和所有的間隙都加上鎖。

所以全表掃描的效率非常低,要盡量避免。

2、InnoDB加鎖過程

如下圖,當我們更新多條數據時,是一行一行的加鎖。

843929bc79bc3c7c27071e61add15c3a.png

所以當同時出現對多條記錄交叉查詢時,很容易出現AB-BA死鎖,如下圖操作。

f07aa9d5c2497c80822cfb5c5d1fb38b.png

附錄

分庫分表的建議:

是否分表

建議單表不超過1KW

分表方式

取模:存儲均勻&訪問均勻

按時間:冷熱庫

分庫

按業務垂直分

水平查分多個庫

參考:

《MySQL技術內幕InnoDB存儲引擎》

內部培訓資料

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/530322.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/530322.shtml
英文地址,請注明出處:http://en.pswp.cn/news/530322.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

navicat for mysql 13_Navicat for MySQL下載

Navicat for MySQL 是一套管理和開發 MySQL 或 MariaDB 的理想解決方案。它使你以單一程序同時連接到 MySQL 和 MariaDB。這個功能齊備的前端軟件為數據庫管理、開發和維護提供了直觀而強大的圖形界面。它提供了一組全面的工具給 MySQL 或MariaDB 新手,同時給專業人…

mysql 日期型中文報錯_mysql日期類型默認值'0000-00-00' 報錯,是什么問題?

如題,本來是 從另一個數據庫中導出的sql文件,在我電腦上導入報這個錯誤,不知道是不是mysql 版本問題。多方搜索無果,所以上來求助。DROP TABLE IF EXISTS workreport_member;CREATE TABLE workreport_member (uid int(10) unsigne…

python在線作業_南開大學20春學期《Python編程基礎》在線作業參考答案

南開大學20春學期(1709、1803、1809、1903、1909、2003)《Python編程基礎》在線作業試卷總分:100 得分:98一、單選題(共20 道試題,共40 分)1.已知“stra\rb\r\nc\n”,則“str.splitlines()”的返回結果是( )。A.[a,b,c]B.[a\r,b\r\n,c\n]C.[a\r,b\r,c]D.[a\r,b,c]答案:A2.已知“…

spring兼容mysql_springboot 最新版本支持 mysql6.0.6嗎

縹緲止盈1.首先在pom文件中加入下列依賴,一個使用jpa所需依賴,一個連接MySQL使用的依賴:mysqlmysql-connector-javaorg.springframework.bootspring-boot-starter-data-jpa 123456789102.在配置文件中添加datasource配置和jpa配置,在mysql中已經提前創建了一個名為db_test的數據…

java集合map_JAVA中的集合類Map、Set、List

*精煉的總結:Collection 是對象集合, Collection 有兩個子接口 List 和 SetList 可以通過下標 (1,2..) 來取得值,值可以重復而 Set 只能通過游標來取值,并且值是不能重復的ArrayList , Vector , LinkedList…

java虛擬機內存監控_java虛擬機內存監控工具jps,jinfo,Jstack,jstat,jmap,jhat使用...

將會打印出很多jvm運行時參數信息,由于比較長這里不再打印出來,可以自己試試,內容一目了然Jstack(Stack Trace for Java):JVM堆棧跟蹤工具jstack用于打印出給定的java進程ID或core file或遠程調試服務的Java堆棧信息,如…

idea 調試java技巧_IDEA 調試Java代碼的兩個技巧

本文介紹兩個使用IDEA 調試Java代碼的兩個技巧:修改變量值使用RuntimeException終止代碼執行修改變量值在Java代碼調試過程中,我們可以修改變量值,使其達到走指定分支的目的,或者使其滿足某個條件。我們以給變量beanName賦值為例&…

java 10進制轉 000x_java 如何把 00 轉換成 0x00 或者 10 轉換成 0x10

public static void main(String[] args) {String s "00000018A0010098C68E00989A690000000000BC614E000055AA55AA";System.out.println(s);byte[] b HexString2Bytes(s);System.out.println(Bytes2HexString(b));}/*** 將指定byte數組以16進制的形式打印到控制臺*…

java免檢異常_java-異常

java提供了異常處理機制:程序運行受阻時候的處理方式。1、異常分類Error:系統錯誤,由java虛擬機拋出,很少發生;免檢異常RuntimeException:程序設計錯誤,通常由java虛擬機拋出;免檢異…

java編程需要數學知識嗎_初學Java編程,需要英語和數學基礎嗎?

原標題:初學Java編程,需要英語和數學基礎嗎?“學習Java編程英語和數學是必備條件嗎?”很多Java零基礎學習或者轉型IT行業的都會有這樣的疑問,其實剛開始學習Java編程是不需要太高深的數學和英語基礎的。剛開始學習Java…

java map put報錯_java 集合(Map)

-------------------|Map 儲存的數據都是以鍵值對的形式,鍵不可重復,值可重復。----------------------------| HashMap----------------------------| TreeMap----------------------------| HashTableMap接口的方法:添加:put(K…

java簡單數據結構_圖解Java常用數據結構

最近在整理數據結構方面的知識, 系統化看了下 Java 中常用數據結構, 突發奇想用動畫來繪制數據流轉過程.主要基于 jdk8, 可能會有些特性與 jdk7 之前不相同, 例如 LinkedList LinkedHashMap 中的雙向列表不再是回環的.HashMap 中的單鏈表是尾插, 而不是頭插入等等, 后文不再贅敘…

jest java_?使用jest進行測試驅動開發

前言本文將使用jest進行測試驅動開發的示例,源碼在github。重點說明在開發中引入單元測試后開發過程,以及測試先行的開發思路。本文的重點是過程以及思維方法,框架以及用法不是重點。本文使用的編程語言是javascript,思路對其他語…

mysql sqlstate 08001_關于Toad連接DB2的sqlstate=08001錯誤

新裝的centos6.3db29.7,數據庫導入完了的之后用Toad連接訪問之的時候出錯了。DB2 Database Error: ERROR [08001] [IBM] SQL30081N A communication error has been detected. Communication protocol being used: "TCP/IP". Communication API being use…

mysql 設置主鍵命令_MySQL常用命令

1、修改MySQL密碼方法一:use mysql;update user set passwordPASSWORD(“123456”) where user‘root’;flush privileges;忘記密碼:sed -ri 3d skip-grant-tables /etc/my.cnfsystemctl restart mariadbuse mysql&…

python 整除的數組_計算和可被整除的所有子數組

在我學習面試的時候,我在GeeksForGeeks上找到了這個問題和解決方案,但不明白答案。在上面說的是Let there be a subarray (i, j) whose sum is divisible by ksum(i, j) sum(0, j) - sum(0, i-1)Sum for any subarray can be written as q*k rem where…

java ha_java – Haproxy Bad Gateway 502

所以我在Jetty servlet面前使用HAProxy.目前的目標只是在配置完所有內容后進行概念驗證,加載和壓力測試.但是我在配置haproxy時遇到問題.我知道這不是我的應用程序的問題,因為我有運行nginx(tengine),一切正常.所以它必須與haproxy配置或haproxy工作的方式不適合我的需要.所以我…

java ioutils_java – 無法解析符號’IOUtils’

我使用以下代碼在我的Android應用程序中創建一個臨時文件:public File streamToFile (InputStream in) throws IOException {File tempFile File.createTempFile("sample", ".tmp");tempFile.deleteOnExit();FileOutputStream out new FileOu…

java const關鍵字_const關鍵字:終于擁有真正的常量聲明語句

你好,今天大叔想和你嘮扯嘮扯 ES6 新增的關鍵字 —— const。在說 const 關鍵字之前,大叔先和你嘮嘮大叔自己對 const 的感受 —— JavaScript 尼瑪終于可以聲明真正的常量啦!大叔為啥會發出這樣滴感嘆?實在是“天下苦秦久矣”呀~…

workerman高并發異步mysql_workerman怎么實現高并發

并發概念太模糊,這里以兩種可以量化的指標并發連接數和并發請求數來說明。并發連接數是指服務器當前時刻一共維持了多少TCP連接,而這些連接上是否有數據通訊并不關注。 (推薦學習: workerman教程)例如一臺消息推送服務器上可能維持了百萬的設…