改文章轉載自楊建勇的個人博客
總結開發過程不斷遇到的查詢瓶頸以及優化
心得:索引的使用是簡單的,應用起來並不是難事。難的是在實際開發中要配合緩存、根據實際的業務設計合理的表結構、以空間換時間對錶結構進行適當的冗餘。查詢的優化並不是一件一蹴而就的事情,是根據業務的不同和業務的發展來實際的優化(未來待續,不斷總結中)
在開始設計表結構的時候就需要提前做好的工作,避免在產生數據之後再增加索引或者變動結構,數據量一多再變動是非常耗時而且耗內存的
前提條件:使用的是MariaDB-10.3.22數據庫,使用的InnoDB引擎
什麼是索引:
索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏所有記錄的引用指針。通俗的說,就是書本上的目錄索引,根據目錄索引可以快速的找到想要的內容
索引類型:
- 唯一索引(UNIQUE)。唯一索引顧名思義,該字段的值必須是唯一的不能重複,唯一索引允許可以有
NULL
值 - 普通索引(INDEX)。普通索引不具備唯一性,數據可以重複,普通索引只是加快查詢速度
- 主鍵索引(PRIMARY KEY)。主鍵索引的查詢速度是最快的,並且每個表只能有一個主鍵列,並且不能爲
NULL
- 複合索引(INDEX多列)
ALTER TABLE table_name ADD INDEX index_name ( column1, column2, column3 )
。索引可以包含一個、兩個或更多個列。兩個或更多個列上的索引被稱作複合索引複合索引專門用於組合搜索,其效率大於索引合併 - 全文索引(FULLTEXT)。全文索引(也稱全文檢索)是目前搜索引擎使用的一種關鍵技術。它能夠利用"分詞技術"等多種算法智能分析出文本文字中關鍵字詞的頻率及重要性,然後按照一定的算法規則智能地篩選出我們想要的搜索結果。在MySQL和MariaDB中,通常情況下只有在
varchar
char
text
的字段類型時纔有效果
索引方式: - BTree。BTree就是B樹索引。經常用的索引方式就是這個。BTree的數據結構以平衡樹的形式來組織,因爲是樹型結構,所以更適合用來處理排序,範圍查找等功能。相對Hash索引,BTree索引在查找單條記錄的速度雖然比不上Hash索引,但是因爲更適合排序等操作,所以他更受用戶的歡迎。畢竟不可能只對數據庫進行單條記錄的操作
- Hash。Hash索引就是哈希索引。在使用中比較少用,它以把數據的索引以Hash形式組織起來,因此當查找某一條記錄的時候,速度非常快。但是因爲是Hash結構,每個鍵只對應一個值,而且是散列的方式分佈。所以他並不支持範圍查找和排序等功能
需要使用索引的原則: - 需要加索引的字段不能設置爲
NULL
,會導致索引失效 - 經常查詢的字段要加上索引
- 與其他表字段建立關聯關係的字段應該建立索引
- 頻繁更新的字段不適合建立索引
WHERE
條件用不到的字段不建立索引
不需要使用索引的原則:<>
和!=
會使索引失效IS NULL
和IS NOT NULL
會導致索引失效OR
會使索引失效
實際操作。用兩張商品表來分析product
表和product_copy
表。兩張表的數據量有1000萬上下
product
表。改表字段都設置成NULL
MariaDB [test]> desc product;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| size | int(11) | YES | | NULL | |
| stock | int(11) | YES | | NULL | |
| color | int(11) | YES | | NULL | |
| code | varchar(255) | YES | | NULL | |
| brand | varchar(255) | YES | | NULL | |
| sale_channel | varchar(255) | YES | | NULL | |
| type_id | int(11) | YES | | NULL | |
| style | int(11) | YES | | NULL | |
| create_time | int(11) | YES | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.024 sec)
product_copy
表結構。該表字段都設置成NOT NULL
MariaDB [test]> desc product_copy;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | MUL | | |
| size | int(11) | NO | | 0 | |
| stock | int(11) | NO | | 0 | |
| color | int(11) | NO | | 0 | |
| code | varchar(255) | NO | | | |
| brand | varchar(255) | NO | | | |
| sale_channel | varchar(255) | NO | | | |
| type_id | int(11) | NO | | 0 | |
| style | int(11) | NO | | 0 | |
| create_time | int(11) | NO | | 0 | |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.001 sec)
再看下索引(product_copy
表):在字段stock
color
創建了普通索引
MariaDB [test]> show index from product_copy;
+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| product_copy | 0 | PRIMARY | 1 | id | A | 11764836 | NULL | NULL | | BTREE | | |
| product_copy | 1 | stock | 1 | stock | A | 202842 | NULL | NULL | | BTREE | | |
| product_copy | 1 | color | 1 | color | A | 180 | NULL | NULL | | BTREE | | |
+--------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.000 sec)
接下來開始對比查詢速度
product
表的查詢分析。從下面的分析結果可以看出沒有加索引的情況下進行了全表查詢
MariaDB [test]> explain select * from product where stock = 1;
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | product | ALL | NULL | NULL | NULL | NULL | 9660796 | Using where |
+------+-------------+---------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.000 sec)
product_copy
表的查詢分析。從下面的分析結果中可以看出,在stock
字段上添加了普通索引,查詢的總條數只有113條。大大的增加了查詢速度
MariaDB [test]> explain select * from product_copy where stock = 1;
+------+-------------+--------------+------+---------------+--------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+--------------+------+---------------+--------+---------+-------+------+-------+
| 1 | SIMPLE | product_copy | ref | xsdxxx | xsdxxx | 4 | const | 113 | |
+------+-------------+--------------+------+---------------+--------+---------+-------+------+-------+
1 row in set (0.013 sec)
再來看下面使用了!=
<>
OR
的分析。可以看出使用這些條件將會使索引失效而進行全表查詢
MariaDB [test]> explain select * from product_copy where stock != 1;
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | product_copy | ALL | xsdxxx | NULL | NULL | NULL | 11764836 | Using where |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
1 row in set (0.001 sec)
MariaDB [test]> explain select * from product_copy where stock <> 1;
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | product_copy | ALL | xsdxxx | NULL | NULL | NULL | 11764836 | Using where |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
1 row in set (0.000 sec)
MariaDB [test]> explain select * from product_copy where stock = 1 or name like "ab";
+------+-------------+--------------+------+---------------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+--------------+------+---------------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | product_copy | ALL | xsdxxx,product_copy | NULL | NULL | NULL | 11764836 | Using where |
+------+-------------+--------------+------+---------------------+------+---------+------+----------+-------------+
1 row in set (0.000 sec)
再來看使用了IS NULL
IS NOT NULL
的分析。因爲設置字段爲NOT NULL
,所以分析IS NULL
的時候直接顯示成Impossible WHERE
。但是可以看出IS NOT NULL
也是進行全表查詢,沒有使用索引
MariaDB [test]> explain select * from product_copy where stock is null;
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+------+-------------+-------+------+---------------+------+---------+------+------+------------------+
1 row in set (0.000 sec)
MariaDB [test]> explain select * from product_copy where stock is not null;
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
| 1 | SIMPLE | product_copy | ALL | stock | NULL | NULL | NULL | 11764836 | Using where |
+------+-------------+--------------+------+---------------+------+---------+------+----------+-------------+
1 row in set (0.000 sec)
配合緩存的使用
1、可以把經常查詢的數據緩存到內存。更新數據的時候再同步到內存
未完待續。。。不斷的總結