MySql分区表性能测试及切换案例
背景
互聯網公司的業務變化很快,數據庫表結構設計相對比較直接,很少會在前期設計的很完善。當業務存活并發展起來后,就需要在擴展性、安全性等方面進行改進。
比如,我們一張記錄用戶狀態的表,存儲在RDS for MySql(InnoDB存儲引擎)中。此業務表最近膨脹到1.5億條記錄,存儲占用30多G,且數據還在不斷增長。
雖然目前整體性能表現尚可,但部分操作耗時越來越長,鎖表沖突事件也開始出現??紤]到數據量的快速增長,以及數據庫本身的雪崩特點,我們認為這張表存在很大的性能風險,急需優化。
性能分析
下面我們深入分析MySql InnoDB表數據量大小對CRUD及DDL操作的性能影響:
- 插入數據:由于使用自增列作為主鍵(大數據表比較推薦這種方式,索引占用存儲空間會大幅減少),增加業務數據的消耗時間為O(1)。但由于各索引數據需要排序,因此增加索引數據的消耗時間為O(logN);
- 查詢數據:如果通過索引查詢,消耗時間為O(logN);如果不通過索引查詢,消耗時間為O(N);
- 刪除數據:消耗時間與查詢數據表現一致,但需要特別注意鎖的問題。雖然我們經常說InnoDB是行鎖,但這種說法是針對MyISAM的表鎖而言。實際上,InnoDB的“行鎖”是“索引鍵鎖”,其鎖機制的實現是基于索引鍵實現的。如果刪除數據時沒有匹配到索引鍵,或即使匹配上索引鍵,但如果索引鍵匹配的數據記錄過多,依然會導致大范圍加鎖從而引起訪問沖突,極大的降低系統性能;
- 更新數據:消耗時間和加鎖分析基本與刪除數據一致,只是多了個索引的重排序;
- DDL操作:MySql 從5.6開始支持在線DDL操作。其原理是進行DDL操作前,將原始數據文件拷貝到新文件中,然后執行DDL操作。同時,將DML操作日志存儲到緩存中,待DDL執行完成后再執行到新的數據文件中。那么,當表數據量較大時,數據文件拷貝時間必然較長,如果這期間存在大量的DML操作,超過緩存上限,則DDL操作會失敗。
- DML(Data Manipulation Language),數據操縱語言,包括UPDATE、INSERT、DELETE,表示對數據記錄層面的操作
- DDL(Data Definition Language),數據定義語言,主要包括CREATE、ALTER、DROP等,表示對表結構層面的操作
優化方案
通過上面的分析,我們可以看出,InnoDB表數據量過大對各種操作都存在較大的性能影響。針對這些問題,有以下三種優化方案:
將此業務表切換到分布式數據庫產品中
此方案最簡單直接。但我們的業務中,只有此表數據量較大且需要查詢詳細單據。僅為了一張表就引入一種存儲機制,考慮到運維和經濟成本,總覺得不劃算。另外,此表還與其他表有一定的聯合查詢操作,分離出去后會增加應用層的復雜度;
對此業務表分庫分表
分庫分表是處理大數據表的利器。但我們數據庫系統的CPU和IO資源很富余(CPU僅10%,IOPS僅300多),完全沒有分庫的必要。而分表會使得應用層的修改工作量巨大,代碼的可讀性也會變差。如果為了業務層的邏輯清晰再引入中間件進行代理訪問,又有殺雞用牛刀之感;
使用MySql自帶的分區表
分區表是MySql 5.1引入的特性。根據官網alter-table-partition-operations的介紹,其本質是將分庫分表直接集成到MySql中。我們知道,傳統的分庫分表功能,存在業務層、中間件、數據庫三層:業務層通過調用中間件的API訪問數據庫,不知道具體的物理存儲細節;中間件將一張很大的邏輯表映射到數據庫中多張較小的物理表,并對業務層的訪問請求進行分解后分別放到對應物理庫中執行,再將執行結果在中間件合并后返回給業務層,從而對業務層屏蔽物理存儲細節;數據庫則提供實際的物理存儲。
而MySql的分區表,借助MySql本身的邏輯架構,將分庫分表功能進行了下沉。MySql邏輯架構中的客戶端即對應業務層,Server層對應中間件層,存儲引擎層對應物理存儲層。簡單的說,分庫表就是我們在數據庫層面看到是一張表,但物理上是分成多個文件獨立存儲。
邏輯上分析,分區表的優點很明顯:既能解決大數據量的性能問題,又能對應用層無縫切換。但是,其真實性能表現和穩定性到底怎么樣? 還是得通過測試來驗證。
分區表性能測試
為方便說明,我們將此業務表邏輯結構簡化為只包含以下4列:id(自增列),depart_id(部門ID),user_id(員工ID),mark(員工業績)。
- 分區表中id作為索引而不是主鍵,原因是由于MySql要求分區鍵必須包含主鍵和唯一鍵索引,但實際上id作為自增列并不具有業務意義,不適合作為分區鍵。同時為了保證分區表和非分區表邏輯結構一致,需要在分區表創建id列,并在其上創建索引及自增。
- 盡管分區表的user_id字段在業務上不應為NULL, 但為了避免 depart_id+user_id 的唯一鍵索引被MySql用作聚簇索引,也需要將其設置可為NULL。這樣一方面可以減少分區表占用的存儲空間;另一方面可以避免數據在DML時頻繁的進行頁面分裂、合并、重組,優化寫入性能。
下面分別針對 插入、查詢、DDL、存儲空間 等幾個關鍵性能指標進行測試(更新和刪除數據的性能表現與查詢數據比較一致,不單獨分析)。測試結果如下:
| 插入性能 | 500萬 | 2693 秒 | 3084 秒 |
| 插入性能 | 1000萬 | 5440 秒 | 6277 秒 |
| 插入性能 | 2000萬 | 12753 秒 | 14175 秒 |
| 查詢性能 | 2000萬記錄,分區鍵索引,查詢100萬次 | 126 秒 | 90 秒 |
| 查詢性能 | 2000萬記錄,非分區鍵索引,查詢100萬次 | 691 秒 | 727 秒 |
| DDL性能 | 新增索引 | 66 秒 | 56 秒 |
| 存儲空間 | 500萬(數據+索引) | 255+384 MB | 351+555 MB |
| 存儲空間 | 1000萬(數據+索引) | 511+900 MB | 551+900 MB |
| 存儲空間 | 2000萬(數據+索引) | 1000+1900 MB | 1000+2100 MB |
可以看出:
通過以上的分析,我們得到以下的結論:
針對大數據表,分區表的插入性能、存儲空間與非分區表基本一致,查詢性能在分區鍵上比非分區鍵好,DDL執行時間比非分區表短。
于是,我們認為可以通過將非分區表切換到分區表來降低該數據表存在的性能風險。
分區表切換
為了避免業務中斷,我們參考pt-online-schema-change的模式進行切換。
MySql在5.5及以前的版本,對Online DDL支持不太好,會導致鎖表。因此,percona推出pt-online-schema-change,利用觸發器實現在DDL過程中不會造成讀寫阻塞。
具體步驟如下:
切換完成后,我們進行了一周的性能觀察:CPU維持在10%,IOPS有8%左右的下降,存儲空間有3%的上升。
單純從整體的性能指標來看,切換前后變化并不是特別明顯。但之前耗時越來越長的操作,耗時穩定了下來,鎖表沖突事件也基本沒有再出現。通過PARTITIONS的分析,最大的一個分區也只有500萬行。即使數據量再擴大10倍,最大分區的數據量也才5000萬,對于單個存儲引擎文件來說,這完全無壓力,理論上性能表現也不會出現大幅下滑。但如果放到非分區表,估計業務高峰流量稍微一沖擊,或者硬件性能出現波動(在資源共享的云計算環境中,較為常見),就有崩潰的風險。
目前,分區表已經穩定在生產環境運行了近一個月。
總體來看,切換分區表比較好的解決了我們當前對于數據量快速增長的數據庫性能的擔憂,至少數據量再增長2、3倍應該是能扛住的。但它是否能如我們預期的在高并發下支持10倍數據量(即單表15億記錄)而性能表現依然穩定,仍有待實踐證明。
總結
以上是生活随笔為你收集整理的MySql分区表性能测试及切换案例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle exchange part
- 下一篇: C++中的友元函数和友元类