MySQL Count(*)提速30倍

壹頁書發表於2014-04-22
MySQL InnoDB是索引組織表,如果根據主鍵大範圍查詢,效率非常低下。
因為主鍵索引中還包含資料,需要掃描大量的塊。

這種場景,如果有一個二級索引,則可以極大的提升查詢速度。

初始化實驗資料(100W記錄)


  1. --建立實驗表
  2. create table t(
  3.     id int primary key,
  4.     num int,
  5.     content varchar(1000)
  6. )engine=innodb;

  7. --建立可以產生隨機字串的函式
  8. DROP FUNCTION IF EXISTS rand_string;
  9. delimiter //
  10. CREATE FUNCTION rand_string(l_num int UNSIGNED,l_type tinyint UNSIGNED)
  11. RETURNS varchar(2000)
  12. BEGIN
  13.  -- Function : rand_string
  14.  -- Author : dbachina#dbachina.com
  15.  -- Date : 2010/5/30
  16.  -- l_num : The length of random string
  17.  -- l_type: The string type
  18.  -- 1.0-9
  19.  -- 2.a-z
  20.  -- 3.A-Z
  21.  -- 4.a-zA-Z
  22.  -- 5.0-9a-zA-Z
  23.  -- :
  24.   -- mysql> select rand_string(12,5) random_string;
  25.   -- +---------------+
  26.   -- | random_string |
  27.   -- +---------------+
  28.   -- | 3KzGJCUJUplw  |
  29.   -- +---------------+
  30.   -- 1 row in set (0.00 sec)
  31.  DECLARE i int UNSIGNED DEFAULT 0;
  32.  DECLARE v_chars varchar(64) DEFAULT '0123456789';
  33.   DECLARE result varchar (2000) DEFAULT '';
  34.  
  35.   IF l_type = 1 THEN
  36.     SET v_chars = '0123456789';
  37.   ELSEIF l_type = 2 THEN
  38.     SET v_chars = 'abcdefghijklmnopqrstuvwxyz';
  39.   ELSEIF l_type = 3 THEN
  40.     SET v_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  41.   ELSEIF l_type = 4 THEN
  42.     SET v_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  43.   ELSEIF l_type = 5 THEN
  44.     SET v_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  45.   ELSE
  46.     SET v_chars = '0123456789';
  47.   END IF;
  48.  
  49.   WHILE i < l_num DO
  50.       SET result = concat( result,substr(v_chars,ceil(rand()*(length(v_chars)-1)),1) );
  51.     SET i = i + 1;
  52.   END WHILE;
  53.   RETURN result;
  54. END;
  55. //
  56. delimiter ;

  57. --插入100W條實驗資料
  58. DROP PROCEDURE IF EXISTS insertTableT;
  59. delimiter //
  60. create procedure insertTableT()
  61. begin
  62.     declare i int;
  63.     set i=0;
  64.     while i<1000000 do
  65.         insert into t values(i,i,rand_string(1000,5));
  66.         set i=i+1;
  67.     end while;
  68. end;
  69. //
  70. delimiter ;

使用主鍵索引的count(*)查詢

  1. mysql> explain select count(*) from t;
  2. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
  3. | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
  4. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
  5. | 1  | SIMPLE      | t     | index | NULL          | PRIMARY | 4       | NULL | 931113 | Using index |
  6. +----+-------------+-------+-------+---------------+---------+---------+------+--------+-------------+
  7. 1 row in set (0.00 sec)

  8. mysql> select count(*) from t;
  9. +----------+
  10. | count(*) |
  11. +----------+
  12. | 1000000  |
  13. +----------+
  14. 1 row in set (6.85 sec)

使用二級索引的count(*)查詢

  1. mysql> create index t_num on t(num);
  2. Query OK, 0 rows affected (9.55 sec)
  3. Records: 0 Duplicates: 0 Warnings: 0

  4. mysql> explain select count(*) from t;
  5. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  6. | id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows   | Extra       |
  7. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  8. | 1  | SIMPLE      | t     | index | NULL          | t_num | 5       | NULL | 931113 | Using index |
  9. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  10. 1 row in set (0.00 sec)

  11. mysql> select count(*) from t;
  12. +----------+
  13. | count(*) |
  14. +----------+
  15. | 1000000  |
  16. +----------+
  17. 1 row in set (0.18 sec)
使用二級索引相比主鍵索引有近30倍的提升。

但是奇怪的是,按照Oracle對於索引的描述,不全為null的內容可以被索引。
很明顯,t表的num欄位可以為空。
插入一個空值之後,居然還可以使用索引??非常奇怪
  1. mysql> insert into t(id,num,content) values(1000001,null,'1');
  2. Query OK, 1 row affected (0.02 sec)

  3. mysql> commit;
  4. Query OK, 0 rows affected (0.04 sec)
包含null值,依然使用索引的count(*)

  1. mysql> explain select count(*) from t;
  2. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  3. | id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows   | Extra       |
  4. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  5. | 1  | SIMPLE      | t     | index | NULL          | t_num | 5       | NULL | 931114 | Using index |
  6. +----+-------------+-------+-------+---------------+-------+---------+------+--------+-------------+
  7. 1 row in set (0.00 sec)

  8. mysql> select count(*) from t;
  9. +----------+
  10. | count(*) |
  11. +----------+
  12. | 1000001  |
  13. +----------+
  14. 1 row in set (0.18 sec)
  對空值的查詢居然還能使用索引?

  1. mysql> explain select * from t where num is null;
  2. +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
  3. | id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
  4. +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
  5. | 1  | SIMPLE      | t     | ref  | t_num         | t_num | 5       | const | 1    | Using index condition |
  6. +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
  7. 1 row in set (0.00 sec)

  8. mysql> select * from t where num is null;
  9. +---------+------+---------+
  10. | id      | num  | content |
  11. +---------+------+---------+
  12. | 1000001 | NULL | 1       |
  13. +---------+------+---------+
  14. 1 row in set (0.00 sec)
不走索引著急,不明白為什麼走索引也著急。
 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1147227/,如需轉載,請註明出處,否則將追究法律責任。

相關文章