?? manual_performance.html
字號:
<p>當你運行<code>mysqladmin status</code>時,你將看見象這樣的一些東西:
</p>
<pre>Uptime: 426 Running threads: 1 Questions: 11082 Reloads: 1 Open tables: 12
</pre>
<p>如果你僅有6個表,這可能有點令人困惑。 </p>
<p><strong>MySQL</strong>是多線程的,因此它可以同時在同一個表上有許多詢問。為了是2個線程在同一個文件上有不同狀態的問題減到最小,表由每個并發進程獨立地打開。這為數據文件消耗一些內存和一個額外的文件描述符。索引文件描述符在所有線程之間共享。
</p>
<h3><a NAME="Memory_use" HREF="manual_toc.html#Memory_use">10.2.7 MySQL怎樣使用內存</a></h3>
<p>下表指出<code>mysqld</code>服務器使用存儲器的一些方式。在應用的地方,給出與存儲器使用相關的服務器變量的名字。
<ul>
<li>關鍵字緩沖區(變量<code>key_buffer_size</code>)由所有線程分享;當需要時,分配服務器使用的其他緩沖區。見<a HREF="manual_Performance.html#Server_parameters">10.2.3 調節服務器參數</a>。</li>
<li>每個連接使用一些線程特定的空間;一個棧(缺省64K,變量<code>thread_stack</code>)、一個連接緩沖區(變量<code>net_buffer_length</code>)和一個結果緩沖區(變量<code>net_buffer_length</code>)。當需要時,連接緩沖區和結果緩沖區動態地被擴大到<code>max_allowed_packet</code>。當一個查詢正在運行當前查詢的一個拷貝時,也分配字符串。
</li>
<li>所有線程共享同一基存儲器。 </li>
<li>目前還沒有什么是內存映射的(除了壓縮表,但是那是另外一個的故事)。這是因為4GB的32位存儲器空間對最大的數據庫來所不是足夠大的。當一個64位尋址空間的系統變得更普遍時,我們可以為內存映射增加全面的支持。
</li>
<li>每個做順序掃描的請求分配一個讀緩沖區(變量<code>record_buffer</code>)。
</li>
<li>所有聯結均用一遍完成并且大多數聯結可以甚至不用一張臨時表來完成。最臨時的表是基于內存的(HEAP)表。有較大記錄長度(以所有列的長度之和計算)的臨時表或包含<code>BLOB</code>列的表在磁盤上存儲。在<strong>MySQL</strong>版本3.23.2前一個問題是如果一張HEAP表超過<code>tmp_table_size</code>的大小,你得到錯誤<code>The
table tbl_name is full</code>。在更新的版本中,這通過必要時自動將在內存的(HEAP)表轉變為一個基于磁盤(MyISAM)的表來處理。為了解決這個問題,你可以通過設置<code>mysqld</code>的<code>tmp_table_size</code>選項,或通過在客戶程序中設置SQL的<code>SQL_BIG_TABLES</code>選項增加臨時表的大小。見<a HREF="manual_Reference.html#SET_OPTION">7.25<code> SET OPTION</code>句法</a>。在<strong>MySQL</strong>
3.20中,臨時表的最大尺寸是<code>record_buffer*16</code>,因此如果你正在使用這個版本,你必須增加<code>record_buffer</code>值。你也可以使用<code>--big-tables</code>選項啟動<code>mysqld</code>以總將臨時表存儲在磁盤上,然而,這將影響許多復雜查詢的速度。
</li>
<li>大多數做排序的請求分配一個排序緩沖區和一個或二個臨時文件。見<a HREF="manual_Problems.html#Temporary_files">18.5 MySQL在哪兒存儲臨時文件</a>。 </li>
<li>幾乎所有的語法分析和計算都在一家本地存儲器中完成。對小項目沒有內存開銷并且一般的較慢存儲器分配和釋放被避免。內存僅為出乎意料的大字符串分配(這用<code>malloc()</code>和<code>free()</code>完成)。
</li>
<li>每個索引文件只被打開一次,并且數據文件為每個并發運行的線程打開一次。對每個并發線程,分配一個表結構、對每列的列結構和大小為<code>3
* n</code>的一個緩沖區(這里<code>n</code>是最大的行長度,不算<code>BLOB</code>列)。一個<code>BLOB</code>使用5
~ 8個字節加上<code>BLOB</code>數據。 </li>
<li>對每個有<code>BLOB</code>列的表,一個緩沖區動態地被擴大以便讀入更大的<code>BLOB</code>值。如果你掃描一個表,分配與最大<code>BLOB</code>值一樣大的一個緩沖區。
</li>
<li>對所有在用的表的表處理器被保存在一個緩存中并且作為一個FIFO管理。通常緩存有64個入口。如果一個表同時被2個運行的線程使用,緩存為此包含2個入口。見<a HREF="manual_Performance.html#Table_cache">10.2.4 MySQL如何打開和關閉數據庫表</a>。
</li>
<li>一個<code>mysqladmin flush-tables</code>命令關閉所有不在用的表并在當前執行的線程結束時,標記所有在用的表準備被關閉。這將有效地釋放大多數在用的內存。
</li>
</ul>
<p><code>ps</code>和其他系統狀態程序可以報導<code>mysqld</code>使用很多內存。這可以是在不同的內存地址上的線程棧造成的。例如,Solaris版本的<code>ps</code>將棧間未用的內存算作已用的內存。你可以通過用<code>swap
-s</code>檢查可用交換區來驗證它。我們用商業內存漏洞探查器測試了<code>mysqld</code>,因此應該有沒有內存漏洞。
</p>
<h3><a NAME="Internal_locking" HREF="manual_toc.html#Internal_locking">10.2.8 MySQL怎樣鎖定數據庫表</a></h3>
<p><strong>MySQL</strong>中所有鎖定不會是死鎖的。這通過總是在一個查詢前立即請求所有必要的鎖定并且總是以同樣的順序鎖定表來管理。
</p>
<p>對<code>WRITE</code>,<strong>MySQL</strong>使用的鎖定方法原理如下:
<ul>
<li>如果在表上沒有鎖,放一個鎖在它上面。 </li>
<li>否則,把鎖定請求放在寫鎖定隊列中。 </li>
</ul>
<p>對<code>READ</code>,<strong>MySQL</strong>使用的鎖定方法原理如下:
<ul>
<li>如果在表上沒有寫鎖定,把一個讀鎖定放在它上面。 </li>
<li>否則,把鎖請求放在讀鎖定隊列中。 </li>
</ul>
<p>當一個鎖定被釋放時,鎖定可被寫鎖定隊列中的線程得到,然后是讀鎖定隊列中的線程。
</p>
<p>這意味著,如果你在一個表上有許多更改,<code>SELECT</code>語句將等待直到有沒有更多的更改。
</p>
<p>為了解決在一個表中進行很多<code>INSERT</code>和<code>SELECT</code>操作的情況,你可在一張臨時表中插入行并且偶爾用來自臨時表的記錄更新真正的表。
</p>
<p>這可用下列代碼做到: </p>
<pre>mysql> LOCK TABLES real_table WRITE, insert_table WRITE;
mysql> insert into real_table select * from insert_table;
mysql> delete from insert_table;
mysql> UNLOCK TABLES;
</pre>
<p>如果你在一些特定的情況字下區分檢索的優先次序,你可以使用<code>LOW_PRIORITY</code>選項的<code>INSERT</code>。見<a HREF="manual_Reference.html#INSERT">7.14<code> INSERT</code>句法</a>。 </p>
<p>你也能改變在<tt>“mysys/thr_lock.c”</tt>中的鎖代碼以使用一個單個隊列。在這種情況下,寫鎖定和讀鎖定將有同樣優先級,它可能幫助一些應用程序。
</p>
<h3><a NAME="Table_locking" HREF="manual_toc.html#Table_locking">10.2.9
數據庫表級鎖定的問題</a></h3>
<p><strong>MySQL</strong>的表鎖定代碼是不會死鎖的。 </p>
<p><strong>MySQL</strong>使用表級鎖定(而不是行級鎖定或列級鎖定)以達到很高的鎖定速度。對于大表,表級鎖定對大多數應用程序來說比行級鎖定好一些,但是當然有一些缺陷。
</p>
<p>在<strong>MySQL</strong>3.23.7和更高版本中,一個人能把行插入到<code>MyISAM</code>表同時其他線程正在讀該表。注意,目前只有在表中內有刪除的行時才工作。
</p>
<p>表級鎖定使很多線程能夠同時讀一個表,但是如果一個線程想要寫一個表,它必須首先得到獨占存取權。在更改期間,所有其他想要存取該特定表的線程將等到更改就緒。
</p>
<p>因為數據庫的更改通常被視為比<code>SELECT</code>更重要,更新一個表的所有語句比從一個表中檢索信息的語句有更高的優先級。這應該保證更改不被“餓死”,因為一個人針對一個特定表會發出很多繁重的查詢。
</p>
<p>從<strong>MySQL 3.23.7</strong>開始,一個人可以能使用<code>max_write_lock_count</code>變量<strong>強制MySQL</strong>在一個表上一個特定數量的插入后發出一個<code>SELECT</code>。
</p>
<p>對此一個主要的問題如下:
<ul>
<li>一個客戶發出一個花很長時間運行的<code>SELECT</code>。 </li>
<li>然后其他客戶在一個使用的表上發出一個<code>UPDATE</code>;這個客戶將等待直到<code>SELECT</code>完成。</li>
<li>另一個客戶在同一個表上發出另一個<code>SELECT</code>語句;因為<code>UPDATE</code>比<code>SELECT</code>有更高的優先級,該<code>SELECT</code>將等待<code>UPDATE</code>的完成。它也將等待第一個<code>SELECT</code>完成!
</li>
</ul>
<p>對這個問題的一些可能的解決方案是:
<ul>
<li>試著使<code>SELECT</code>語句運行得更快;你可能必須創建一些摘要(summary)表做到這點。
</li>
<li>用<code>--low-priority-updates</code>啟動<code>mysqld</code>。這將給所有更新(修改)一個表的語句以比<code>SELECT</code>語句低的優先級。在這種情況下,在先前情形的最后的<code>SELECT</code>語句將在<code>INSERT</code>語句前執行。
</li>
<li>你可以用<code>LOW_PRIORITY</code>屬性給與一個特定的<code>INSERT</code>、<code>UPDATE</code>或<code>DELETE</code>語句較低優先級。
</li>
<li>為<strong>max_write_lock_count</strong>指定一個低值來啟動<code>mysqld</code>使得在一定數量的<code>WRITE</code>鎖定后給出<code>READ</code>鎖定。
</li>
<li>通過使用SQL命令:<code>SET SQL_LOW_PRIORITY_UPDATES=1</code>,你可從一個特定線程指定所有的更改應該由用低優先級完成。見<a HREF="manual_Reference.html#SET_OPTION">7.25<code> SET OPTION</code>句法</a>。 </li>
<li>你可以用<code>HIGH_PRIORITY</code>屬性指明一個特定<code>SELECT</code>是很重要的。見<a HREF="manual_Reference.html#SELECT">7.12<code> SELECT</code>句法</a>。 </li>
<li>如果你有關于<code>INSERT</code>結合<code>SELECT</code>的問題,切換到使用新的<code>MyISAM</code>表,因為它們支持并發的<code>SELECT</code>和<code>INSERT</code>。
</li>
<li>如果你主要混合<code>INSERT</code>和<code>SELECT</code>語句,<code>DELAYED</code>屬性<code>的INSERT</code>將可能解決你的問題。見<a HREF="manual_Reference.html#INSERT">7.14<code> INSERT</code>句法</a>。 </li>
<li>如果你有關于<code>SELECT</code>和<code>DELETE</code>的問題,<code>LIMIT</code>選項的<code>DELETE</code>可以幫助你。見<a HREF="manual_Reference.html#DELETE">7.11<code> DELETE</code>句法</a>。 </li>
</ul>
<h2><a NAME="Data_size" HREF="manual_toc.html#Data_size">10.3 使你的數據盡可能小</a></h2>
<p>最基本的優化之一是使你的數據(和索引)在磁盤上(并且在內存中)占據的空間盡可能小。這能給出巨大的改進,因為磁盤讀入較快并且通常也用較少的主存儲器。如果在更小的列上做索引,索引也占據較少的資源。
</p>
<p>你能用下面的技術使表的性能更好并且使存儲空間最小:
<ul>
<li>盡可能地使用最有效(最小)的類型。<strong>MySQL</strong>有很多節省磁盤空間和內存的專業化類型。
</li>
<li>如果可能使表更小,使用較小的整數類型。例如,<code>MEDIUMINT</code>經常比<code>INT</code>好一些。
</li>
<li>如果可能,聲明列為<code>NOT NULL</code>。它使任何事情更快而且你為每列節省一位。注意如果在你的應用程序中你確實需要<code>NULL</code>,你應該毫無疑問使用它,只是避免缺省地在所有列上有它。</li>
<li>如果你沒有任何變長列(<code>VARCHAR</code>、<code>TEXT</code>或<code>BLOB</code>列),使用固定尺寸的記錄格式。這比較快但是不幸地可能會浪費一些空間。見<a HREF="manual_Performance.html#Choosing_table_type">10.6 選擇一種表類型</a>。 </li>
<li>每張桌子應該有盡可能短的主索引。這使一行的辨認容易而有效。</li>
<li>對每個表,你必須決定使用哪種存儲/索引方法。見<a HREF="manual_Server.html#Table_types">9.4 MySQL表類型</a>。也可參見<a HREF="manual_Performance.html#Choosing_table_type">10.6 選擇一種表類型</a>。 </li>
<li>只創建你確實需要的索引。索引對檢索有好處但是當你需要快速存儲東西時就變得糟糕。如果你主要通過搜索列的組合來存取一個表,以它們做一個索引。第一個索引部分應該是最常用的列。如果你總是使用許多列,你應該首先以更多的副本使用列以獲得更好的列索引壓縮。
</li>
<li>如果很可能一個索引在頭幾個字符上有唯一的前綴,僅僅索引該前綴比較好。<strong>MySQL</strong>支持在一個字符列的一部分上的索引。更短的索引更快,不僅因為他們占較少的磁盤空間而且因為他們將在索引緩存中給你更多的命中率并且因此有更少磁盤尋道。見<a HREF="manual_Performance.html#Server_parameters">10.2.3 調節服務器參數</a>。</li>
<li>在一些情形下,分割一個經常被掃描進2個表的表是有益的。特別是如果它是一個動態格式的表并且它可能使一個能用來掃描后找出相關行的較小靜態格式的表。
</li>
</ul>
<h2><a NAME="MySQL_indexes" HREF="manual_toc.html#MySQL_indexes">10.4 MySQL索引的使用</a></h2>
<p>索引被用來快速找出在一個列上用一特定值的行。沒有索引,MySQL不得不首先以第一條記錄開始并然后讀完整個表直到它找出相關的行。表越大,花費時間越多。如果表對于查詢的列有一個索引,MySQL能快速到達一個位置去搜尋到數據文件的中間,沒有必要考慮所有數據。如果一個表有1000行,這比順序讀取至少快100倍。注意你需要存取幾乎所有1000行,它較快的順序讀取,因為此時我們避免磁盤尋道。
</p>
<p>所有的MySQL索引(<code>PRIMARY</code>、<code>UNIQUE</code>和<code>INDEX</code>)在B樹中存儲。字符串是自動地壓縮前綴和結尾空間。見<a HREF="manual_Reference.html#CREATE_INDEX">7.27<code> CREATE INDEX</code>句法</a>。 </p>
<p>索引用于:
<ul>
<li>快速找出匹配一個<code>WHERE</code>子句的行。 </li>
<li>當執行聯結時,從其他表檢索行。 </li>
<li>對特定的索引列找出<code>MAX()</code>或<code>MIN()</code>值。 </li>
<li>如果排序或分組在一個可用鍵的最左面前綴上進行(例如,<code>ORDER
BY key_part_1,key_part_2</code>),排序或分組一個表。如果所有鍵值部分跟隨<code>DESC</code>,鍵以倒序被讀取。
</li>
<li>在一些情況中,一個查詢能被優化來檢索值,不用咨詢數據文件。如果對某些表的所有使用的列是數字型的并且構成某些鍵的最左面前綴,為了更快,值可以從索引樹被檢索出來。
</li>
</ul>
<p>假定你發出下列<code>SELECT</code>語句: </p>
<pre>mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -