0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

454398 ? 來源:博客園 ? 作者: Yelbosh ? 2020-10-29 14:36 ? 次閱讀

本文主要是針對MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)做一個(gè)比較深入的剖析,并且對其中涉及到的重要的概念,如多版本并發(fā)控制(MVCC),臟讀(dirty read),幻讀(phantom read),四種隔離級別(isolation level)等作詳細(xì)的闡述,并且基于一個(gè)簡單的例子,對MySQL的加鎖進(jìn)行了一個(gè)詳細(xì)的分析。本文的總結(jié)參考了何登成前輩的博客,并且在前輩總結(jié)的基礎(chǔ)上,進(jìn)行了一些基礎(chǔ)性的說明,希望對剛?cè)腴T的同學(xué)產(chǎn)生些許幫助,如有錯(cuò)誤,請不吝賜教。按照我的寫作習(xí)慣,還是通過幾個(gè)關(guān)鍵問題來組織行文邏輯,如下:

什么是MVCC(多版本并發(fā)控制)?如何理解快照讀(snapshot read)和當(dāng)前讀(current read)?

什么是隔離級別?臟讀?幻讀?InnoDB的四種隔離級別的含義是什么?

什么是死鎖?

InnoDB是如何實(shí)現(xiàn)MVCC的?

一個(gè)簡單的sql在不同場景下的加鎖分析

一個(gè)復(fù)雜的sql的加鎖分析

接下來,我將按照這幾個(gè)關(guān)鍵問題的順序,對以上問題作一一解答,并且在解答的過程中,爭取將加鎖技術(shù)的細(xì)節(jié),闡述的更加清楚。

1.1 MVCC:Multi-Version Concurrent Control 多版本并發(fā)控制

MVCC是為了實(shí)現(xiàn)數(shù)據(jù)庫的并發(fā)控制而設(shè)計(jì)的一種協(xié)議。從我們的直觀理解上來看,要實(shí)現(xiàn)數(shù)據(jù)庫的并發(fā)訪問控制,最簡單的做法就是加鎖訪問,即讀的時(shí)候不能寫(允許多個(gè)西線程同時(shí)讀,即共享鎖,S鎖),寫的時(shí)候不能讀(一次最多只能有一個(gè)線程對同一份數(shù)據(jù)進(jìn)行寫操作,即排它鎖,X鎖)。這樣的加鎖訪問,其實(shí)并不算是真正的并發(fā),或者說它只能實(shí)現(xiàn)并發(fā)的讀,因?yàn)樗罱K實(shí)現(xiàn)的是讀寫串行化,這樣就大大降低了數(shù)據(jù)庫的讀寫性能。加鎖訪問其實(shí)就是和MVCC相對的LBCC,即基于鎖的并發(fā)控制(Lock-Based Concurrent Control),是四種隔離級別中級別最高的Serialize隔離級別。為了提出比LBCC更優(yōu)越的并發(fā)性能方法,MVCC便應(yīng)運(yùn)而生。

幾乎所有的RDBMS都支持MVCC。它的最大好處便是,讀不加鎖,讀寫不沖突。在MVCC中,讀操作可以分成兩類,快照讀(Snapshot read)和當(dāng)前讀(current read)??煺兆x,讀取的是記錄的可見版本(可能是歷史版本,即最新的數(shù)據(jù)可能正在被當(dāng)前執(zhí)行的事務(wù)并發(fā)修改),不會對返回的記錄加鎖;而當(dāng)前讀,讀取的是記錄的最新版本,并且會對返回的記錄加鎖,保證其他事務(wù)不會并發(fā)修改這條記錄。在MySQL InnoDB中,簡單的select操作,如select * from table where ? 都屬于快照讀;屬于當(dāng)前讀的包含以下操作:

select * from table where ? lock in share mode; (加S鎖)

select * from table where ? for update; (加X鎖,下同)

insert, update, delete操作

針對一條當(dāng)前讀的SQL語句,InnoDB與MySQL Server的交互,是一條一條進(jìn)行的,因此,加鎖也是一條一條進(jìn)行的。先對一條滿足條件的記錄加鎖,返回給MySQL Server,做一些DML操作;然后再讀取下一條加鎖,直至讀取完畢。需要注意的是,以上需要加X鎖的都是當(dāng)前讀,而普通的select(除了for update)都是快照讀,每次insert、update、delete之前都是會進(jìn)行一次當(dāng)前讀的,這個(gè)時(shí)候會上鎖,防止其他事務(wù)對某些行數(shù)據(jù)的修改,從而造成數(shù)據(jù)的不一致性。我們廣義上說的幻讀現(xiàn)象是通過MVCC解決的,意思是通過MVCC的快照讀可以使得事務(wù)返回相同的數(shù)據(jù)集。如下圖所示:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

注意,我們一般說在MyISAM中使用表鎖,因?yàn)镸yISAM在修改數(shù)據(jù)記錄的時(shí)候會將整個(gè)表鎖起來;而InnoDB使用的是行鎖,即我們以上所談的MVCC的加鎖問題。但是,并不是InnoDB引擎不會使用表鎖,比如在alter table的時(shí)候,Innodb就會將該表用表鎖鎖起來。

1.2 隔離級別

在SQL的標(biāo)準(zhǔn)中,定義了四種隔離級別。每一種級別都規(guī)定了,在一個(gè)事務(wù)中所做的修改,哪些在事務(wù)內(nèi)和事務(wù)間是可見的,哪些是不可見的。低級別的隔離可以執(zhí)行更高級別的并發(fā),性能好,但是會出現(xiàn)臟讀和幻讀的現(xiàn)象。首先,我們從兩個(gè)基礎(chǔ)的概念說起:

臟讀(dirty read):兩個(gè)事務(wù),一個(gè)事務(wù)讀取到了另一個(gè)事務(wù)未提交的數(shù)據(jù),這便是臟讀。

幻讀(phantom read):兩個(gè)事務(wù),事務(wù)A與事務(wù)B,事務(wù)A在自己執(zhí)行的過程中,執(zhí)行了兩次相同查詢,第一次查詢事務(wù)B未提交,第二次查詢事務(wù)B已提交,從而造成兩次查詢結(jié)果不一樣,這個(gè)其實(shí)被稱為不可重復(fù)讀;如果事務(wù)B是一個(gè)會影響查詢結(jié)果的insert操作,則好像新多出來的行像幻覺一樣,因此被稱為幻讀。其他事務(wù)的提交會影響在同一個(gè)事務(wù)中的重復(fù)查詢結(jié)果。

下面簡單描述一下SQL中定義的四種標(biāo)準(zhǔn)隔離級別:

READ UNCOMMITTED (未提交讀):隔離級別:0. 可以讀取未提交的記錄。會出現(xiàn)臟讀。

READ COMMITTED (提交讀):隔離級別:1. 事務(wù)中只能看到已提交的修改。不可重復(fù)讀,會出現(xiàn)幻讀。(在InnoDB中,會加行所,但是不會加間隙鎖)該隔離級別是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)隔離級別,但是MySQL的則是RR。

REPEATABLE READ (可重復(fù)讀):隔離級別:2.在InnoDB中是這樣的:RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時(shí)保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),因此不存在幻讀現(xiàn)象。但是標(biāo)準(zhǔn)的RR只能保證在同一事務(wù)中多次讀取同樣記錄的結(jié)果是一致的,而無法解決幻讀問題。InnoDB的幻讀解決是依靠MVCC的實(shí)現(xiàn)機(jī)制做到的。

SERIALIZABLE (可串行化):隔離級別:3. 該隔離級別會在讀取的每一行數(shù)據(jù)上都加上鎖,退化為基于鎖的并發(fā)控制,即LBCC。

需要注意的是,MVCC只在RC和RR兩個(gè)隔離級別下工作,其他兩個(gè)隔離級別都和MVCC不兼容。

1.3 死鎖

死鎖是指兩個(gè)或者多個(gè)事務(wù)在同一資源上相互作用,并請求鎖定對方占用的資源,從而導(dǎo)致惡性循環(huán)的現(xiàn)象。當(dāng)多個(gè)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能產(chǎn)生死鎖。多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí),也會產(chǎn)生死鎖。且看下面的兩個(gè)產(chǎn)生死鎖的例子:

第一個(gè)死鎖很好理解,而第二個(gè)死鎖,由于在主索引(聚簇索引表)上仍舊是對兩條記錄進(jìn)行了不同順序的加鎖,因此仍舊會造成死鎖。死鎖的發(fā)生與否,并不在于事務(wù)中有多少條SQL語句,死鎖的關(guān)鍵在于:兩個(gè)(或以上)的Session加鎖的順序不一致。因此,我們通過分析加鎖細(xì)節(jié),可以判斷所寫的sql是否會發(fā)生死鎖,同時(shí)發(fā)生死鎖的時(shí)候,我們應(yīng)該如何處理。

1.4 InnoDB的MVCC實(shí)現(xiàn)機(jī)制

MVCC可以認(rèn)為是行級鎖的一個(gè)變種,它可以在很多情況下避免加鎖操作,因此開銷更低。MVCC的實(shí)現(xiàn)大都都實(shí)現(xiàn)了非阻塞的讀操作,寫操作也只鎖定必要的行。InnoDB的MVCC實(shí)現(xiàn),是通過保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來實(shí)現(xiàn)的。一個(gè)事務(wù),不管其執(zhí)行多長時(shí)間,其內(nèi)部看到的數(shù)據(jù)是一致的。也就是事務(wù)在執(zhí)行的過程中不會相互影響。下面我們簡述一下MVCC在InnoDB中的實(shí)現(xiàn)。

InnoDB的MVCC,通過在每行記錄后面保存兩個(gè)隱藏的列來實(shí)現(xiàn):一個(gè)保存了行的創(chuàng)建時(shí)間,一個(gè)保存行的過期時(shí)間(刪除時(shí)間),當(dāng)然,這里的時(shí)間并不是時(shí)間戳,而是系統(tǒng)版本號,每開始一個(gè)新的事務(wù),系統(tǒng)版本號就會遞增。在RR隔離級別下,MVCC的操作如下:

select操作。a.InnoDB只查找版本早于(包含等于)當(dāng)前事務(wù)版本的數(shù)據(jù)行。可以確保事務(wù)讀取的行,要么是事務(wù)開始前就已存在,或者事務(wù)自身插入或修改的記錄。b.行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號??梢源_保事務(wù)讀取的行,在事務(wù)開始之前未刪除。

insert操作。將新插入的行保存當(dāng)前版本號為行版本號。

delete操作。將刪除的行保存當(dāng)前版本號為刪除標(biāo)識。

update操作。變?yōu)閕nsert和delete操作的組合,insert的行保存當(dāng)前版本號為行版本號,delete則保存當(dāng)前版本號到原來的行作為刪除標(biāo)識。

由于舊數(shù)據(jù)并不真正的刪除,所以必須對這些數(shù)據(jù)進(jìn)行清理,innodb會開啟一個(gè)后臺線程執(zhí)行清理工作,具體的規(guī)則是將刪除版本號小于當(dāng)前系統(tǒng)版本的行刪除,這個(gè)過程叫做purge。

1.5 一個(gè)簡單SQL的加鎖分析

在MySQL的InnoDB中,都是基于聚簇索引表的。而且普通的select操作都是基于快照讀,是不需要加鎖的。那么我們在分析其他的sql語句的時(shí)候,如何分析加鎖細(xì)節(jié)?下面我們以一個(gè)簡單的delete操作的SQL為例,進(jìn)行一個(gè)詳細(xì)的闡述。且看下面的SQL:

delete from t1 where id=10;

如果對這條SQL進(jìn)行加鎖分析,那么MySQL是如何加鎖的呢?一般情況下,我們直觀的感受是:會在id=10的記錄上加鎖。但是,這樣輕率的下結(jié)論是片面的,要想確定MySQL的加鎖情況,我們還需要知道更多的條件。還需要知道哪些條件呢?比如:

id列是不是主鍵?

系統(tǒng)的隔離級別是什么?

id非主鍵的話,其上有建立索引嗎?

建立的索引是唯一索引嗎?

該SQL的執(zhí)行計(jì)劃是什么?索引掃描?全表掃描?

接下來,我將這些問題的答案進(jìn)行組合,然后按照從易到難的順序,逐個(gè)分析每種組合下,對應(yīng)的SQL會加哪些鎖。

組合1:id列是主鍵,RC隔離級別

組合2:id列是二級唯一索引,RC隔離級別

組合3:id列是二級非唯一索引,RC隔離級別

組合4:id列上沒有索引,RC隔離級別

組合5:id列是主鍵,RR隔離級別

組合6:id列是二級唯一索引,RR隔離級別

組合7:id列是二級非唯一索引,RR隔離級別

組合8:id列上沒有索引,RR隔離級別

組合9:Serializable隔離級別

組合1:id列是主鍵,RC隔離級別

當(dāng)id是主鍵的時(shí)候,我們只需要在該id=10的記錄上加上x鎖即可。如下圖所示:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

組合2:id列是二級唯一索引,RC隔離級別

在這里我先解釋一下聚簇索引和普通索引的區(qū)別。在InnoDB中,主鍵可以被理解為聚簇索引,聚簇索引中的葉子結(jié)點(diǎn)就是相應(yīng)的數(shù)據(jù)行,具有聚簇索引的表也被稱為聚簇索引表,數(shù)據(jù)在存儲的時(shí)候,是按照主鍵進(jìn)行排序存儲的。我們都知道,數(shù)據(jù)庫在select的時(shí)候,會選擇索引列進(jìn)行查找,索引列都是按照B+樹(多叉搜索樹)數(shù)據(jù)結(jié)構(gòu)進(jìn)行存儲,找到主鍵之后,再回到聚簇索引表中進(jìn)行查詢,這叫回表查詢。那我們自然會問,當(dāng)使用索引進(jìn)行查詢的時(shí)候,與索引相對應(yīng)的記錄會被上鎖嗎?會的。如果id是唯一索引,那么只給該唯一索引所對應(yīng)的索引記錄上x鎖;如果id是非唯一索引,那么所對應(yīng)的所有的索引記錄上都會上x鎖。如下圖所示:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

組合3:id列是二級非唯一索引,RC隔離級別

解釋同上,如下圖:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

組合4:id列上沒有索引,RC隔離級別

由于id列上沒有索引,因此只能走聚簇索引,進(jìn)行全部掃描。有人說會在表上加X鎖;有人說會在聚簇索引上,選擇出來的id = 10 的記錄加上X鎖。真實(shí)情況如下圖:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

若id列上沒有索引,SQL會走聚簇索引的全掃描進(jìn)行過濾,由于過濾是由MySQL Server層面進(jìn)行的。因此每條記錄,無論是否滿足條件,都會被加上X鎖。但是,為了效率考量,MySQL做了優(yōu)化,對于不滿足條件的記錄,會在判斷后放鎖,最終持有的,是滿足條件的記錄上的鎖,但是不滿足條件的記錄上的加鎖/放鎖動作不會省略。同時(shí),優(yōu)化也違背了2PL的約束(同時(shí)加鎖同時(shí)放鎖)。

組合5,6同以上(因?yàn)橹挥幸粭l結(jié)果記錄,只能在上面加鎖)

組合7:id列是二級非唯一索引,RR隔離級別

在RR隔離級別下,為了防止幻讀的發(fā)生,會使用Gap鎖。這里,你可以把Gap鎖理解為,不允許在數(shù)據(jù)記錄前面插入數(shù)據(jù)。首先,通過id索引定位到第一條滿足查詢條件的記錄,加記錄上的X鎖,加GAP上的GAP鎖,然后加主鍵聚簇索引上的記錄X鎖,然后返回;然后讀取下一條,重復(fù)進(jìn)行。直至進(jìn)行到第一條不滿足條件的記錄[11,f],此時(shí),不需要加記錄X鎖,但是仍舊需要加GAP鎖,最后返回結(jié)束。如下圖所示:

組合8:id列無索引,RR隔離級別

在這種情況下,聚簇索引上的所有記錄,都被加上了X鎖。其次,聚簇索引每條記錄間的間隙(GAP),也同時(shí)被加上了GAP鎖。如下圖:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

但是,MySQL是做了相關(guān)的優(yōu)化的,就是所謂的semi-consistent read。semi-consistent read開啟的情況下,對于不滿足查詢條件的記錄,MySQL會提前放鎖,同時(shí)也不會添加Gap鎖。

組合9:Serializable隔離級別

和RR隔離級別一樣。

1.6 一個(gè)復(fù)雜的SQL的加鎖分析

這里我們只是列出一個(gè)結(jié)論,因?yàn)橐婕暗組ySQL的where查詢條件的分析,因此這里先不做詳細(xì)介紹,我會在之后的博客中詳細(xì)說明。如下圖:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

結(jié)論:在RR隔離級別下,針對一個(gè)復(fù)雜的SQL,首先需要提取其where條件。Index Key確定的范圍,需要加上GAP鎖;Index Filter過濾條件,視MySQL版本是否支持ICP,若支持ICP,則不滿足Index Filter的記錄,不加X鎖,否則需要X鎖;Table Filter過濾條件,無論是否滿足,都需要加X鎖。加鎖的結(jié)果如下所示:

深度剖析MySQL/InnoDB的并發(fā)控制和加鎖技術(shù)

總結(jié)

本文只是對MVCC的一些基礎(chǔ)性的知識點(diǎn)進(jìn)行了詳細(xì)的總結(jié),參考了網(wǎng)上和書上比較多的資料和實(shí)例。
編輯:hfy

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 死鎖
    +關(guān)注

    關(guān)注

    0

    文章

    25

    瀏覽量

    8059
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    798

    瀏覽量

    26401
  • MVCC
    +關(guān)注

    關(guān)注

    0

    文章

    13

    瀏覽量

    1459
收藏 人收藏

    評論

    相關(guān)推薦

    C語言深度剖析

    C語言深度剖析
    發(fā)表于 08-25 09:08

    C語言深度剖析

    C語言深度剖析[完整版].pdfC語言深度剖析[完整版].pdf (919.58 KB )
    發(fā)表于 03-19 05:11

    詳解Mysql數(shù)據(jù)庫InnoDB存儲引擎事務(wù)

    關(guān)于Mysql數(shù)據(jù)庫InnoDB存儲引擎事務(wù)的一點(diǎn)理解
    發(fā)表于 05-13 10:11

    由于InnoDB MVCC導(dǎo)致的并發(fā)BUG介紹

    [原]記錄一個(gè)由于InnoDB MVCC導(dǎo)致的并發(fā)BUG
    發(fā)表于 07-17 09:46

    InnoDB鎖的特點(diǎn)和狀態(tài)查詢

    MySQL探秘(五)InnoDB鎖的類型和狀態(tài)查詢
    發(fā)表于 08-07 11:45

    分布式MySQLInnoDB cluster

    分布式MySQL——InnoDB cluster和性能測試
    發(fā)表于 04-15 08:43

    最有用的mysql問答

    、壓縮、空間函數(shù)等,但是不支持事務(wù)和行級鎖,所以一般用于有大量查詢少量插入的場景來使用,而且myisam不支持外鍵,并且索引和數(shù)據(jù)是分開存儲的。 innodb是基于聚簇索引建立的,和myisam相反它支持事務(wù)、外鍵,并且通過MVCC來支持高并發(fā),索引和數(shù)據(jù)存儲在一起。 2
    的頭像 發(fā)表于 09-30 17:43 ?1664次閱讀
    最有用的<b class='flag-5'>mysql</b>問答

    MySQL中的高級內(nèi)容詳解

    MySQL 進(jìn)階?。?! 本文思維導(dǎo)圖如下。 事務(wù)控制和鎖定語句 我們知道,MyISAM 和 MEMORY 存儲引擎支持表級鎖定(table-level locking),InnoDB 存儲引擎支持行級鎖定
    的頭像 發(fā)表于 03-11 16:55 ?2176次閱讀
    <b class='flag-5'>MySQL</b>中的高級內(nèi)容詳解

    關(guān)于InnoDB的內(nèi)存結(jié)構(gòu)及原理詳解

    除此之外還聊了一下MySQLInnoDB的日志,和兩次寫,總的來說算是一個(gè)入門級別的介紹,這篇文章就來詳細(xì)介紹一下InnoDB的內(nèi)存結(jié)構(gòu)。
    的頭像 發(fā)表于 04-16 16:15 ?2745次閱讀
    關(guān)于<b class='flag-5'>InnoDB</b>的內(nèi)存結(jié)構(gòu)及原理詳解

    innodb究竟是如何存數(shù)據(jù)的

    前言如果你使用過mysql數(shù)據(jù)庫,對它的存儲引擎:innodb,一定不會感到陌生。 眾所周知,在mysql5以前,默認(rèn)的存儲引擎是:myslam。但mysql5之后,默認(rèn)的存儲引擎已經(jīng)
    的頭像 發(fā)表于 10-09 15:41 ?1300次閱讀
    <b class='flag-5'>innodb</b>究竟是如何存數(shù)據(jù)的

    MySQL5.6 InnoDB支持全文檢索

    在早期的 MySQL 中,InnoDB 并不支持全文檢索技術(shù),從 MySQL 5.6 開始,InnoDB 開始支持全文檢索。
    的頭像 發(fā)表于 11-12 15:14 ?1336次閱讀

    剖析MySQL InnoDB存儲原理(上)

    一、MySQL記錄的存儲結(jié)構(gòu): 1、Page的結(jié)構(gòu),如下圖:
    的頭像 發(fā)表于 02-15 15:45 ?412次閱讀
    <b class='flag-5'>剖析</b><b class='flag-5'>MySQL</b> <b class='flag-5'>InnoDB</b>存儲原理(上)

    剖析MySQL InnoDB存儲原理(下)

    一、InnoDB存儲引擎內(nèi)存管理 1.1 概念: Buffer Pool:預(yù)分配的內(nèi)存池; Page:Buffer Pool的最小單位; Free list:空閑Page組成的鏈表;
    的頭像 發(fā)表于 02-15 15:47 ?387次閱讀
    <b class='flag-5'>剖析</b><b class='flag-5'>MySQL</b> <b class='flag-5'>InnoDB</b>存儲原理(下)

    詳細(xì)總結(jié)下InnoDB存儲引擎中行鎖的加鎖規(guī)則

    對于常見的 DML 語句(如 UPDATE、DELETE 和 INSERT ),InnoDB 會自動給相應(yīng)的記錄行加寫鎖
    的頭像 發(fā)表于 02-21 14:02 ?570次閱讀

    MySQL中的InnoDB是什么?

    有許多強(qiáng)大的MySQL存儲引擎可供我們使用,而InnoDB無疑是最受歡迎的存儲引擎之一。它高度可靠和高效,因此它成為5.5版本以后所有MySQL的默認(rèn)存儲引擎也就不足為奇了。
    的頭像 發(fā)表于 04-13 09:09 ?697次閱讀