MySQL將行數據和索引數據保存在不一樣的文件中。許多(幾乎全部)其 它數據庫將行數據和索引數據混合保存在用一個文件中。咱們認爲MySQL 選擇對廣範圍的現代系統更好一些。html
保存行數據的另外一種方式是將每一個列的信息保存在單獨的區域(例如SDBM和Focus)。 這樣會對每一個訪問多個列的查詢形成性能問題。由於當訪問多個列時退化得很快,咱們認爲該模型對通常數據庫不合適。mysql
更常見的情形是索引和數據保存在一塊兒(例如Oracle/Sybase)。在這種狀況下,你 能夠在索引的葉級頁找到行的信息。該佈局比較好的事情是在許多狀況下,根據索引緩存得怎樣,能夠保存一個硬盤讀取。該佈局的不利之處表如今:算法
· 表掃描要慢得多,由於你必須通讀索引以得到數據。sql
· 你不能只使用表來檢索查詢的數據。數據庫
· 你須要使用更多的空間,由於你必須從節點複製索引(你不能保存節點上的行)。數組
· 刪除要通過一段時間後才退化表(由於刪除時一般不會更新節點上的索引)。緩存
· 只緩存索引數據會更加困難。服務器
最基本的優化之一是使表在磁盤上佔據的空間儘量小。這能給出巨大的改進,由於磁盤讀入較快,而且在查詢執行過程當中小表的內容被處理時佔用較少的主存儲 器。若是在更小的列上作索引,索引也佔據較少的資源。數據結構
MySQL支持許多不一樣的存儲引擎(表類型)和行格式。對於每一個 表,能夠肯定使用哪一個存儲引擎和索引方法。爲應用程序選擇合適的表格式能夠大大提升性能。參見第 15章:存儲引擎和表類型。多線程
可使用下面的技術可使表的性能更好而且使存儲空間最小:
· 在一些情形下,將一個常常被掃描的表分割爲2個表是有益的。特別是若是它是一個動態格式的 表,而且可能使用一個掃描表時能用來找出相關行的較小靜態格式的表。
全部MySQL列類型能夠被索引。對相關列使用索引是提升SELECT操做性能的最佳途徑。
根據存儲引擎定義每一個表的最大索引數和最大索引長度。參見第 15章:存儲引擎和表類型。 全部存儲引擎支持每一個表至少16個索引,總索引長度至少爲256字節。大多數存儲引擎有更高的限 制。
在索引定義中用col_name(length)語 法,你能夠建立一個只使用CHAR或VARCHAR列的第1個length字 符的索引。按這種方式只索引列值的前綴可使索引文件小得多。
MyISAM和InnoDB存儲引擎還支持對BLOB和TEXT列 的索引。當索引一個BLOB或TEXT列時,你必須爲 索引指定前綴長度。例如:
CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));
在MySQL 5.1中,對於MyISAM和InnoDB表,前 綴能夠達到1000字節長。請注意前綴的限制應以字節爲單位進行測量,而CREATE TABLE語句中的前綴長度解釋爲字符數。當爲使用多字節字符集的列指定前綴長度時必定要加以考慮。
還能夠建立FULLTEXT索引。該索引能夠用於全文搜索。只有MyISAM存儲引擎支持FULLTEXT索 引,而且只爲CHAR、VARCHAR和TEXT列。索引老是對整個列 進行,不支持局部(前綴)索引。詳情參見12.7節,「全文搜索功能」。
也能夠爲空間列類型建立索引。只有MyISAM存儲引擎支持空間類型。空間索引使用R-樹。
默認狀況MEMORY(HEAP)存 儲引擎使用hash索引,但也支持B-樹索引。
MySQL能夠爲多個列建立索引。一個索引能夠包括15個列。對於某些列類型,能夠索引列的 前綴(參見7.4.3節,「列索引」)。
多列索引能夠視爲包含經過鏈接索引列的值而建立的值的排序的數組。
MySQL按這樣的方式使用多列索引:當你在WHERE子句中爲索引的第1個 列指定已知的數量時,查詢很快,即便你沒有指定其它列的值。
假定表具備下面的結構:
CREATE TABLE test (
id INT NOT NULL,
last_name CHAR(30) NOT NULL,
first_name CHAR(30) NOT NULL,
PRIMARY KEY (id),
INDEX name (last_name,first_name)
);
name索引是一個對last_name和first_name的 索引。索引能夠用於爲last_name,或者爲last_name和first_name在 已知範圍內指定值的查詢。所以,name索引用於下面的查詢:
SELECT * FROM test WHERE last_name='Widenius';
SELECT * FROM test
WHERE last_name='Widenius' AND first_name='Michael';
SELECT * FROM test
WHERE last_name='Widenius'
AND (first_name='Michael' OR first_name='Monty');
SELECT * FROM test
WHERE last_name='Widenius'
AND first_name >='M' AND first_name < 'N';
然而,name索引不用於下面的查詢:
SELECT * FROM test WHERE first_name='Michael';
SELECT * FROM test
WHERE last_name='Widenius' OR first_name='Michael';
MySQL使用索引提升查詢性能的方式將在7.4.5節,「MySQL如何使用索引」中討論。
索引用於快速找出在某個列中有一特定值的行。不使用索引,MySQL必須從第1條記錄開始然 後讀完整個表直到找出相關的行。表越大,花費的時間越多。若是表中查詢的列有一個索引,MySQL能快速到達一個位置去搜尋到 數據文件的中間,沒有必要看全部數據。若是一個表有1000行,這比順序讀取至少快100倍。注意 若是你須要訪問大部分行,順序讀取要快得多,由於此時咱們避免磁盤搜索。
大多數MySQL索引(PRIMARY KEY、UNIQUE、INDEX和FULLTEXT)在B樹 中存儲。只是空間列類型的索引使用R-樹,而且MEMORY表還支持hash索 引。
字符串自動地壓縮前綴和結尾空格。參見13.1.4節,「CREATE INDEX語法」。
總的來講,按後面的討論使用索引。本節最後描述hash索引(用於MEMORY表)的 特徵。
索引用於下面的操做:
· 快速找出匹配一個WHERE子句的行。
· 刪除行。若是能夠在多個索引中進行選擇,MySQL一般使用找到最少行的索引。
· 當執行聯接時,從其它表檢索行。
· 對具體有索引的列key_col找出MAX()或MIN()值。 由預處理器進行優化,檢查是否對索引中在key_col之 前發生全部關鍵字元素使用了WHERE key_part_# = constant。在這種狀況下,MySQL爲 每一個MIN()或MAX()表達式執行一次關鍵字查找,並用常數替換它。若是全部表達式替換爲常 量,查詢當即返回。例如:
· SELECT MIN(key_part2),MAX(key_part2)
· FROM tbl_name WHERE key_part1=10;
· 若是對一個可用關鍵字的最左面的前綴進行了排序或分組(例如,ORDER BY key_part_1,key_part_2),排序或分組一個表。若是全部關鍵字元素後面有DESC, 關鍵字以倒序被讀取。參見7.2.12節,「MySQL如何優化ORDER BY」。
· 在一些狀況中,能夠對一個查詢進行優化以便不用查詢數據行便可以檢索值。若是查詢只使用來自某個表的數字 型而且構成某些關鍵字的最左面前綴的列,爲了更快,能夠從索引樹檢索出值。
· SELECT key_part3 FROM tbl_name
· WHERE key_part1=1
假定你執行下面的SELECT語句:
mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
若是col1和col2上存在一個多列索引,能夠直接取出相應行。若是col1和col2上 存在單列索引,優化器試圖經過決定哪一個索引將找到更少的行來找出更具限制性的索引而且使用該索引取行。
若是表有一個多列索引,優化器可使用最左面的索引前綴來找出行。例如,若是有一個3列索引(col1,col2,col3), 則已經對(col1)、(col1,col2)和(col1,col2,col3)上 的搜索進行了索引。
若是列不構成索引最左面的前綴,MySQL不能使用局部索引。假定有下面顯示的SELECT語 句。
SELECT * FROM tbl_name WHERE col1=val1;
SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
SELECT * FROM tbl_name WHERE col2=val2;
SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;
若是 (col1,col2,col3)有 一個索引,只有前2個查詢使用索引。第3個和第4個查詢確實包括索引的 列,但(col2)和(col2,col3)不 是 (col1,col2,col3)的 最左邊的前綴。
也能夠在表達式經過=、>、>=、<、<=或 者BETWEEN操做符使用B-樹索引進行列比較。若是LIKE的參數 是一個不以通配符開頭的常量字符串,索引也能夠用於LIKE比較。例如,下面的SELECT語句使 用索引:
SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%';
SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%';
在第1個語句中,只考慮帶'Patrick' <=key_col < 'Patricl'的行。在第2個語句中,只考慮帶'Pat' <=key_col < 'Pau'的行。
下面的SELECT語句不使用索引:
SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%';
SELECT * FROM tbl_name WHERE key_col LIKE other_col;
在第一條語句中,LIKE值以一個通配符字符開始。在第二條語句中,LIKE值不是一個常 數。
若是使用... LIKE '%string%'而且string超 過3個字符,MySQL使用Turbo Boyer-Moore算法初始化字符串的模式而後使用該模式來更快地進行搜索。
若是col_name被索引,使用col_name IS NULL的搜索將使用索引。
任何不跨越WHERE子句中的全部AND級的索引不用於優化查詢。換句話說,爲了可以使用索 引,必須在每一個AND組中使用索引前綴。
下面的WHERE子句使用索引:
... WHERE index_part1=1 AND index_part2=2 AND other_column=3
/* index = 1 OR index = 2 */
... WHERE index=1 OR A=10 AND index=2
/* optimized like "index_part1='hello'" */
... WHERE index_part1='hello' AND index_part3=5
/* Can use index on index1 but not on index2 or index3 */
... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3;
下面的WHERE子句不使用索引:
/* index_part1 is not used */
... WHERE index_part2=1 AND index_part3=2
/* Index is not used in both parts of the WHERE clause */
... WHERE index=1 OR A=10
/* No index spans all rows */
... WHERE index_part1=1 OR index_part2=10
有時MySQL不使用索引,即便有可用的索引。一種情形是當優化器估計到使用索引將須要MySQL訪 問表中的大部分行時。(在這種狀況下,表掃描可能會更快些,由於須要的搜索要少)。然而,若是此類 查詢使用LIMIT只搜索部分行,MySQL則使用索引,由於它能夠更快地找到幾行並在結果中返 回。
Hash索引還有一些其它特徵:
· 它們只用於使用=或<=>操做符的等式比較(但很 快)。它們用於比較 操做符,例如發現範圍值的<。
· 優化器不能使用hash索引來加速ORDER BY操做。(該類索引不能用來按順序搜索下一個條目)。
· MySQL不能肯定在兩個值之間大約有多少行(這被範圍優化器 用來肯定使用哪一個索引)。若是你將一個MyISAM表改成hash-索 引的MEMORY表,會影響一些查詢。
· 只能使用整個關鍵字來搜索一行。(用B-樹索引,任何關鍵字的 最左面的前綴可用來找到行)。
爲了使硬盤I/O最小化,MyISAM存儲引擎使用一個被許多數據庫管理系統使用的策略。它 使用一個緩存機制將常常訪問的表鎖在內存中:
· 對於索引塊,維護一個稱之爲鍵高速緩衝(或鍵 高速緩衝區)的特殊結構。該結構包含大量塊緩存區,其中放置了最經常使用的索引塊。
· 對於數據塊,MySQL不使用特殊緩存。而使用原生的操做系統文件系統的緩存。
本節首先描述了MyISAM鍵高速緩衝的基本操做。而後討論了提升 鍵高速緩衝性能並使你更好地控制緩存操做的最新的更改:
· 多個線程能夠並行訪問緩存。
· 能夠設置多個鍵高速緩衝,並將表索引指定給具體緩存。
可使用key_buffer_size系統變量控制 鍵高速緩衝的大小。若是該變量設置爲零,不使用鍵高速緩衝。若是key_buffer_size值過小不能分配最小數量 的塊緩存區(8),也不使用 鍵高速緩衝。
若是鍵高速緩衝不工做,只使用操做系統提供的原生文件系統緩存區訪問索引文件。(換句話說,使用與表數據塊相同的策略表 來訪問索引塊)。
索引塊是一個連續的訪問MyISAM索引文件的單位。一般一個索引塊的大小等於索引B-樹節 點的大小。(在硬盤上使用B-樹數據結構表示索引。樹底部的節點爲葉子節點。葉子節點上面的節點爲 非葉子節點)。
鍵高速緩衝結構中的全部塊緩存區大小相同。該大小能夠等於、大於或小於表索引塊的大小。一般這兩個值中的一個是另外一個的幾倍。
當必須訪問表索引塊中的數據時,服務器首先檢查是否它能夠用於鍵高速緩衝中的某些塊緩存區。若是適用,服務器訪問鍵高速緩衝中的數據而不是硬盤上的數據。 也就是說,從緩存讀取或寫入緩存,而不是從硬盤讀寫。不然,服務器選擇一個包含一個不一樣的表索引塊的緩存塊緩存區,並用須要的表索引塊的拷貝替換那裏的數 據。一旦新的索引塊位於緩存中,能夠訪問索引數據。
若是用於替換的塊已經被修改了,塊被視爲「髒了」。在這種狀況下,在替換前,其內容被刷新到它來自的表索引。
一般服務器聽從LRU(最近最少使用)策 略:當選擇一個塊用於替換時,它選擇最近最少使用的索引塊。爲了使該選擇更容易, 鍵高速緩衝模塊維護全部使用的塊的專門隊列(LRU鏈)。 當訪問塊時,它被放到隊列最後。當塊須要替換時,隊列開頭的塊是最近最少使用的塊,併成爲第1個候選者。
在如下條件下,線程能夠同時訪問鍵高速緩衝緩存區:
· 沒有被更新的緩存區能夠被多個線程訪問。
· 正被更新的緩存區讓須要使用它的線程等待直到更新完成。
· 多個線程能夠發起請求替換緩存塊,只要它們不彼此干擾(也就是說,只要它們須要不一樣的索 引塊,而且使不一樣的緩存塊被替換)。
對鍵高速緩衝的共享訪問容許服務器大大提升吞吐量。
對鍵高速緩衝的共享訪問能夠提升性能但不能徹底消除線程之間的竟爭。它們仍然競爭對鍵高速緩衝緩存區的訪問進行管理的控制結構。爲了進一步下降 鍵高速緩衝訪問竟爭,MySQL 5.1還提供了多個鍵高速緩衝,容許你爲不一樣的鍵高速緩衝分配不一樣的表索引。
有多個鍵高速緩衝時,當爲給定的MyISAM表處理查詢時,服務器必須知道使用哪一個緩存。默認狀況,全部MyISAM表 索引被緩存到默認 鍵高速緩衝中。要想爲具體鍵高速緩衝分配表索引,應使用CACHE INDEX語句(參見13.5.5.1節,「CACHE INDEX語法」)。
例如,下面的語句將表t1、t2和t3的索引分配給名爲hot_cache的 鍵高速緩衝:
mysql> CACHE INDEX t1, t2, t3 IN hot_cache;
+---------+--------------------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------+--------------------+----------+----------+
| test.t1 | assign_to_keycache | status | OK |
| test.t2 | assign_to_keycache | status | OK |
| test.t3 | assign_to_keycache | status | OK |
+---------+--------------------+----------+----------+
能夠用SET GLOBAL參數設置語句或使用服務器啓動選項設置在CACHE INDEX語句中引用的鍵高速緩衝的大小來建立鍵高速緩衝。例如:
mysql> SET GLOBAL keycache1.key_buffer_size=128*1024;
要想刪除鍵高速緩衝,將其大小設置爲零:
mysql> SET GLOBAL keycache1.key_buffer_size=0;
請注意不能刪除默認鍵高速緩衝。刪除默認鍵高速緩衝的嘗試將被忽略:
mysql> set global key_buffer_size = 0;
mysql> show variables like 'key_buffer_size';
+-----------------+---------+
| Variable_name | Value |
+-----------------+---------+
| key_buffer_size | 8384512 |
+-----------------+---------+
鍵高速緩衝變量是結構式系統變量,有一個名和組件。對於keycache1.key_buffer_size,keycache1是 緩存變量名,key_buffer_size是緩存組件。關於引用結構式 鍵高速緩衝系統變量所使用的語法的描述,參見9.4.1節,「結構式系統變量」
默認狀況下,表索引被分配給服務器啓動時建立的主要(默認)鍵高速緩衝。當 鍵高速緩衝被刪除後,全部分配給它的索引被從新分配給默認鍵高速緩衝。
對於一個忙的服務器,咱們建議採用使用三個鍵高速緩衝的策略:
· 佔用爲全部鍵高速緩衝分配的空間的20%的「熱」鍵高速緩衝。該緩存用於頻繁用於搜索但 沒有更新的表。
· 佔用爲全部鍵高速緩衝分配的空間的20%的「冷」鍵高速緩衝。該緩存用於中等大小、大量 修改的表,例如臨時表。
· 佔用鍵高速緩衝空間的20%的「溫」鍵高速緩衝。使用它做爲默認 鍵高速緩衝,默認狀況被全部其它表使用。
使用3個鍵高速緩衝有好處的一個緣由是對一個鍵高速緩衝結構的訪問不會阻擋對其它的訪問。訪問分配給一個緩存的表的查 詢不會與訪問分配給其它緩存的表的查詢競爭。因爲其它緣由也會提升性能:
· 熱緩存只用於檢索查詢,所以其內容決不會被修改。結果是,不管什麼時候須要從硬盤上拉入索引塊,選擇用於替換的緩存塊的內容不須要先刷新。
· 對於分配給熱緩存的索引,若是沒有查詢須要索引掃描,頗有可能對應索引B-樹的非葉子節 點的索引塊仍然在緩存中。
· 當更新的節點位於緩存中而且不須要先從硬盤讀入時,爲臨時表頻繁執行的更新操做會執行得更快。若是臨時表的索引的大小能夠與冷鍵高速緩衝相比較,極可能更 新的節點位於緩存中。
CACHE INDEX在一個表和 鍵高速緩衝之間創建一種聯繫,但每次服務器重啓時該聯繫被丟失。若是你想要每次服務器重啓時該聯繫生效,一個發辦法是使用選項文件:包括配置 鍵高速緩衝的變量設定值,和一個init-file選項用來命名包含待執行的CACHE INDEX語句的一個文件。例如:
key_buffer_size = 4G
hot_cache.key_buffer_size = 2G
cold_cache.key_buffer_size = 2G
init_file=/path/to/data-directory/mysqld_init.sql
每次服務器啓動時執行mysqld_init.sql中的語句。該文件每行應包含一個SQL語 句。下面的例子分配幾個表,分別對應hot_cache和cold_cache:
CACHE INDEX a.t1, a.t2, b.t3 IN hot_cache
CACHE INDEX a.t4, b.t5, b.t6 IN cold_cache
默認狀況,鍵高速緩衝管理系統採用LRU策略選擇要收回的鍵高速緩衝塊,但它也支持更復雜的方法,稱之爲「中點插入策略」。
當使用中點插入策略時,LRU鏈被分爲兩個部分:一條熱子鏈和一條溫子鏈。兩部分之間的劃分點不固定,但 鍵高速緩衝管理系統關注溫部分不「過短」,老是包含至少key_cache_division_limit比 例的 鍵高速緩衝塊。key_cache_division_limit是結構式 鍵高速緩衝變量的一個組件,所以其值是一個能夠根據每一個緩存進行設置的參數。
當一個索引塊從表中讀入鍵高速緩衝,它被放入溫子鏈的末端。通過必定量的訪問後(訪問塊), 它被提高給熱子鏈。目前,須要用來提高一個塊(3)的訪問次數與全部索引塊的相同。
提高到熱子鏈的塊被放到子鏈的末端。塊而後在該子鏈中循環。若是塊在子鏈的開頭停留足夠長的時間,它被降到溫鏈。該時間由鍵高速緩衝key_cache_age_threshold組 件的值肯定。
對於包含N個塊的 鍵高速緩衝,閾值表示,熱子鏈開頭的沒有在最後N *key_cache_age_threshold/100次訪問中被訪問的塊將被移動到溫子鏈開頭。該塊而後變爲 第1個擠出的候選者,由於替換的塊老是來自溫子鏈的開頭。
中點插入策略容許你將更有價值的塊老是在緩存中。若是你想使用簡單的LRU策略,使key_cache_division_limit值 保持其默認值100。
若執行的查詢要求索引掃描有效推出全部索引塊對應有數值的高級B-樹節點的緩存,中點插入策略能夠幫助提升性能。要想 避免,必須使用中點插入策略,而key_cache_division_limit設置爲遠小於100。 而後在索引掃描操做過程當中,有數值的常常訪問的節點被保留在熱子鏈中。
若是鍵高速緩衝內有足夠的塊以容納整個索引的塊,或者至少容納對應其非葉節點的塊,則在使用前,預裝含索引塊的鍵高速緩衝頗有意義。預裝能夠以更有效的方 式將表索引塊放入 鍵高速緩衝緩存區中:經過順序地從硬盤讀取索引塊。
不進行預裝,塊仍然根據查詢須要放入鍵高速緩衝中。儘管塊將仍然在緩存中(由於有足夠的緩存區保存它們),它們以隨機方式從硬盤上索取,而不是以順序方 式。
要想將索引預裝到緩存中,使用LOAD INDEX INTO CACHE語句。例如,下面的語句能夠預裝表t1和t2索 引的節點(索引塊):
mysql> LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES;
+---------+--------------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+---------+--------------+----------+----------+
| test.t1 | preload_keys | status | OK |
| test.t2 | preload_keys | status | OK |
+---------+--------------+----------+----------+
IGNORE LEAVES修改器只容許預裝索引非葉節點所用的塊。這樣,上述的語句預裝t1中 的全部索引塊,但只預裝t2中的非葉節點對應的塊。
若是已經使用CACHE INDEX語句爲一個索引分配了一個鍵高速緩衝,預裝能夠將索引塊放入該緩存。不然,索引被裝入默認鍵高速緩衝。
鍵高速緩衝能夠經過更新其參數值隨時從新構建。例如:
mysql> SET GLOBAL cold_cache。key_buffer_size=4*1024*1024;
若是你爲key_buffer_size或key_cache_block_size鍵 高速緩衝組件分配的值與組件當前的值不一樣,服務器將毀掉緩存的舊結構並根據新值建立一個新的。若是緩存包含任何髒的塊,服務器在銷燬前將它們保存到硬盤上 並從新建立緩存。若是你設置其它 鍵高速緩衝參數,則不會發生從新構建。
當從新構建鍵高速緩衝時,服務器首先將任何髒緩存區的內容刷新到硬盤上。以後,緩存內容再也不須要。然而,從新構建並不阻塞須要使用分配給緩存的索引的查 詢。相反,服務器使用原生文件系統緩存直接訪問表索引。文件系統緩存不如使用 鍵高速緩衝有效,所以儘管查詢能夠執行,但速度會減慢。緩存被從新構建後,它又能夠緩存分配給它的索引了,而且索引再也不使用文件系統緩存。
存儲引擎蒐集優化器使用的表的統計信息。表統計基於數數值組,其中數數值組是一系列有相同的關鍵字前綴值的記錄。對於優化器,重要的統計即爲數數值組的平 均大小。
MySQL用下述方式使用平均數數值組:
· 估計必須爲每一個ref訪問讀取多少行
· 估計部分聯接將產生多少行;也就是說,下述形式的操做將產生的行數:
· (...) JOIN tbl_name ON tbl_name.key = expr
隨着索引的平均數數值組大小的增長,索引將更沒有用,由於每一個查找的平均行數增長:爲了讓索引有利於優化目的,最好是每一個索引值對應表內的少許行數。當某 個給定的索引值產生較多行時,索引更加沒有用,MySQL更不可能使用它。
平均數數值組大小與表的集的勢相關,即數數值組的數目。SHOW INDEX語句顯示集的勢值(基於N/S), 其中N是表內的記錄數,S是 平均數數值組大小。該比例產生表內數數值組的大約數。
對於基於<=>比較 操做符的聯接,NULL並不視爲與任何其它值不一樣:NULL <=> NULL,正如對於其它N ,N <=> N。
然而,對於基於=操做符的聯接,NULL與非NULL值不一樣:當expr1或expr2(或 二者)爲NULL時,expr1 = expr2不爲真。這樣影響比較形式tbl_name.key = expr的ref訪問:若是expr當 前的值爲NULL,MySQL不會訪問表,由於比較不能爲真。
對於=比較,表內有多少NULL值並不重要。爲了優化目的,相關 值爲非NULL數值組的平均大小。然而,MySQL目前不容許蒐集或使用該平均大小。
對於MyISAM表,你可使用myisam_stats_method系 統變量部分控制表統計信息的蒐集。該變量有兩個可能的不一樣值,以下所示:
· 當myisam_stats_method爲nulls_equal時, 全部NULL值被視爲相等的(也就是說,它們都造成一個數值組)。
若是NULL數值組大小遠大於平均非NULL數 值組大小,該方法向上傾斜平均數數值組大小。這樣使索引對於優化器來講比它實際爲查找非NULL值 的聯接更加沒有用。結果是,nulls_equal方法會使優化器進行ref訪 問時本應使用索引而沒有使用。
· 當myisam_stats_method爲nulls_unequal時,NULL值 不視爲相同。相反,每一個NULL值造成一個單獨的數值組,大小爲1。
若是你有許多NULL值,該方法向下傾斜平均數數值組大小。若是平均非NULL數值組較大, 統計大小爲1的每一個組的NULL值會使優化器太高估計查找非NULL值 的聯接的索引值。結果是,當其它方法會更好時,nulls_unequal方法會使優化器爲ref查 找使用該索引。
若是你要使用許多使用<=>而不是=的聯接,在比較過程當中NULL值 並不特殊,一個NULL等於另外一個NULL。在這種狀況下,nulls_equal是 合適的統計方法。
myisam_stats_method系統變量有全局和會話值。設置全局值會影響MyISAM 爲全部MyISAM表的統計的蒐集。設置會話值隻影響當前客戶鏈接的統計的蒐集。這說明你能夠強制用給定 的方法從新生成表的統計的蒐集,而不須要由於設置myisam_stats_method的會話值而影響其它客戶。
可使用下面任一方法來從新生成表的統計信息:
· 設置myisam_stats_method,而後執行CHECK TABLE語句
· 執行myisamchk --stats_method=method_name --analyze
· 更改表,使其統計信息不爲最新(例如,插入一行而後刪除它), 而後設置myisam_stats_method並執行ANALYZE TABLE語句
使用myisam_stats_method的一些警告:
你能夠強制顯式蒐集表的統計信息,如上所述。然而,MySQL也能夠自動蒐集統計信息。例如,若是在爲表執行語句的過程 中,一些語句修改了表,MySQL能夠蒐集統計信息。(例如,大批插入或刪除,或者執行ALTER TABLE語句時可能發生)。若是發生,使用myisam_stats_method此 時全部的值蒐集統計信息。這樣,若是你使用一個方法蒐集統計信息,但當後面自動蒐集一個表的統計信息時myisam_stats_method被 設置爲另外一個方法,將使用其它方法。
對於給定的MyISAM表,還不能說出使用哪一個方法來產生統計信息。
myisam_stats_method只適合MyISAM表。其它存儲引擎只有一個方法來 蒐集表的統計信息。一般它接近於nulls_equal方法。
當運行mysqladmin status時,將看見象這樣的一些東西:
Uptime: 426 Running threads: 1 Questions: 11082
Reloads: 1 Open tables: 12
若是你僅有6個表,Open tables值爲12可能有點使人困惑。
MySQL是多線程的,所以許多客戶能夠同時在同一個表上進行查詢。爲了使多個客戶線程在同一個表上有不一樣狀態的 問題減到最小,表被每一個併發進程獨立地打開。這樣須要額外的內存但通常會提升性能。對於MyISAM表,數據文件須要爲每一個打 開表的客戶提供一個額外的文件描述符。(索引文件描述符在全部線程之間共享)。
下一節中提供了該主題的更多的信息。參見7.4.9節,「MySQL如何打開和關閉表」。
table_cache、max_connections和max_tmp_tables系 統變量影響服務器保持打開的文件的最大數量。若是你增長這些值其中的一個或兩個,會遇到操做系統爲每一個進程打開文件描述符的數量強加的限制。許多操做系統 容許你增長打開的文件的限制,儘管該方法隨系統的不一樣而不一樣。查閱操做系統文檔以肯定是否能夠增長限制以及如何操做。
table_cache與max_connections有關。例如,對於200個 並行運行的鏈接,應該讓表的緩存至少有200 * N,這裏N是 能夠執行的查詢的一個聯接中表的最大數量。還須要爲臨時表和文件保留一些額外的文件描述符。
確保操做系統能夠處理table_cache設置所指的打開的文件描述符的數目。若是table_cacheis設 得過高,MySQL可能爲文件描述符耗盡資源並拒絕鏈接,不能執行查詢,而且很不可靠。還必須考慮到MyISAM存 儲引擎須要爲每一個打開的表提供兩個文件描述符。能夠在mysqld_safe中使用--open-files-limit啓 動選項來增長MySQL適用的文件描述符的數量。參見A.2.17節,「文件未找到」。
打開表的緩存能夠保持在table_cache條。 默認爲64;能夠用mysqld的--table_cache選 項來更改。請注意 MySQL能夠臨時打開更多的 表以執行查詢。
在下面的條件下,未使用的表將被關閉並從表緩存中移出:
· 當緩存滿了而且一個線程試圖打開一個不在緩存中的表時。
· 當緩存包含超過table_cache個條目,而且緩存中的表再也不被任何線程使用。
· 當表刷新操做發生。當執行FLUSH TABLES語句或執行mysqladmin flush-tables或mysqladmin refresh命令時會發生。
當表緩存滿時,服務器使用下列過程找到一個緩存入口來使用:
· 當前未使用的表被釋放,以最近最少使用順序。
· 若是緩存滿了而且沒有表能夠釋放,可是一個新表須要打開,緩存必須臨時被擴大。
若是緩存處於一個臨時擴大狀態而且一個表從在用變爲不在用狀態,它被關閉並從緩存中釋放。
對每一個併發訪問打開一個表。這意味着,若是2個線程訪問同一個表或在同一個查詢中訪問表兩次(例 如,將錶鏈接爲自身時),表須要被打開兩次。每一個並行的打開要求在表緩存中有一個條目。任何表的第一次打開佔2個 文件描述符:一個用於數據文件另外一個用於索引文件。表的每一次額外使用僅佔一個數據文件的文件描述符。索引文件描述符在全部線程之間共享。
若是你正用HANDLER tbl_name OPEN語句打開一個表,將爲該線程專門分配一個表。該表不被其它線程共享,只有線程調用HANDLER tbl_name CLOSE或線程終止後才被關閉。表關閉後,被拉回表緩存中(若是緩存 不滿)。參見13.2.3節,「HANDLER語法」。
能夠經過檢查mysqld的狀 態變量Opened_tables肯定表緩存是否過小:
mysql> SHOW STATUS LIKE 'Opened_tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Opened_tables | 2741 |
+---------------+-------+
若是值很大,即便你沒有發出許多FLUSH TABLES語句,也應增長表緩存的大小。參見5.3.3節,「服務器系統變量」和5.3.4節,「服務器狀態變量」。