Mysql某個表有近千萬數據,CRUD比較慢,如何優化?

登西麥瑞


1. 選取最適用的字段屬性

表中字段的寬度設得儘可能小:char 的上限為 255 字節(固定佔用空間),varchar 的上限 65535 字節(實際佔用空間),text 的上限為 65535。

儘量把字段設置為 NOT NULL,執行查詢的時候,數據庫不用去比較 NULL 值。

2. 使用連接(JOIN)來代替子查詢 (Sub-Queries)

連接(JOIN)之所以更有效率一些,是因為 MySQL 不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作(聯合查詢的條件加索引更快)。

3. 使用聯合 (UNION) 來代替手動創建的臨時表

把需要使用臨時表的兩條或更多的 SELECT 查詢合併的一個查詢中。

SELECT Name, Phone FROM client UNION SELECT Name, BirthDate FROM author UNION SELECT Name, Supplier FROM product;

4. 事務

儘管我們可以使用子查詢(Sub-Queries)、連接(JOIN)和聯合(UNION)來創建各種各樣的查詢,但不是所有的數據庫操作都可以只用一條或少數幾條 SQL 語句就可以完成的。更多的時候是需要用到一系列的語句來完成某種工作。

作用是:

要麼語句塊中每條語句都操作成功,要麼都失敗。換句話說,就是可以保持數據庫中數據的一致性和完整性。

事物以 BEGIN 關鍵字開始,COMMIT 關鍵字結束。在這之間的一條 SQL 操作失敗,那麼,ROLLBACK 命令就可以把數據庫恢復到 BEGIN 開始之前的狀態。

5. 鎖定表

儘管事務是維護數據庫完整性的一個非常好的方法,但卻因為它的獨佔性,有時會影響數據庫的性能,尤其是在很大的應用系統中。

由於在事務執行的過程中,數據庫將會被鎖定,因此其它的用戶請求只能暫時等待直到該事務結束。

LOCK TABLE inventory WRITE

SELECT Quantity FROM inventory

WHEREItem='book';

...

UPDATE inventory SET Quantity=11

WHEREItem='book';

UNLOCK TABLES

這裡,我們用一個 SELECT 語句取出初始數據,通過一些計算,用 UPDATE 語句將新值更新到表中。

包含有 WRITE 關鍵字的 LOCK TABLE 語句可以保證在 UNLOCK TABLES 命令被執行之前,不會有其它的訪問來對 inventory 進行插入、更新或者刪除的操作。

6、使用外鍵

鎖定表的方法可以維護數據的完整性,但是它卻不能保證數據的關聯性。這個時候我們就可以使用外鍵。

例如,外鍵可以保證每一條銷售記錄都指向某一個存在的客戶。在這裡,外鍵可以把 customerinfo 表中的 CustomerID 映射到 salesinfo 表中 CustomerID,任何一條沒有合法 CustomerID 的記錄都不會被更新或插入到 salesinfo 中。

CREATE TABLE customerinfo

(

CustomerID INT NOT NULL ,

PRIMARY KEY ( CustomerID )

) TYPE = INNODB;

CREATE TABLE salesinfo

(

SalesID INT NOT NULL,

CustomerID INT NOT NULL,

PRIMARY KEY(CustomerID, SalesID),

FOREIGN KEY (CustomerID) REFERENCES customerinfo

(CustomerID) ON DELETECASCADE

) TYPE = INNODB;

注意例子中的參數 “ON DELETE CASCADE”。該參數保證當 customerinfo 表中的一條客戶記錄被刪除的時候,salesinfo 表中所有與該客戶相關的記錄也會被自動刪除。

如果要在 MySQL 中使用外鍵,一定要記住在創建表的時候將表的類型定義為事務安全表 InnoDB 類型。該類型不是 MySQL 表的默認類型。定義的方法是在 CREATE TABLE 語句中加上 TYPE=INNODB。

7. 使用索引

查詢語句當中包含有 MAX (), MIN () 和 ORDERBY 這些命令的時候,性能提高更為明顯。

索引應建立在那些將用於 JOIN, WHERE 判斷和 ORDER BY 排序的字段上。儘量不要對數據庫中某個含有大量重複的值的字段建立索引。對於一個 ENUM 類型的字段來說,出現大量重複值是很有可能的情況,例如 customerinfo 中的 “province”.. 字段,在這樣的字段上建立索引將不會有什麼幫助;相反,還有可能降低數據庫的性能。

8. 優化的查詢語句

SELECT FROM order WHERE YEAR(OrderDate)<2001;

SELECT FROM order WHERE OrderDate

SELECT FROM inventory WHERE Amount/7<24;

SELECT FROM inventory WHERE Amount<24*7;

避免在查詢中讓 MySQL 進行自動類型轉換,因為轉換過程也會使索引變得不起作用。


一起學全棧


一個千萬級的數據庫的表查詢問題屬於小問題。做好幾個方面就能有很大改觀了:

1、硬件上的升級,換成固態硬盤組成Raid5,內存至少加到32G;

2、數據庫構架升級為多個節點的負載均衡構架,對於Mysql推薦使用第三方數據庫中間軟件完成Mycat;

3、精確的數據庫優化,分為幾個步驟(重點):

  • Mysql配置文件參數的上的優化,具體可百度一下;
  • 在需要查詢和排序的字段上建好索引,把默認值都改為不為Null(數字值的為0,字符串型的為空字符,日期型的為0000-00-00),對於已經為Null的字段,全部用相應的有效值代替;
  • 對於需要進行字符串模糊搜索的,則在數據庫上配套使用ElasticSearch之類的全文索引技術,摒棄數據庫自身的like或是instr等SQL語句式的字符串模糊搜索;
  • 對於需要多表查詢且某個表的數據為靜態數據(解釋:指的是長時間內不會發生變更的數據。例如:商品的分類表,幾乎不經常變更)的,可以先靜態數據存放到緩存中,待應用取出後,再與緩存數據進行拼接完成數據的輸出。舉例說明:

    需求:查詢商品信息時,要根據商品表中的商品分類ID讀取到商品分類的名稱。

    步驟1:將商品分類表的數據以分ID做為緩存索引,全部寫入到MemCache中;

    步驟2:按條件讀取到符合預期的商品列表信息;

    步驟3:循環讀取商品列表,根據列表中每一行的數據的商品分類ID,去讀取MemCache中的分類名稱;
4、針對業務上的優化,對於那些時效性不強的查詢,改為後臺任務定期生成結果,而不是實時查詢的方式完成。例如:找出昨天的日誌記錄,可以使用計劃任務的方式去定期生成昨天的日誌記錄結果,待用戶需要數據時,直接從生成的日誌記錄結果讀取返回即可。此舉,可大大減少數據庫的查詢數量和負載。

老虎哥說事


數據千萬級別之多,佔用的存儲空間也比較大,可想而知它不會存儲在一塊連續的物理空間上,而是鏈式存儲在多個碎片的物理空間上。可能對於長字符串的比較,就用更多的時間查找與比較,這就導致用更多的時間。

  • 可以做表拆分,減少單表字段數量,優化表結構。
  • 在保證主鍵有效的情況下,檢查主鍵索引的字段順序,使得查詢語句中條件的字段順序和主鍵索引的字段順序保持一致。

主要兩種拆分 垂直拆分,水平拆分。


分享到:


相關文章: