一次 MySQL 千萬級大表的優(yōu)化過程
背景:?使用阿里云RDS for MySQL數(shù)據(jù)庫,有個用戶上網(wǎng)記錄表6個月的數(shù)據(jù)量近2000萬,保留最近一年的數(shù)據(jù)量達到4000萬,查詢速度極慢,日??ㄋ溃瑖乐赜绊憳I(yè)務(wù)。老系統(tǒng),當時設(shè)計系統(tǒng)的人大概是大學(xué)沒畢業(yè),表設(shè)計和SQL語句寫的不僅僅是垃圾,簡直無法直視。原開發(fā)人員都已離職,到我來維護,這就是傳說中的維護不了就跑路,然后我就是掉坑的那個?。?/p>
1方案概述
方案一:優(yōu)化現(xiàn)有MySQL數(shù)據(jù)庫。
優(yōu)點:不影響現(xiàn)有業(yè)務(wù),源程序不需要修改代碼,成本最低。
缺點:有優(yōu)化瓶頸,數(shù)據(jù)量過億就玩完了。
方案二:升級數(shù)據(jù)庫類型,換一種100%兼容MySQL的數(shù)據(jù)庫。
優(yōu)點:不影響現(xiàn)有業(yè)務(wù),源程序不需要修改代碼,你幾乎不需要做任何操作就能提升數(shù)據(jù)庫性能。
缺點:多花錢。
方案三:一步到位,大數(shù)據(jù)解決方案,更換newSQL/noSQL數(shù)據(jù)庫。
優(yōu)點:沒有數(shù)據(jù)容量瓶頸。
缺點:需要修改源程序代碼,影響業(yè)務(wù),總成本最高。
2優(yōu)化現(xiàn)有MySQL數(shù)據(jù)庫
?
? 數(shù)據(jù)庫設(shè)計
? 表字段避免null值出現(xiàn),null值很難查詢優(yōu)化且占用額外的索引空間,推薦默認數(shù)字0代替null。
? 盡量使用INT而非BIGINT,如果非負則加上UNSIGNED(這樣數(shù)值容量會擴大一倍),當然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
? 使用枚舉或整數(shù)代替字符串類型。
? 盡量使用TIMESTAMP而非DATETIME。
? 單表不要有太多字段,建議在20以內(nèi)。
? 用整型來存IP。
??索引設(shè)計
? 索引并不是越多越好,要根據(jù)查詢有針對性的創(chuàng)建,考慮在WHERE和ORDER BY命令上涉及的列建立索引,可根據(jù)EXPLAIN來查看是否用了索引還是全表掃描。
? 應(yīng)盡量避免在WHERE子句中對字段進行NULL值判斷,否則將導(dǎo)致引擎放棄使用索引而進行全表掃描。
? 值分布很稀少的字段不適合建索引,例如"性別"這種只有兩三個值的字段。
? 字符字段只建前綴索引。
? 字符字段最好不要做主鍵。
? 不用外鍵,由程序保證約束。
? 盡量不用UNIQUE,由程序保證約束。
? 使用多列索引時注意順序和查詢條件保持一致,同時刪除不必要的單列索引。
? 使用可存下數(shù)據(jù)的最小的數(shù)據(jù)類型,整型 < date,time < char,varchar < blob*
? 使用簡單的數(shù)據(jù)類型,整型比字符處理開銷更小,因為字符串的比較更復(fù)雜。如,int類型存儲時間類型,bigint類型轉(zhuǎn)ip函數(shù)。
? 使用合理的字段屬性長度,固定長度的表會更快。使用enum、char而不是varchar。
? 盡可能使用not null定義字段。
? 盡量少用text,非用不可最好分表。
? 查詢頻繁的列,在where,group by,order by,on從句中出現(xiàn)的列。
? where條件中<,<=,=,>,>=,between,in,以及l(fā)ike 字符串+通配符(%)出現(xiàn)的列。
? 長度小的列,索引字段越小越好,因為數(shù)據(jù)庫的存儲單位是頁,一頁中能存下的數(shù)據(jù)越多越好。
? 離散度大(不同的值多)的列,放在聯(lián)合索引前面。查看離散度,通過統(tǒng)計不同的列值來實現(xiàn),count越大,離散程度越高。
??SQL編寫
? 使用limit對查詢結(jié)果的記錄進行限定。
? 避免select *,將需要查找的字段列出來。
? 使用連接(join)來代替子查詢。
? 拆分大的delete或insert語句。
? 可通過開啟慢查詢?nèi)罩緛碚页鲚^慢的SQL。
? 不做列運算:SELECT id WHERE age + 1 = 10,任何對列的操作都將導(dǎo)致表掃描,它包括數(shù)據(jù)庫教程函數(shù)、計算表達式等等,查詢時要盡可能將操作移至等號右邊。
? SQL語句盡可能簡單:一條SQL只能在一個cpu運算;大語句拆小語句,減少鎖時間;一條大SQL可以堵死整個庫。
? OR改寫成IN:OR的效率是n級別,IN的效率是log(n)級別,in的個數(shù)建議控制在200以內(nèi)。
? 不用函數(shù)和觸發(fā)器,在應(yīng)用程序?qū)崿F(xiàn)。
? 避免%xxx式查詢。
? 少用JOIN。
? 使用同類型進行比較,比如用'123'和'123'比,123和123比。
? 盡量避免在WHERE子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
? 對于連續(xù)數(shù)值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5。
? 列表數(shù)據(jù)不要拿全表,要使用LIMIT來分頁,每頁數(shù)量也不要太大。
??分區(qū)
? 可以讓單表存儲更多的數(shù)據(jù)。
? 分區(qū)表的數(shù)據(jù)更容易維護,可以通過清楚整個分區(qū)批量刪除大量數(shù)據(jù),也可以增加新的分區(qū)來支持新插入的數(shù)據(jù)。另外,還可以對一個獨立分區(qū)進行優(yōu)化、檢查、修復(fù)等操作。
? 部分查詢能夠從查詢條件確定只落在少數(shù)分區(qū)上,速度會很快。
? 分區(qū)表的數(shù)據(jù)還可以分布在不同的物理設(shè)備上,從而搞笑利用多個硬件設(shè)備。
? 可以使用分區(qū)表賴避免某些特殊瓶頸,例如InnoDB單個索引的互斥訪問、ext3文件系統(tǒng)的inode鎖競爭。
? 可以備份和恢復(fù)單個分區(qū)。
? 一個表最多只能有1024個分區(qū)。
? 如果分區(qū)字段中有主鍵或者唯一索引的列,那么所有主鍵列和唯一索引列都必須包含進來。
? 分區(qū)表無法使用外鍵約束。
? NULL值會使分區(qū)過濾無效。
? 所有分區(qū)必須使用相同的存儲引擎。
??分表
? 分表就是把一張大表,按照如上過程都優(yōu)化了,還是查詢卡死,那就把這個表分成多張表,把一次查詢分成多次查詢,然后把結(jié)果組合返回給用戶。
? 分表分為垂直拆分和水平拆分,通常以某個字段做拆分項。比如以id字段拆分為100張表:表名為 tableName_id%100。
? 但:分表需要修改源程序代碼,會給開發(fā)帶來大量工作,極大的增加了開發(fā)成本,故:只適合在開發(fā)初期就考慮到了大量數(shù)據(jù)存在,做好了分表處理,不適合應(yīng)用上線了再做修改,成本太高?。?!而且選擇這個方案,都不如選擇我提供的第二第三個方案的成本低!故不建議采用。
??分庫
? 把一個數(shù)據(jù)庫分成多個,建議做個讀寫分離就行了,真正的做分庫也會帶來大量的開發(fā)成本,得不償失!不推薦使用。
?
3升級數(shù)據(jù)庫
? 開源數(shù)據(jù)庫會帶來大量的運維成本且其工業(yè)品質(zhì)和MySQL尚有差距,有很多坑要踩,如果你公司要求必須自建數(shù)據(jù)庫,那么選擇該類型產(chǎn)品。如tiDB pingcap/tidb,Cubrid Open Source Database With Enterprise Features。
??阿里云POLARDB,POLARDB 是阿里云自研的下一代關(guān)系型分布式云原生數(shù)據(jù)庫,100%兼容MySQL,存儲容量最高可達 100T,性能最高提升至 MySQL 的 6 倍。POLARDB 既融合了商業(yè)數(shù)據(jù)庫穩(wěn)定、可靠、高性能的特征,又具有開源數(shù)據(jù)庫簡單、可擴展、持續(xù)迭代的優(yōu)勢,而成本只需商用數(shù)據(jù)庫的 1/10。
??阿里云OcenanBase,淘寶使用的,扛得住雙十一,性能卓著,但是在公測中,我無法嘗試,但值得期待。
??阿里云HybridDB for MySQL?(原PetaData),云數(shù)據(jù)庫HybridDB for MySQL (原名PetaData)是同時支持海量數(shù)據(jù)在線事務(wù)(OLTP)和在線分析(OLAP)的HTAP(Hybrid Transaction/Analytical Processing)關(guān)系型數(shù)據(jù)庫。
??騰訊云DCDB,DCDB又名TDSQL,一種兼容MySQL協(xié)議和語法,支持自動水平拆分的高性能分布式數(shù)據(jù)庫——即業(yè)務(wù)顯示為完整的邏輯表,數(shù)據(jù)卻均勻的拆分到多個分片中;每個分片默認采用主備架構(gòu),提供災(zāi)備、恢復(fù)、監(jiān)控、不停機擴容等全套解決方案,適用于TB或PB級的海量數(shù)據(jù)場景。
4換大數(shù)據(jù)引擎
? hadoop家族。hbase/hive懟上就是了。但是有很高的運維成本,一般公司可能會考慮成本,沒幾十萬投入是不會有很好的產(chǎn)出的!
? 我選擇了阿里云的MaxCompute配合DataWorks,使用超級舒服,按量付費,成本極低。
? MaxCompute可以理解為開源的Hive,提供SQL/mapreduce/ai算法/python腳本/shell腳本等方式操作數(shù)據(jù),數(shù)據(jù)以表格的形式展現(xiàn),以分布式方式存儲,采用定時任務(wù)和批處理的方式處理數(shù)據(jù)。DataWorks提供了一種工作流的方式管理你的數(shù)據(jù)處理任務(wù)和調(diào)度監(jiān)控。
? 當然你也可以選擇阿里云hbase等其他產(chǎn)品,我這里主要是離線處理,故選擇MaxCompute,基本都是圖形界面操作,大概寫了300行SQL,費用不超過100塊錢就解決了數(shù)據(jù)處理問題。
5總結(jié)
解決問題,方法和思路很重要。其實小編之前也給公司解決過類似的問題,最后不僅節(jié)省了成本,而且查詢性能提升也非??捎^。當時公司數(shù)據(jù)庫(RDS)單表數(shù)據(jù)量已經(jīng)到了20多億條數(shù)據(jù)(可能會一些開發(fā)者不理解,為什么單表會這么大,其實是歷史遺留問題,前面同學(xué)可能沒有考慮到平臺數(shù)據(jù)的暴增后的情況)整庫存儲已經(jīng)20多TB,最高32TB,而這張表就將近10個TB,日增還在幾千萬,而且,查詢性能已經(jīng)嚴重下降,幾張大表的查詢,有時需要幾十秒,甚至幾分鐘,面臨RDS急需升級的問題,而升級費用是單月額外增加6W,這是一筆不小的開銷,問題的解決,迫在眉睫。
同樣的,我們也是先分析問題,然后對數(shù)據(jù)庫做了重新設(shè)計和優(yōu)化,并將業(yè)務(wù)表和統(tǒng)計表做了分庫處理,又對于數(shù)據(jù)量目前一般的表,做了分表,讀寫做了分離,對于數(shù)據(jù)量超大的,日增也較大的幾張表,直接將數(shù)據(jù)遷移到了HDFS,按日分區(qū),使用大數(shù)據(jù)組件查詢。
在業(yè)務(wù)層代碼和數(shù)據(jù)層中間,新增了一個微服務(wù),提供接口調(diào)用。使用SpringBoot+Druid+Mybatis+Hive+Presto+MySQL和一些其他組件搭建了一個框架,支持多數(shù)據(jù)源,多庫,多表的快速查詢和分頁操作,查詢性能提升不少,最主要的,原來的幾張大表在不影響業(yè)務(wù)的前提下,全部遷移到了HDFS存儲,RDS數(shù)據(jù)量也瞬間降了下來,也就不需要在升級RDS了。
下面是關(guān)于數(shù)據(jù)庫優(yōu)化的教程資料 大家可以學(xué)學(xué)