高性能mysql第一章——架构
第一章 架構
1.1 mysql邏輯架構
mysql服務器邏輯架構圖如上圖所示。
第一層為連接/線程處理層。每個客戶端連接mysql服務器,都會擁有一個線程。服務器會緩存線程,因此無需為每一個連接新建或釋放線程。mysql5.5以上的版本還提供了線程池,可以用少量線程服務大量連接。當客戶端連接到服務器時,服務器需要對其認證,根據用戶名主機名密碼等信息,確定客戶端是否有查詢/更新某個數據庫內某張表的權限。
第二層為mysql的核心服務功能層。包括查詢解析、分析、優化、緩存以及所有內置函數(日期、時間等),所有跨存儲引擎的功能 都在這一層實現(視圖、存儲過程、觸發器等)。
第三層包含了存儲引擎。存儲引擎負責數據的存儲和提取,每個存儲引擎都有它的優勢與劣勢,服務器通過API與存儲引擎通信。存儲引擎API包含幾十個底層函數,用于執行“開始一個事務”等操作,但是不會解析sql。
1.2 并發控制
討論mysql兩個層面的并發控制:存儲引擎層 與 服務器層
1.2.1 讀寫鎖
在處理并發寫或并發讀寫時,可以通過由兩種類型的鎖組成的鎖系統來解決問題,這兩種鎖就是 共享鎖(讀鎖) 和 排它鎖(寫鎖)。讀鎖是共享的,互相不阻塞,寫鎖是排它的,只有一個線程能進行寫操作,其他讀鎖和寫鎖都是阻塞的。
并且,寫鎖擁有更高的優先級。在一個鎖隊列中,寫鎖可以插到讀鎖的前面。
1.2.2 鎖粒度
一種理想的鎖方式是,盡量只鎖定需要修改的資源,而不是所有資源。鎖定的數據量越小,并發程度越高。
但是鎖也是需要時空開銷的,判斷是否有鎖、加鎖、釋放鎖的操作都需要額外的開銷,如果鎖粒度太小,雖然并發程度高,但系統花大量資源去管理鎖,而不是存取數據,也是得不償失。
鎖策略 就是在 鎖的開銷 和 數據安全性 之間尋求平衡。大多數據庫都是行級鎖 ,而mysql提供了更多鎖的可能性。每種存儲引擎都可以實現自己的鎖策略和鎖粒度。將鎖粒度固定在某一級別,可以為特定的應用場景提供更好的性能,但同時也會失去對一些應用場景的支持。但好在mysql支持多個存儲引擎。
表鎖
表鎖是mysql中最基本、開銷最小的鎖策略。盡管存儲引擎可以設計管理自己的鎖,但mysql服務器還是利用表鎖來實現不同的目的。例如:會為alter table之類的語句使用表鎖,而忽略存儲引擎的鎖機制。
行級鎖
行級鎖是mysql中支持并發量最大、開銷最大的鎖策略。行級鎖只在存儲引擎層實現,服務器層沒有實現。并且服務器層完全不了解存儲引擎層的鎖實現。
1.3 事務
事務是一組原子性的操作,是一個獨立執行單元。事務內的語句,要么全部執行成功,要么全部執行失敗。
start transaction sql1 sql2 sql3 commit用start transaction開始一個事務,三條sql語句,要么全部執行成功commit上去將結果永遠保留,要么roll back撤銷所有修改。
事務的ACID
一個良好的事務處理系統,必須滿足ACID,否則任何事情都有可能發生。比如運行到一半程序崩掉、運行時其他事務也在運行并和當前事務操作同一條數據……
原子性(atomicity): 要么全部執行成功,要么全部執行失敗。
一致性(consistency): 數據庫總是從一個一致性狀態,轉移到另一個一致性狀態中去。
隔離性(isolation): 通常,一個事務在commit之前,所做的修改對其他事務而言是不可見的。
持久性(durability): 一旦事務提交,則會永久保留到數據庫內。
一個數據庫系統想要實現ACID,需要做許多復雜的工作,像鎖粒度升級會增加系統開銷一樣,事務處理過程中的安全性的保證,需要更強的cpu處理能力、更大的內存和磁盤。但我們可自行選擇,如果不需要事務,可以使用非事務型的存儲引擎。
1.3.1 隔離級別
在sql標準中定義了四種隔離級別。每一種都規定了,在事務中的修改,哪些在事務內和事務間是可見的,哪些是不可見的。較低級別的隔離,意味著更好的并發性和較小的開銷。
未提交讀: 事務還未提交,其他事務就可見到更改。這種情況非常危險,不推薦使用。
提交讀: 一個事務從開始到提交結束,其中的修改僅事務本身可見。大多數據庫系統的默認隔離級別都是提交讀(mysql不是)。提交讀又稱作不可重復讀,即同一事務中兩次讀的結果可能是不一樣的。
可重復讀: 此級別保證了同一事務中多次讀的結果是相同的。可重復讀是mysql的默認隔離級別。但可重復讀依然存在問題,即在這個事務執行時,其他事務在該范圍內插入了新數據。
可串行化: 這是最高級別的隔離,會在讀寫的每一行數據上都加鎖,可能會導致大量超時和鎖爭用的問題。
1.3.2 死鎖
當兩個或多個事物占用著自己的資源,而都在等待對方占用的資源時,會形成死鎖。
start transaction update a update b commitstart transaction update b update a commit如果湊巧,兩個事務都執行了第一條語句,在等待第二條語句的執行,那么就會形成死鎖。為了解決這種問題,數據庫系統引入了大量的死鎖檢測和死鎖超時機制。
一種是在事務開始前檢測死鎖的循環依賴,若有可能導致死鎖,則報錯。
一種是發生死鎖時,設置最長等待時間,大于這個時間則放棄資源(不推薦,性能變差)。
一種是發生死鎖時,將擁有最少行級寫鎖的事務回滾。
1.3.3 mysql中的事務
mysql提供了兩種事務型存儲引擎:InnoDB、NDB Cluster。
mysql5.5.5之后,InnDB為mysql的默認存儲引擎。
1. 自動提交 autocommit
mysql默認采用自動提交模式。如果不是顯示的開啟一個事務,任何查詢都會被當作一個事務。在當前連接中,可以通過設置autocommit變量開啟/禁用自動模式。
當autocommit為off時,所有的查詢語句都是在一個事務中,直到遇到commit或rollback命令,該事務結束,同時開啟下一個事務。
有一些mysql命令,在執行之前會強行commit當前的活動事務。例如alter table、lock tables等,具體要看mysql版本。
2. 混合使用存儲引擎
mysql服務器層是不管事務,事務是由下層的存儲引擎層實現的。所以在同一事務中,使用多種存儲引擎是不可靠的。如果在事務中,混合使用了事務型的表與非事務型的表(InnoDB表和MyISAM表),正常情況不會有什么問題,但當需要roll back時,非事務型的表是不可回滾的。
1.4 多版本并發控制
mysql多數事務型存儲引擎實現的都不是簡單的行級鎖,而是引入了多版本并發控制(MVCC) 用于 提高并發量,但每種存儲引擎的實現方式不同。
MVCC是行級鎖的變種,但它在很多情況下避免了加鎖的操作,因此開銷更低。雖然實現機制各不相同,但大都實現了非阻塞的讀操作,寫操作也只鎖定必要的行。
以InnoDB的MVCC為例,InnoDB以 在每行記錄后面保存兩個隱藏的列 來實現MVCC。這兩個列,一列保存 這一行的創建時間,一列保存 這一行的過期時間。但存儲的并不是真正的時間,而是 系統版本號。每開始一個事務,系統版本號就會自增。事務通過自身的系統版本號與這兩個隱藏的列對比,來操作數據。
在可重復度的隔離級別下,InnoDB的MVCC的具體操作如下:
select
InnoDB查找 創建系統版本號 <= 事務系統版本號的行,即在事務開始前就存在的行,或者由事務自身插入或修改的行。
同時該行的過期系統版本號要么未定義,要么 > 事務系統版本號。
insert
新插入的每一行 創建時間都是當前事務的系統版本號。
delete
刪除的每一行 過期時間都是當前事務的系統版本號。
update
插入一條新數據,創建時間是當前事務的系統版本號,將原來行的過期時間置為當前事務的系統版本號。
這兩個額外的系統版本號,使得大多數讀操作都不用加鎖。不足之處是需要額外的存儲空間,要進行更多的判斷以及額外的維護工作。
MVCC只在 提交讀 和 可重復度 兩個隔離級別下工作,因為未提交讀總是能讀取到最新的行,而不是符合當前事務版本的行,而串行化會對所有行加鎖。
1.5 mysql存儲引擎
文件系統中,mysql將每個數據庫(schema)保存為數據目錄下的一個子目錄。創建表時,mysql會在數據庫子目錄下創建一個和表同名的 frm后綴文件保存表的定義。因為mysql使用文件系統的目錄和文件來保存數據庫和表的結構定義,大小寫敏感和操作系統密切相關。
可以看到,該文件存儲著表名、存儲引擎、表行數、創建時間、更新時間、所用字符集等等一些基礎信息。
總結
以上是生活随笔為你收集整理的高性能mysql第一章——架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu18 安装redis-man
- 下一篇: 第一百三十六期:详细讲解 Redis 的