1. 程式人生 > >Mysql千萬級大表優化

Mysql千萬級大表優化

數據庫服務 時間段 系統 時也 導致 slave 如何 1.5 傳統

Mysql的單張表的最大數據存儲量尚沒有定論,一般情況下mysql單表記錄超過千萬以後性能會變得很差。因此,總結一些相關的Mysql千萬級大表的優化策略。

1.優化sql以及索引

1.1優化sql

1、有索引但未被用到的情況(不建議)

(1)避免like的參數以通配符開頭

盡量避免Like的參數以通配符開頭,否則數據庫引擎會放棄使用索引而進行全表掃描。

以通配符開頭的sql語句,例如:select * from t_credit_detail where Flistid like ‘%0‘\G

技術分享圖片

這是全表掃描,沒有使用到索引,不建議使用。

不以通配符開頭的sql語句,例如:select * from t_credit_detail where Flistid like ‘2%‘\G

技術分享圖片

很明顯,這使用到了索引,是有範圍的查找了,比以通配符開頭的sql語句效率提高不少。

(2) 避免where條件不符合最左前綴原則。最左前綴原則:mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整(IN和=可以亂序)。

(3) 使用!= 或 <> 操作符時

盡量避免使用!= 或 <>操作符,否則數據庫引擎會放棄使用索引而進行全表掃描。使用>或<會比較高效。

select * from t_credit_detail where Flistid != ‘2000000608201108010831508721‘\G

技術分享圖片

(4) 避免索引列參與計算

應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。

select * from t_credit_detail where Flistid +1 > ‘2000000608201108010831508722‘\G

技術分享圖片

(5) 避免對字段進行null值判斷

應盡量避免在where子句中對字段進行null值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如: 低效:select * from t_credit_detail where Flistid is null ;

可以在Flistid上設置默認值0,確保表中Flistid列沒有null值,然後這樣查詢: 高效:select * from t_credit_detail where Flistid =0;

(6) 避免使用or來連接條件

應盡量避免在where子句中使用or來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如: 低效:select * from t_credit_detail where Flistid = ‘2000000608201108010831508721‘ or Flistid = ‘10000200001‘;

可以用下面這樣的查詢代替上面的 or 查詢: 高效:select from t_credit_detail where Flistid = ‘2000000608201108010831508721‘ union all select from t_credit_detail where Flistid = ‘10000200001‘;

技術分享圖片

2、避免select *

在解析的過程中,會將‘*‘ 依次轉換成所有的列名,這個工作是通過查詢數據字典完成的,這意味著將耗費更多的時間。

所以,應該養成一個需要什麽就取什麽的好習慣。

3、order by 語句優化

任何在Order by語句的非索引項或者有計算表達式都將降低查詢速度。

方法:1.重寫order by語句以使用索引;2.為所使用的列建立另外一個索引 3.絕對避免在order by子句中使用表達式。

4、GROUP BY語句優化

提高GROUP BY 語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉(如下例,先用where語句過濾掉一部分數據)

低效:

SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT
OR JOB = ‘MANAGER 

高效:

SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT
OR JOB = ‘MANAGER
GROUP by JOB

5、用 exists 代替 in

很多時候用 exists 代替 in 是一個好的選擇: select num from a where num in(select num from b) 用下面的語句替換: select num from a where exists(select 1 from b where num=a.num)

6、使用 varchar/nvarchar 代替 char/nchar

盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

7、能用DISTINCT的就不用GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID

可改為:

SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10

8、能用UNION ALL就不要用UNION

UNION ALL不執行SELECT DISTINCT函數,這樣就會減少很多不必要的資源。(UNION ALL允許重復

9、在Join表的時候使用相當類型的例,並將其索引

如果應用程序有很多JOIN 查詢,你應該確認兩個表中Join的字段是被建過索引的。這樣,MySQL內部會啟動為你優化Join的SQL語句的機制。

而且,這些被用來Join的字段,應該是相同的類型的。例如:如果你要把 DECIMAL 字段和一個 INT 字段Join在一起,MySQL就無法使用它們的索引。對於那些STRING類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.Id_P = Orders.Id_P
ORDER BY Persons.LastName

1.2優化索引

很多數據庫系統性能不理想是因為系統沒有經過整體優化,存在大量性能低下的SQL 語句。這類SQL語句性能不好的首要原因是缺乏高效的索引。沒有索引除了導致語句本身運行速度慢外,更是導致大量的磁盤讀寫操作,使得整個系統性能都受之影響而變差。解決這類系統的首要辦法是優化這些沒有索引或索引不夠好的SQL語句。優化SQL語句的關鍵是盡可能減少語句的logical reads(是指語句執行時需要訪問的單位為8K的數據頁) logical reads 越少,其需要的內存和CPU時間也就越少,語句執行速度就越快。不言而喻,索引的最大好處是它可以極大減少SQL語句的logical reads數目,從而極大減少語句的執行時間。創建索引的關鍵是索引要能夠大大減少語句的logical reads。一個索引好不好,主要看它減少的logical reads多不多。 運行set statistics io命令可以得到SQL語句的logical reads信息。Logical reads中包含該語句從內存數據緩沖區中訪問的頁數和從物理磁盤讀取的頁數。而physical reads表示那些沒有駐留在內存緩沖區中需要從磁盤讀取的數據頁

1對出現在where子句中的字段加索引

全表掃描的性能通常是很差的,要盡量避免。 創建索引的技巧之一是對經常出現在where條件中的字段創建索引

2.組合索引

單字段索引是指只有一個字段的索引,而組合索引指有多個字段構成的索引。如果where語句中有多個字段,那麽可以考慮創建組合索引。組合索引中字段的順序是非常重要的,越是唯一的字段越是要靠前。(根據最左前綴準則)另外,無論是組合索引還是單個列的索引,盡量不要選擇那些唯一性很低的字段。所以如果對單字段進行索引,建議使用set statistics profile(會輸出語句的執行計劃)來驗證索引確實被充分使用。logical reads越少的索引越好。

3.覆蓋索引

覆蓋索引能夠使得語句不需要訪問表僅僅訪問索引就能夠得到所有需要的數據。 因為聚集索引葉子節點就是數據所以無所謂覆蓋與否,所以覆蓋索引主要是針對非聚集索引而言。執行計劃中除了index seek外,還有一個Bookmark Lookup關鍵字。 Bookmark Lookup表示語句在訪問索引後還需要對表進行額外的Bookmark Lookup操作才能得到數據。也就是說為得到一行數據起碼有兩次IO一次訪問索引,一次訪問基本表。 如果語句返回的行數很多,那麽Bookmark Lookup操作的開銷是很大的。 覆蓋索引能夠避免昂貴的Bookmark Lookup操作,減少IO的次數,提高語句的性能。 覆蓋索引需要包含select子句和WHERE子句中出現的所有字段。所以創建覆蓋索引是減少logical reads提升語句性能的非常有用的優化技巧。

問題1,是否值得在identity字段(自增字段)上建立聚集索引。

答案取決於identity 字段如何在語句中使用。如果你經常根據該字段搜索返回很少的行,那麽在其上建立索引是值得的。反之如果identity字段根本很少在語句中使用,那麽就不應該對其建立任何索引。

問題2,一個表應該建立多少索引合適。

如果表的80%以上的語句都是讀操作,那麽索引可以多些。但是不要太多。 特別是不要對那些更新頻繁的表其建立很多的索引。很少表有超過5個以上的索引。過多的索引不但增加其占用的磁盤空間,也增加了SQL Server 維護索引的開銷。

問題4:為什麽SQL Server 在執行計劃中沒有使用你認為應該使用的索引?原因是多樣的。

一種原因是該語句返回的結果超過了表的20%數據,使得SQL Server 認為scanseek更有效。另一種原因可能是表字段的statistics過期了,不能準確反映數據的分布情況。你可以使用命令UPDATE STATISTICS tablename with FULLSCAN來更新它。只有同步的準確的statistics才能保證SQL Server 產生正確的執行計劃。

問題5、什麽使用聚集索引,什麽時候使用非聚集索引

SQL Server 中索引有聚集索引和非聚集索引兩種。它們的主要差別是前者的索引葉子就是數據本身,而後者的葉子節點包含的是指向數據的書簽(即數據行號或聚集索引的key)。來自聚集索引的鍵值由所有非聚集索引作為查找鍵使用,因此存儲在每個非聚集索引的葉條目內。對一個表而言聚集索引只能有一個,而非聚集索引可以有多個。只是聚集索引沒有Bookmark Lookup操作。

在創建聚集索引之前,應先了解您的數據是如何被訪問的。可考慮將聚集索引用於:

1. 包含大量非重復值的列。

2. 使用下列運算符返回一個範圍值的查詢:BETWEEN>>=< <=

3. 被連續訪問的列。

4. 返回大型結果集的查詢。

5. 經常被使用聯接或 GROUP BY 子句的查詢訪問的列;一般來說,這些是外鍵列。 ORDER BY GROUP BY 子句中指定的列進行索引,可以使 SQL Server 不必對數據進行排序,因為這些行已經排序。這樣可以提高查詢性能。

聚集索引不適用於:

1. 頻繁更改的列,這將導致整行移動(因為 SQL Server 必須按物理順序保留行中的數據值)。這一點要特別註意,因為在大數據量事務處理系統中數據是易失的。

1.3緩存

1.4主從復制、主主復制

MySQL主從復制是其最重要的功能之一。主從復制是指一臺服務器充當主數據庫服務器,另一臺或多臺服務器充當從數據庫服務器,主服務器中的數據自動復制到從服務器之中。MySQL主從復制的基礎是主服務器對數據庫修改記錄二進制日誌,從服務器通過主服務器的二進制日誌自動執行更新。

技術分享圖片

主從復制的作用

1、做數據的熱備,作為後備數據庫,主數據庫服務器故障後,可切換到從數據庫繼續工作,避免數據丟失。

2、架構的擴展。業務量越來越大,I/O訪問頻率過高,單機無法滿足,此時做多庫的存儲,降低磁盤I/O訪問的頻率,提高單個機器的I/O性能。(請求發到多部機子上)

3、讀寫分離,使數據庫能支撐更大的並發。一主多從的部署方案,將涉及數據寫的操作放在Master端操作,而將數據讀的操作分散到眾多的Slave當中。降低了Master的負載,提高數據寫入的響應效率;多臺從服務器提供讀,分擔了請求,提高了讀的效率。在報表中尤其重要。由於部分報表sql語句非常的慢,導致鎖表,影響前臺服務。如果前臺使用master,報表使用slave,那麽報表sql將不會造成前臺鎖,保證了前臺速度。

主從復制的原理

1.數據庫有個bin-log二進制文件,記錄了所有sql語句。

2.我們的目標就是把主數據庫的bin-log文件的sql語句復制過來。

3.讓其在從數據的relay-log重做日誌文件中再執行一次這些sql語句即可。

主從復制步驟以及涉及的線程:

一:主庫db的更新事件(update、insert、delete)被寫到binlog
二:從庫發起連接,連接到主庫
三:此時主庫創建一個binlog dump thread線程,把binlog的內容發送到從庫
四:從庫啟動之後,創建一個I/O線程,讀取主庫傳過來的binlog內容並寫入到relay log.
五:還會創建一個SQL線程,從relay log裏面讀取內容,從Exec_Master_Log_Pos位置開始

1.binlog輸出線程:每當有從庫連接到主庫的時候,主庫都會創建一個線程輸出binlog,然後發送binlog內容到從庫。
2.從庫I/O線程:當START SLAVE語句在從庫開始執行之後,從庫創建一個I/O線程,該線程連接到主庫並請求主庫發送binlog裏面的更新記錄到從庫上。(從主庫先傳輸下來)從庫I/O線程讀取主庫的binlog輸出線程發送的更新並拷貝這些更新到本地文件,其中包括relay log文件(再更新relay log)
3.從庫的SQL線程:從庫創建一個SQL線程,這個線程讀取從庫I/O線程寫到relay log的更新事件並執行。

做主從後主服務器掛了怎麽辦

假設發生了突發事件,master宕機,現在的需求是要將從庫提升為主庫,另外一個為從庫:

1.確保所有的從庫的relay log全部更新完畢,在每個從庫上執行stop slave io_thread; show processlist;直到看到Has read all relay log,則表示從庫更新都執行完畢了
2.登陸所有從庫,查看master.info文件,選擇一個從庫為新的主庫
3.登陸該從庫,執行stop slave; 並進入數據庫目錄,刪除master.info和relay-log.info文件, 配置my.cnf文件,開啟log-bin,如果有log-slaves-updates和read-only則要註釋掉,執行reset master
4.創建用於同步的用戶並授權slave,同第五大步驟
5.登錄另外一臺從庫,執行stop slave停止同步
6.根據第七大步驟連接到新的主庫
7.執行start slave;
8.修改新的master數據,測試slave是否同步更新

主從復制延遲

主庫針對讀寫操作,順序寫 binlog,從庫單線程去主庫讀"寫操作的binlog",從庫取到 binlog在本地原樣執行(隨機寫),來保證主從數據邏輯上一致.mysql的主從復制都是單線程的操作,主庫對所有DDL和DML產生 binlog,binlog是順序寫,所以效率很高,slave的Slave_IO_Running線程到主庫取日誌,效率比較高,下一步問題來了,slave的 slave_sql_running線程將主庫的 DDL和DML操作在 slave實施。DML,DDL的IO操作是隨機的,不能順序的,成本高很多,還有可能slave上的其他查詢產生 lock,由於 slave_sql_running也是單線程的,所以 一個 DDL卡住了,需求需求執行一段時間,那麽所有之後的DDL會等待這個 DDL執行完才會繼續執行,這就導致了延遲.由於master可以並發,Slave_sql_running線程卻不可以,所以主庫執行 DDL需求一段時間,在slave執行相同的DDL時,就產生了延遲.

主從同步延遲產生原因
當主庫的TPS並發較高時,產生的DDL數量超過Slave一個 sql線程所能承受的範圍,那麽延遲就產生了,當然還有就是可能與 slave的大型 query語句產生了鎖等待
首要原因:數據庫在業務上讀寫壓力太大,CPU計算負荷大,網卡負荷大,硬盤隨機IO太高
次要原因:讀寫 binlog帶來的性能影響,網絡傳輸延遲
主從同步延遲解決方案
架構方面:mysql壓力變小,延遲自然會變小
1.業務的持久化層的實現采用分庫架構,mysql服務可平行擴展分散壓力
2.單個庫讀寫分離,一主多從,主寫從讀,分散壓力。
3.服務的基礎架構在業務和mysql之間加放 cache層
4.不同業務的mysql放在不同的機器
5.使用比主加更好的硬件設備作slave

1.5讀寫分離

MySQL的主從復制和MySQL的讀寫分離兩者有著緊密聯系,首先部署主從復制,只有主從復制完了,才能在此基礎上進行數據的讀寫分離。讀寫分離就是只在主服務器上寫,只在從服務器上讀,基本的原理是讓主數據庫處理事務性查詢,而從數據庫處理select查詢,數據庫復制被用來把事務性查詢導致的改變更新同步到集群中的從數據庫,即主從復制。

1.基於程序代碼內部實現
在代碼中根據select,insert進行路由分類,這類方法也是目前生產環境應用最廣泛的,優點是性能好,因為在程序代碼中實現,不需要曾加額外的設備作為硬件開支,缺點是需要開發人員來實現,運維人員無從下手。
2.基於中間代理層實現
代理一般位於客戶端和服務器之間,代理服務器接到客戶端請求後通過判斷後轉發到後端數據庫,有兩個代表性程序。
(1)mysql-proxy 為mysql開源項目,通過其自帶的lua腳本進行SQL判斷,雖然是mysql的官方產品,但是mysql官方不建議將其應用到生產環境
(2)Amoeba (變形蟲)由陳思儒開發,曾就職與阿裏巴巴,該程序由java語言進行開發,阿裏巴巴將其應用於生成環境,它不支持事物和存儲過程
通過程序代碼實現mysql讀寫分離自然是一個不錯的選擇,但是並不是所有的應用都適合在程序代碼中實現讀寫分離,像一些大型復雜的java應用,如果在程序代碼中實現讀寫分離對代碼改動就較大,像這種應用一般會考慮使用代理層來實現。

技術分享圖片

MySQL Proxy是一個處於你的client端和MySQL server端之間的簡單程序,它可以監測、分析或改變它們的通信。它使用靈活,沒有限制,常見的用途包括:負載平衡,故障、查詢分析,查詢過濾和修改等等。MySQL Proxy就是這麽一個中間層代理,簡單的說,MySQL Proxy就是一個連接池,負責將前臺應用的連接請求轉發給後臺的數據庫,並且通過使用lua腳本,可以實現復雜的連接控制和過濾,從而實現讀寫分離和負載平衡。對於應用來說,MySQL Proxy是完全透明的,應用則只需要連接到MySQL Proxy的監聽端口即可。當然,這樣proxy機器可能單點失效,但完全可以使用多個proxy機器做為冗余,在應用服務器的連接池配置中配置到多個proxy的連接參數即可。MySQL Proxy更強大的一項功能是實現“讀寫分離”,基本原理是讓主數據庫處理事務性查詢,讓從庫處理SELECT查詢。數據庫復制被用來把事務性查詢導致的變更同步到集群中的從庫。

讀寫分離的好處:
1
)物理服務器增加,負荷增加 2)主從只負責各自的寫和讀,極大程度的緩解X鎖和S鎖爭用 3)從庫可配置myisam引擎,提升查詢性能以及節約系統開銷 4)從庫同步主庫的數據和主庫直接寫還是有區別的,通過主庫發送來的binlog恢復數據,但是,最重要區別在於主庫向從庫發送binlog是異步的,從庫恢復數據也是異步的 5)讀寫分離適用與讀遠大於寫的場景,如果只有一臺服務器,當select很多時,update和delete會被這些select訪問中的數據堵塞,等待select結束,並發性能不高。 對於寫和讀比例相近的應用,應該部署雙主相互復制 6)可以在從庫啟動是增加一些參數來提高其讀的性能,例如--skip-innodb、--skip-bdb、--low-priority-updates以及--delay-key-write=ALL 當然這些設置也是需要根據具體業務需求來定得,不一定能用上 7)分攤讀取。假如我們有1主3從,不考慮上述1中提到的從庫單方面設置,假設現在1 分鐘內有10條寫入,150條讀取。那麽,1主3從相當於共計40條寫入,而讀取總數沒變,因此平均下來每臺服務器承擔了10條寫入和50條讀取(主庫不 承擔讀取操作)。因此,雖然寫入沒變,但是讀取大大分攤了,提高了系統性能。另外,當讀取被分攤後,又間接提高了寫入的性能。所以,總體性能提高了,說白 了就是拿機器和帶寬換性能。MySQL官方文檔中有相關演算公式:官方文檔 見6.9FAQ之“MySQL復制能夠何時和多大程度提高系統性能” 8)MySQL復制另外一大功能是增加冗余,提高可用性,當一臺數據庫服務器宕機後能通過調整另外一臺從庫來以最快的速度恢復服務,因此不能光看性能,也就是說1主1從也是可以的。

1.6分區、垂直分表、水平分表

  • 表分區

表分區其實就是將一張大數據量表中的數據按照不同的分區策略分配到不同的系統分區、硬盤或是不同的服務器設備上,實現數據的均衡分配,這樣做的好處是均衡大數據量數據到不同的存儲介子中,這樣每個分區均攤了一部分數據,然後可以定位到指定的分區中,對數據表進行需求操作,另外,也方便管理水表,比如要刪除某個時間段的數據,就可以按照日期分區,然後直接刪除該日期分區即可,並且效率相對於傳統的DELETE數據效率高很多,這裏以Mysql為例進行說明。

分區和分表的區別:

分區和分表針對的都是數據表,而分表是真正的生成數據表,是將一張大數據量的表分成多個小表實現數據均衡;

分區並不是生成新的數據表,而是將表的數據均衡分攤到不同的硬盤,系統或是不同服務器存儲介子中,實際上還是一張表。

另外,分區和分表都可以做到將表的數據均衡到不同的地方,提高數據檢索的效率,降低數據庫的頻繁IO壓力值,

分區的優點如下:

1、相對於單個文件系統或是硬盤,分區可以存儲更多的數據;

2、數據管理比較方便,比如要清理或廢棄某年的數據,就可以直接刪除該日期的分區數據即可;

3、精準定位分區查詢數據,不需要全表掃描查詢,大大提高數據檢索效率;

4、可跨多個分區磁盤查詢,來提高查詢的吞吐量;

5、在涉及聚合函數查詢時,可以很容易進行數據的合並;

技術分享圖片

表的分區的原理理解起來比較簡單,其實就是把一張大數據量的表,根據分區策略進行分區,分區設置完成之後,由數據庫自身的儲存引擎來實現分發數據到指定的分區中去,正如上圖所示,一張數據表被分成了n個分區,並且分區被放入到不同的介子disk中,每個disk中包含自少一個分區,這就實現了數據的均衡以及通過跨分區介子檢索提高了整體的數據操作IO吞吐率。

表分區的策略:

目前在MySql中支持四種表分區的方式,分別為HASH、RANGE、LIST及KEY,當然在其它的類型數據庫中,分區的實現方式略有不同,但是分區的思想原理是相同,具體如下。

Hash:HASH分區主要用來確保數據在預先確定數目的分區中平均分布,而在RANGE和LIST分區中,必須明確指定一個給定的列值或列值集合應該保存在哪個分區中,而在HASH分區中,MySQL自動完成這些工作,你所要做的只是基於將要被哈希的列值指定一個列值或表達式,以及指定被分區的表將要被分割成的分區數量。

CREATE TABLE t_product_item (
      id int(7) not null,
      title varchar(40) not null,
      subtitle varchar(60) null,
      price double not null,
      imgurl varchar(70) not null,
      producttype int(2) not null,
      createtime datetime not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8
 PARTITION BY HASH(YEAR(createtime))                   //指定Hash的列值或表達式
 PARTITIONS 10                          //指定分區數量

Range:基於屬於一個給定連續區間的列值,把多行分配給同一個分區,這些區間要連續且不能相互重疊使用VALUES LESS THAN操作符來進行定義。

 CREATE TABLE t_product_item (
      id int(7) not null,
      title varchar(40) not null,
      subtitle varchar(60) null,
      price double not null,
      imgurl varchar(70) not null,
      producttype int(2) not null,
      createtime datetime not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8
 PARTITION BY RANGE(producttype) (                     //指定producttype作為range劃分的列,並對值進行區域劃分
      PARTITIONP0 VALUES LESS THAN(2),
      PARTITIONP1 VALUES LESS THAN(4),
      PARTITIONp2 VALUES LESS THAN(6),
      PARTITIONp3 VALUES LESS THAN MAXVALUE
);

List:類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇分區的。LIST分區通過使用“PARTITION BY LIST(expr)”來實現,其中“expr” 是某列值或一個基於某個列值、並返回一個整數值的表達式,然後通過“VALUES IN (value_list)”的方式來定義每個分區,其中“value_list”是一個通過逗號分隔的整數列表。

CREATE TABLE t_product_item (
      id int(7) not null,
      title varchar(40) not null,
      subtitle varchar(60) null,
      price double not null,
      imgurl varchar(70) not null,
      producttype int(2) not null,
      createtime datetime not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8
 PARTITION BY LIST(producttype) (                              //利用枚舉出列值或表達式-->整型集合
      PARTITIONP0 VALUES IN (0,1),                               //利用IN進行分區
      PARTITIONP1 VALUES IN (2,3),
      PARTITIONP2 VALUES IN (4,5),
      PARTITIONP3 VALUES IN (6,7,8,9,10,11,12)
 )

Key:類似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL 服務器提供其自身的哈希函數。必須有一列或多列包含整數值。

表分區的註意:

1、引擎的統一
在對同一個表進行分區時,必須保證數據表的引擎相同,比如:不能對一個分區的表為InnoDB,而另一個分區的引擎為MySIAM。
2、分區關聯性
在對數據表分區時,不能只對數據進行分區,需要連同其對應的索引等屬性一同分區動作,某種程度上可以保持數據屬性的完整。
3、分區的級別
對表進行分區之後,如果某個分區中的數據量依然很大或是增長迅速,那麽你同樣可以再進行子分區操作,將該數據再分區到其它分區中。另外,如果在一個分區中使用了子分區,那麽其它的子分區也必須定義。
4、LIST分區
LIST分區沒有類似如“VALUESLESS THAN MAXVALUE”這樣的包含其他值在內的定義。將要匹配的任何值都必須在值列表中找到。
5、Linear線性
分區策略KEY和HASH都支持使用線性LINEAR的算法,也就是分區的編號是通過2的冪(powers-of-two)算法得到,而不是通過模數算法。

  • 垂直分表、水平分表

垂直分表

1.減少記錄的字段可使內存加載更多行數據,有利於查詢。
2.受限於操作系統中的文件大小限制。
切分原則:把不常用或業務邏輯不緊密或存儲內容比較多的字段分到新的表中可使表存儲更多數據。另外垂直分割可以使得數據行變小,一個數據頁就能存放更多的數據,在查詢時就會減少I/O次數。其缺點是需要管理冗余列,查詢所有數據需要join操作。

水平分表

1.隨著數據量的增大,table行數巨大,查詢的效率越來越低。表很大,分割後可以降低在查詢時需要讀的數據和索引的頁數,同時也降低了索引的層數,提高查詢速度。
2.同樣受限於操作系統中的文件大小限制,數據量不能無限增加,當到達一定容量時,需要水平切分以降低單表(文件)的大小。

切分原則:
增量區間或散列或其他業務邏輯。使用哪種切分方法要根據實際業務邏輯判斷:比如對表的訪問多是近期產生的新數據,歷史數據訪問較少,可以考慮根據時間增量把數據按照一定時間段(比如每年)切分。如果對表的訪問較均勻,沒有明顯的熱點區域,則可以考慮用範圍(比如每500w一個表)或普通Hash或一致性Hash來切分。

全局主鍵問題:原本依賴數據庫生成主鍵(比如自增)的表在拆分後需要自己實現主鍵的生成,因為一般拆分規則是建立在主鍵上的(拆分後仍然要保證主鍵在全局的唯一性),所以在插入新數據時需要確定主鍵後才能找到存儲的表。

Mysql千萬級大表優化