7.4.6.3 中點(diǎn)插入策略
默認(rèn)地,MySQL 4.1的索引緩存管理系統(tǒng)采用LRU策略來選擇要被清除的緩存區(qū)塊,不過它也支持更完善的方法,叫做"中點(diǎn)插入策略"。
使用中點(diǎn)插入策略時(shí),LRU鏈就被分割成兩半:一個(gè)熱子鏈,一個(gè)溫子鏈。兩半分割的點(diǎn)不是固定的,不過緩存管理系統(tǒng)會(huì)注意不讓溫子鏈部分"太短",總是至少包括全部緩存區(qū)塊的 key_cache_division_limit 比率。key_cache_division_limit 是緩存結(jié)構(gòu)體變量的組件部分,因此它是每個(gè)緩存都可以設(shè)置這個(gè)參數(shù)值。
當(dāng)一個(gè)索引區(qū)塊從表中讀入緩存時(shí),它首先放在溫子鏈的末尾。當(dāng)達(dá)到一定的點(diǎn)擊率(訪問這個(gè)區(qū)塊)后,它就提升到熱子鏈中去。目前,要提升一個(gè)區(qū)塊的點(diǎn)擊率(3)對(duì)每個(gè)區(qū)塊來說都是一樣的。將來,我們會(huì)讓點(diǎn)擊率依靠B樹中對(duì)應(yīng)的索引區(qū)塊節(jié)點(diǎn)的級(jí)別:包含非葉子節(jié)點(diǎn)的索引區(qū)塊所要求的提升點(diǎn)擊率就低一點(diǎn),包含葉子節(jié)點(diǎn)的B索引樹的區(qū)塊的值就高點(diǎn)。
提升起來的區(qū)塊首先放在熱子鏈的末尾。這個(gè)區(qū)塊在熱子鏈內(nèi)一直循環(huán)。如果這個(gè)區(qū)塊在該子鏈開頭位置停留時(shí)間足夠長了,它就會(huì)被降級(jí)回溫子鏈。這個(gè)時(shí)間是由索引緩存結(jié)構(gòu)體變量的組件 key_cache_age_threshold 值來決定的。
這個(gè)閥值是這么描述的,一個(gè)索引緩存包含了 N 個(gè)區(qū)塊,熱子鏈開頭的區(qū)塊在低于 N*key_cache_age_threshold/100 次訪問后就被移動(dòng)到溫子鏈的開頭位置。它又首先成為被刪除的候選對(duì)象,因?yàn)橐惶鎿Q的區(qū)塊還是從溫子鏈的開頭位置開始的。
中點(diǎn)插入策略就能在緩存中總能保持更有價(jià)值的區(qū)塊。如果更喜歡采用LRU策略,只需讓 key_cache_division_limit 的值低于默認(rèn)值 100。
中點(diǎn)插入策略能幫助改善在執(zhí)行需要有效掃描索引,它會(huì)將所有對(duì)應(yīng)到B樹中高級(jí)別的有價(jià)值的節(jié)點(diǎn)推出的查詢時(shí)的性能。為了避免這樣,就必須設(shè)定 key_cache_division_limit 遠(yuǎn)遠(yuǎn)低于100以采用中點(diǎn)插入策略。則在掃描索引操作時(shí)那些有價(jià)值的頻繁點(diǎn)擊的節(jié)點(diǎn)就會(huì)保留在熱子鏈中了。
7.4.6.4 索引預(yù)載入
如果索引緩存中有足夠的區(qū)塊用來保存全部索引,或者至少足夠保存全部非葉子節(jié)點(diǎn),那么在使用前就載入索引緩存就很有意義了。將索引區(qū)塊以十分有效的方法預(yù)載入索引緩存緩沖:從磁盤中順序地讀取索引區(qū)塊。
沒有預(yù)載入,查詢所需的索引區(qū)塊仍然需要被放到緩存中去。雖然索引區(qū)塊要保留在緩存中,因?yàn)橛凶銐虻木彌_,它們可以從磁盤中隨機(jī)讀取到,而非順序地。
想要預(yù)載入緩存,可以使用 LOAD INDEX INTO CACHE 語句。如下語句預(yù)載入了表 t1 和 t2 的索引節(jié)點(diǎn)(區(qū)塊):
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 就只預(yù)載入非葉子節(jié)點(diǎn)的索引區(qū)塊。因此,上述語句加載了 t1 的全部索引區(qū)塊,但是只加載 t2 的非葉子節(jié)點(diǎn)區(qū)塊。
如果使用 CACHE INDEX 語句將索引指向一個(gè)索引緩存,將索引區(qū)塊預(yù)先放到那個(gè)緩存中去。否則,索引區(qū)塊只會(huì)加載到默認(rèn)的緩存中去。
7.4.6.5 索引緩存大小
MySQL 4.1引進(jìn)了對(duì)每個(gè)索引緩存的新變量 key_cache_block_size。這個(gè)變量可以指定每個(gè)索引緩存的區(qū)塊大小。用它就可以來調(diào)整索引文件I/O操作的性能。
當(dāng)讀緩沖的大小和本地操作系統(tǒng)的I/O緩沖大小一樣時(shí),就達(dá)到了I/O操作的最高性能了。但是設(shè)置索引節(jié)點(diǎn)的大小和I/O緩沖大小一樣未必能達(dá)到最好的總體性能。讀比較大的葉子節(jié)點(diǎn)時(shí),服務(wù)器會(huì)讀進(jìn)來很多不必要的數(shù)據(jù),這大大阻礙了讀其他葉子節(jié)點(diǎn)。
目前,還不能控制數(shù)據(jù)表的索引區(qū)塊大小。這個(gè)大小在服務(wù)器創(chuàng)建索引文件 `.MYI' 時(shí)已經(jīng)設(shè)定好了,它根據(jù)數(shù)據(jù)表的索引大小的定義而定。在很多時(shí)候,它設(shè)置成和I/O緩沖大小一樣。在將來,可以改變它的值,并且會(huì)全面采用變量 key_cache_block_size。
7.4.6.6 重建索引緩存
索引緩存可以通過修改其參數(shù)值在任何時(shí)候重建它,例如:
mysql> SET GLOBAL cold_cache.key_buffer_size=4*1024*1024;
如果設(shè)定索引緩存的結(jié)構(gòu)體變量組件變量 key_buffer_size 或 key_cache_block_size 任何一個(gè)的值和它當(dāng)前的值不一樣,服務(wù)器就會(huì)清空原來的緩存,在新的變量值基礎(chǔ)上重建緩存。如果緩存中有任何的'臟'索引塊,服務(wù)器會(huì)先把它們保存起來然后才重建緩存。重新設(shè)定其他的索引緩存變量并不會(huì)重建緩存。
重建緩存時(shí),服務(wù)器會(huì)把所有的'臟'緩沖的內(nèi)容先刷新到磁盤中去。之后,緩存的內(nèi)容就無效了。不過,重建的時(shí)候并不阻止那些需要使用指向到緩存中的索引的查詢。相反地,服務(wù)器使用本地文件系統(tǒng)緩存直接訪問數(shù)據(jù)表索引。文件系統(tǒng)緩存不如索引緩存來的高效,因此,可以預(yù)見這時(shí)的查詢會(huì)比較慢。一旦緩存重建完了,指向它的索引又可以使用了,同時(shí)也就不再使用文件系統(tǒng)緩存來訪問索引了。
7.4.7 MySQL 如何統(tǒng)計(jì)打開的表數(shù)量
執(zhí)行命令 mysqladmin status 時(shí),可以看到類似如下結(jié)果:
Uptime: 426 Running threads: 1 Questions: 11082 Reloads: 1 Open tables: 12
如果只有6個(gè)數(shù)據(jù)表時(shí),Open tables 的值確實(shí)12,這可能會(huì)有些疑惑。
MySQL是多線程的,因此可能會(huì)有多個(gè)客戶端同時(shí)發(fā)起查詢某個(gè)表的請(qǐng)求。為了最小化多個(gè)客戶端線程在同一個(gè)表上的不同狀態(tài),針對(duì)每個(gè)并發(fā)的線程單獨(dú)打開數(shù)據(jù)表。這會(huì)占用一些內(nèi)存,但是通常會(huì)提高性能。如果是 MyISAM 表,針對(duì)每個(gè)打開數(shù)據(jù)表的客戶端都要有一個(gè)額外的文件描述符打開數(shù)據(jù)文件的情況(不過索引文件的描述符則可以在所有的線程間共享)。ISAM 存儲(chǔ)引擎則共享這些操作。
想要了解更多,可以看下一章的詳細(xì)內(nèi)容"7.4.8 How MySQL Opens and Closes Tables"。
7.4.8 MySQL 如何打開和關(guān)閉數(shù)據(jù)表
系統(tǒng)變量 table_cache, max_connections 和 max_tmp_tables 影響著服務(wù)器保持打開的文件最大數(shù)量。提高一個(gè)或多個(gè)這些變量,就可以提高操作系統(tǒng)在每次處理時(shí)能打開的文件描述符限制。很多操作系統(tǒng)都可以增加文件打開的數(shù)量限制,不過每個(gè)系統(tǒng)的方法都各不一樣。查閱一下操作系統(tǒng)文檔來判斷是否可以提高限制以及怎么去做。
table_cache 和 max_connections 相關(guān)。例如,有200個(gè)并發(fā)的連接,那么則必須至少有 200 * N 大小的表緩存,N 是在一個(gè)表連接中最大的表數(shù)量。同時(shí)也需要為臨時(shí)表和臨時(shí)文件保留一些額外的文件描述符。
請(qǐng)確認(rèn)操作系統(tǒng)可以通過設(shè)定 table_cache 來處理對(duì)應(yīng)數(shù)量的打開文件。如果 table_cache 設(shè)得太高了,MySQL可能會(huì)用完全部的文件描述符而拒絕新連接,就無法執(zhí)行查詢,變得很不可靠。因此必須要考慮到 MyISAM 存儲(chǔ)引擎要為每個(gè)獨(dú)立打開的表使用兩個(gè)文件描述符。可以在啟動(dòng) mysqld_safe 的時(shí)候增加 --open-files-limit 參數(shù)來提高M(jìn)ySQL打開文件描述符的數(shù)量。詳情請(qǐng)看"A.2.17 File Not Found".
緩存中會(huì)保存 table_cache 個(gè)打開的表會(huì)。它的默認(rèn)值是64;它可以在啟動(dòng) mysqld 的時(shí)候通過修改 --table_cache 參數(shù)來改變。注意,MySQL可能會(huì)臨時(shí)打開比這個(gè)數(shù)更多的表來執(zhí)行查詢。
在以下情況中,一個(gè)不用的表會(huì)被關(guān)閉并且從緩存中被刪除:
- 如果緩存滿了,并且一個(gè)線程試圖打開一個(gè)不在緩存中的表。
- 如果緩存中已經(jīng)包含了不止
table_cache個(gè)表目,并且線程無需再使用該表。 - 當(dāng)發(fā)生刷新表操作。當(dāng)提交一個(gè)
FLUSH TABLES語句或者執(zhí)行mysqladmin flush-tablesormysqladmin refresh命令時(shí)就會(huì)這樣。
當(dāng)表緩存滿了,服務(wù)器遵循以下步驟來分配一個(gè)新的緩存表目:
- 當(dāng)前沒使用的表都釋放,依照最近最少使用的順序。
- 如果有一個(gè)新的表要被打開,但是緩存滿了且沒有表被釋放,緩存就臨時(shí)根據(jù)需要擴(kuò)充一下。
當(dāng)緩存處于臨時(shí)擴(kuò)充狀態(tài),且有表處于從使用變成不使用狀態(tài)時(shí),就關(guān)閉這個(gè)表并且從緩存中釋放。
每個(gè)并行訪問都打開一個(gè)表。這意味著當(dāng)有兩個(gè)線程同時(shí)訪問一個(gè)表,或者一個(gè)線程在同一個(gè)查詢中訪問兩次這個(gè)表(例如,在表連接中連接自己),那么這個(gè)表就需要被打開兩次。每次并發(fā)的打開都在表緩存中請(qǐng)求一個(gè)表目。每個(gè)表第一次打開時(shí)都需要兩個(gè)文件描述符:一個(gè)給數(shù)據(jù)文件,一個(gè)給索引文件。其他新增的對(duì)該表的打開則只需要一個(gè)文件,給數(shù)據(jù)文件。索引文件的描述符在所有的線程間是共享的。
如果使用 HANDLER tbl_name OPEN 語句打開表,則有一個(gè)專用的表對(duì)象給該線程。這個(gè)表對(duì)象不和其他線程共享,并且除非調(diào)用 HANDLER tbl_name CLOSE 語句或者線程結(jié)束,否則它不會(huì)關(guān)閉;這樣的話,它就重新放回表索引中(如果索引還未滿)。詳情請(qǐng)看"14.1.3 HANDLER Syntax"。
可以檢查mysqld 的狀態(tài)變量 Opened_tables 來判斷表索引是否太小了:
mysql> SHOW STATUS LIKE 'Opened_tables'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Opened_tables | 2741 | +---------------+-------+
如果這個(gè)值比較大,不過完全沒必要執(zhí)行一大堆的 FLUSH 語句。只需加大表索引緩存大小即可。詳情請(qǐng)看"5.2.3 Server System Variables"和"5.2.4 Server Status Variables"。
TABLES
7.4.9 在一些數(shù)據(jù)庫中創(chuàng)建太多表的缺點(diǎn)
如果在一個(gè)目錄下有很多的 MyISAM 或 ISAM 表,打開,關(guān)閉,創(chuàng)建等操作就會(huì)變慢。如果在很多不同的表上執(zhí)行SELECT 語句,當(dāng)表緩存滿了之后這就會(huì)有些開銷,因?yàn)槊總€(gè)表都需要被打開,其他的必須關(guān)閉??梢约哟蟊砭彺鎭斫档瓦@個(gè)開銷。



