mysql 索引優化

    索引通常用於在數據規模大時對查詢進行優化的一種機制,對於通常的查詢來講,mysql會去遍歷整個表,來查詢符合要求的結果;若是藉助於索引,mysql會將要索引的字段按照必定的算法進行處理,並生成一個相似於書本目錄的文件存放在相應的位置,這樣在查詢時,mysql會先去查找這些"目錄",而後根據這些"目錄"來快速定位所需記錄的位置,這樣的查找不用遍歷整個記錄集,速度天然會很快,對於海量數據尤爲如此。php

     關於如何建立索引,網上有相關的例子,這裏很少講,可是有一個須要注意,在向存在索引的表中插入數據時,由於要維護索引信息,要比不存在索引的錶慢一些,所以當數據量大時,能夠考慮在插入完數據以後再創建索引。索引分爲單列索引和組合索引,對於這兩種索引,分別介紹其優化問題。mysql

一、單列索引

     單列全部只包含一個字段,一個表能夠包含多個單列索引,可是不要把這個和組合索引混淆。利用如下sql建立測試表:算法

--建立包含單列索引的index_test_single_a表
CREATE TABLE `index_test_a` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` char(255) CHARACTER SET utf8 NOT NULL,
  `content` text CHARACTER SET utf8,
  `num` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `indexName` (`title`),
  UNIQUE KEY `numIndex` (`num`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=latin1;

--建立不包含單列索引的index_test_single_b表
CREATE TABLE `index_test_b` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` char(255) CHARACTER SET utf8 NOT NULL,
  `content` text CHARACTER SET utf8,
  `num` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=latin1;

其中a表包含title的單列索引,b表的title字段不存在索引,可是兩個表都有一個主鍵id,其實主鍵也是索引的一種,這個會在後面詳細解釋。 

寫程序向這兩個表中各導入10000條數據,而後就能夠測試了。sql

1.一、測試查詢索引字段所用的時間,代碼以下:

<?php
	//phpinfo();
	ini_set('max_execution_time', 200);
	$con = mysql_connect("localhost:3306","root","710100");
	if (!$con)
	{	
	  die('Could not connect: ' . mysql_error());
	}
	else{
	        mysql_select_db("test",$con);
		
	
		$sqlA = "select * from index_test_a where title = 'title_4999';";
		
		
		
		$sqlB = "select * from index_test_b where  title = 'title_4999';";
	
		$startTimeA = microtime();
		
		
		
		$result = mysql_query($sqlA) or   die( "Invalid   query:   "   .   mysql_error()); 
		
		$endTimeA = microtime();
		
		echo "A表查詢全部記錄所用時間:".(($endTimeA-$startTimeA)*1000)."毫秒";
		
		echo "<br>";
		
		$startTimeB = microtime();
		
		$result = mysql_query($sqlB) or   die( "Invalid   query:   "   .   mysql_error()); 
		
		$endTimeB = microtime();
		
		echo "B表查詢全部記錄所用時間:".(($endTimeB-$startTimeB)*1000)."毫秒";
		mysql_close($con);
	}
	
	
?>

執行結果以下:

A表查詢全部記錄所用時間:0.624毫秒函數

B表查詢全部記錄所用時間:44.484毫秒性能

能夠看到僅僅10000條記錄的查找差異,時間已經相差了幾十倍,所以對於常常查詢的字段,索引是十分必要的。相應的,若是咱們查詢沒有作索引的字段,那麼是沒有區別的,將以上的sql語句改成以下所示:測試

$sqlA = "select * from index_test_a where content = 'content_4999';";
		
$sqlB = "select * from index_test_b where  content = 'content_4999';";

結果以下:

A表查詢全部記錄所用時間:23.848毫秒優化

B表查詢全部記錄所用時間:24.155毫秒spa

1.二、測試like查詢

在咱們項目中,若是數據量大,則不推薦like查詢,由於其查詢效率比較低,可是對於索引字段來講,like能命中嗎?code

能夠將sql語句改爲以下所示:

$sqlA = "select * from index_test_a where title like '4999%'";
$sqlB = "select * from index_test_b where title like '4999%'";

測試結果以下:

A表查詢全部記錄所用時間:0.488毫秒

B表查詢全部記錄所用時間:25.281毫秒

能夠看到對於模糊查詢來講,若是是前綴匹配,則會命中索引,可是若是咱們將sql改成後綴匹配或者任意匹配,那麼兩者所消耗的查詢時間是一致的:

$sqlA = "select * from index_test_a where title like '%4999'";
$sqlB = "select * from index_test_b where title like '%4999'";

$sqlA = "select * from index_test_a where title like '%4999'";
$sqlB = "select * from index_test_b where title like '%4999'";

A表查詢全部記錄所用時間:44.742毫秒

B表查詢全部記錄所用時間:45.752毫秒

即兩者都沒有命中索引。

 

1.三、測試or語句,將sql改成以下所示:

$sqlA = "select * from index_test_a where  content='content_4999' or title='title_4999';";

$sqlB = "select * from index_test_b where  content='content_4999' or title='title_4999';";

測試結果以下:

A表查詢全部記錄所用時間:49.904毫秒

B表查詢全部記錄所用時間:50.131毫秒

繼續將sql改成以下:

$sqlA = "select * from index_test_a where  id=4999  or title='title_4999';";

$sqlB = "select * from index_test_b where  id=4999  or title='title_4999';";

測試結果以下:

A表查詢全部記錄所用時間:0.86毫秒

B表查詢全部記錄所用時間:47.318毫秒

從上面的結果能夠看到,當or中有一個字段沒有索引的時候,那麼將不會命中索引;反之,若是or運算的全部字段均作了索引,那麼是能夠命中的。

1.四、測試in,將sql語句繼續改成以下所示:

$sqlA = "select title from index_test_a  where title in ('title_4999','title_5000');";
$sqlB = "select title from index_test_b  where title in ('title_4999','title_5000');";

測試結果爲:

A表查詢全部記錄所用時間:0.817毫秒
B表查詢全部記錄所用時間:24.234毫秒

 可見對於索引字段,in也是能夠命中索引的。

1.五、測試<,>,between等,將sql改成以下所示:

$sqlA = "select title from index_test_a  where num < 999;";
$sqlB = "select title from index_test_b  where num < 999;";

測試結果以下:

A表查詢全部記錄所用時間:11.469毫秒

B表查詢全部記錄所用時間:21.728毫秒

可見兩者差異不是很大,所以是沒有命中索引的。

1.六、對於mysql函數,索引的命中,將sql改成以下所示:

$sqlA = "select num from index_test_a  where char(num) in ('999','9999');";
$sqlB = "select num from index_test_b  where  char(num) in ('999','9999');";

獲得的結果以下所示:

A表查詢全部記錄所用時間:11.322毫秒

B表查詢全部記錄所用時間:12.429毫秒

因此若是在條件中使用函數,那麼索引將會失效。

二、組合索引

組合索引包括對多個列的索引,而不是多個單列索引的組合,將表a中的因此改爲(title,num)的組合索引,進行如下測試:

2.一、or測試

將sql語句改爲以下所示:

$sqlA = "select * from index_test_a where  num=4999  or title='title_4999';";
$sqlB = "select * from index_test_b where  num=4999 or title='title_4999';";

 結果以下所示:

A表查詢全部記錄所用時間:52.535毫秒

B表查詢全部記錄所用時間:53.031毫秒

 這時索引沒有命中,索引組合索引的or運算和兩個單列索引的or運算是不一樣的,前者失效然後者依然有效。

2.二、and測試

將sql語句改爲以下所示:

$sqlA = "select * from index_test_a where  num=4999  and title='title_4999';";
$sqlB = "select * from index_test_b where  num=4999 and title='title_4999';";

 結果以下所示:

A表查詢全部記錄所用時間:0.666毫秒

B表查詢全部記錄所用時間:43.042毫秒

 繼續改成以下所示:

$sqlA = "select * from index_test_a where  num=4999 ;";
$sqlB = "select * from index_test_b where  num=4999 ;";

 獲得的結果爲:

A表查詢全部記錄所用時間:39.398毫秒

B表查詢全部記錄所用時間:41.057毫秒

 而改爲以下sql:

$sqlA = "select * from index_test_a where  title='title_'4999 ;";
$sqlB = "select * from index_test_b where  title='title_4999' ;";

 獲得的結果則爲:

A表查詢全部記錄所用時間:0.753毫秒

B表查詢全部記錄所用時間:48.248毫秒

由以上三組結果能夠看出,組合索引是最左前綴匹配的,即條件中要包含第一個索引列,纔會命中索引。

三、    索引的優缺點

利用索引能夠大大加快咱們的搜索,可是維護索引須要額外的開銷,尤爲是當索引較多的時候,大量的數據會很容易帶來索引量的膨脹,所以對於頻繁要用到的查詢,咱們才須要作索引,這樣才能以最小的代價得到最大的性能提高。

相關文章
相關標籤/搜索