1. 程式人生 > >MySQL資料庫優化心得

MySQL資料庫優化心得


       選擇適當的欄位型別,特別是主鍵

  選擇欄位的一般原則是保小不保大,能用佔用位元組小的欄位就不用大欄位。比如主鍵, 我們強烈建議用自增型別,不用guid,為什麼?省空間啊?空間是什麼?空間就是效率!按4個位元組和按32個位元組定位一條記錄,誰快誰慢太明顯了。涉及到 幾個表做join時,效果就更明顯了。值得一提的是,datetime和timestamp,datetime佔用8個位元組,而timestamp佔用4 個位元組,只用了一半,而timestamp表示的範圍是1970—2037,對於大多數應用,尤其是記錄什麼考試時間,登入時間這類資訊,綽綽有餘啊。

  檔案、圖片等大檔案用檔案系統

儲存,不用資料庫

  不用多說,鐵律!!!資料庫只儲存路徑。

  外來鍵表示清楚,方便建立索引

  我們都知道,在powerdesigner裡為兩個實體建立關係,生成物理模型時會自動給外來鍵建立索引。所以我們不要怕建立關係把線拉亂,建立個ShortCut就好了。

  掌握表的寫入時機

  在庫模式相同的情況下,如何使用資料庫也對效能有著重要作用。同樣是寫入一個表,先寫和後寫對後續的操作會產生很大影響。例如在上面提到的適度冗餘裡的例子,        

   我們最初的目的是記錄考生的總分,以達到提高檢索效率的目的,也就是在錄入成績時寫入這個表。在需求裡有這樣的要求:列出本次考試的所有學生成績,沒有 錄入成績的也顯示該學生名稱,只是總分顯示為空。這個查詢就需要用【學生資訊】left outer join 【學生考試總分資訊】,大家都知道outer join 的效率比join是要低的,為了避免這個問題,我們就在佈置考試的時候寫入這個表,把所有學生都插入進去,分數都是null,這樣一來我們就可以用 join達到這個效果了。而且還有這樣的好處:在某次考試中,安排了一個班所有學生考試,所有學生都錄入了成績。現在班裡轉來一個新生,那麼在此時如果查 詢學生成績,就會列出這個新生,結果是未錄入成績,這顯然是不對的。如果在安排的時候就寫入,就可以記錄下該次考試中實際的考生了,這個表的作用,也就不 知是冗餘了。

    寧可集中批量操作,避免頻繁讀寫

  系統裡包含了積分部分,學生和老師通過系統做了操作都可以獲得積分,而且積分規 則很複雜,限制每類操作獲得積分不同,每人每天每類積分都有上限。比如登入,一次登入就可以獲得1分,但是不管你登入多少次,一天只能累積一個登入積分。 這個還是簡單的,有的積分很變態,比如老師積分中有一類是看老師判作業的情況,規則是:老師判了作業,發現學生有錯的,學生改過了,老師再判,如果這時候 學生都對了,就給老師加分,如果學生還是錯的,那就接著改,知道學生都改對了,老師都判完了,才能給老師加分。如果用程式來處理,很可能每個功能都會額外 的寫一堆程式碼來處理這個雞肋似的積分。不僅程式設計的同事幹活找不到重點,還平白給資料庫帶來了很大的壓力。經過和需求人員的討論,確定積分沒有必要實時累 積,於是我們採取後臺指令碼批量處理的方式。夜深人靜的時候,讓機器自己玩去吧。

  這個變態的積分規則用批處理讀出來是這樣的:  

      
1select person_id, @semester_id, 301003, 0, @one_marks, assign_date, @one_marks 2         from hom_assignmentinfo   ha, hom_assign_class hac 3         where ha.assignment_id = hac.assignment_id 4              and ha.assign_datebetween @time_beginand @time_end 5              and ha.assignment_idnot in 6                    ( 7                        select haa.assignment_idfrom hom_assignment_appraise haa, hom_check_assignment hca 8                         where haa.appraise_id = hca.appraise_id and haa.if_submit=1 9                              and ( 10                                      (hca.recheck_state = 3004001 and hca.check_resultin (3003002, 3003003) ) 11                                      or 12                                      (hca.recheck_state = 3004002 and hca.recheck_resultin (3003002, 3003003)) 13                                    ) 14                    ) 15              and ha.assignment_idnot in 16                    ( 17                        select assignment_idfrom hom_assignment_appraisewhere if_submit=0and result_type = 0 18                    ) 19              and ha.assignment_idin      20                    ( 21                        select haa.assignment_idfrom hom_assignment_appraise haa, hom_check_assignment hca 22                         where haa.appraise_id = hca.appraise_id and haa.if_submit=1 23                              and hca.check_resultin (3003002, 3003003) 24                    );

  這還只是箇中間過程,這要是用程式實時處理,即使程式設計人員不罷工,資料庫也會歇了。

  選擇合適的引擎

   Mysql提供了很多種引擎,我們用的最多的是myisam,innodb,memory這三類。官方手冊上說道myisqm比innodb的讀速度要 快,大概是3倍。不過書不能盡信啊,《OreIlly.High.Performance.Mysql》這本書裡提到了myisam和innodb的比 較,在測試中myisam的表現還不及innodb。至於memory,哈哈,還是比較好用的。在批處理種作臨時表是個不錯的選擇(如果記憶體夠大)。在我的一個批處理中,速度比近乎1:10。

    二、SQL語句優化

 Sql語句優化工具

  ·慢日誌

  如果發現系統慢了,又說不清楚是哪裡慢,那麼就該用這個工具了。只需要為mysql配置引數,mysql會自己記錄下來慢的sql語句。配置很簡單,引數檔案裡配置:

  slow_query_log=d:/slow.txt

  long_query_time = 2

  就可以在d:/slow.txt裡找到執行時間超過2秒的語句了,根據這個檔案定位問題吧。

  ·mysqldumpslow.pl

慢日誌檔案可能會很大,讓人去看是很難受的事。這時候我們可以通過mysql自帶的工具來分析。這個工具可以格式化慢日誌檔案,對於只是引數不同的語句 會歸類類並,比如有兩個語句select * from a where id=1 和select * from a where id=2,經過這個工具整理後就只剩下select * from a where id=N,這樣讀起來就舒服多了。而且這個工具可以實現簡單的排序,讓我們有的放矢。

      Explain

  現在我們已經知道是哪個語句慢了,那麼它為什麼慢呢?看看mysql是怎麼執行的吧,用explain可以看到mysql執行計劃,下面的用法來源於手冊

  EXPLAIN語法(獲取SELECT相關資訊)

  EXPLAIN [EXTENDED] SELECT select_options

  EXPLAIN語句可以用作DESCRIBE的一個同義詞,或獲得關於MySQL如何執行SELECT語句的資訊:

  · EXPLAIN tbl_name是DESCRIBE tbl_name或SHOW COLUMNS FROM tbl_name的一個同義詞。

  · 如果在SELECT語句前放上關鍵詞EXPLAIN,MySQL將解釋它如何處理SELECT,提供有關表如何聯接和聯接的次序。

  該節解釋EXPLAIN的第2個用法。

  藉助於EXPLAIN,可以知道什麼時候必須為表加入索引以得到一個使用索引來尋找記錄的更快的SELECT。

  如果由於使用不正確的索引出現了問題,應執行ANALYZE TABLE更新表的統計(例如關鍵字集的勢),這樣會影響優化器進行的選擇。

  還可以知道優化器是否以一個最佳次序聯接表。為了強制優化器讓一個SELECT語句按照表命名順序的聯接次序,語句應以STRAIGHT_JOIN而不只是SELECT開頭。

   EXPLAIN為用於SELECT語句中的每個表返回一行資訊。表以它們在處理查詢過程中將被MySQL讀入的順序被列出。MySQL用一遍掃描多次聯 接(single-sweep multi-join)的方式解決所有聯接。這意味著MySQL從第一個表中讀一行,然後找到在第二個表中的一個匹配行,然後在第3個表中等等。當所有的 表處理完後,它輸出選中的列並且返回表清單直到找到一個有更多的匹配行的表。從該表讀入下一行並繼續處理下一個表。

  當使用EXTENDED關鍵字時,EXPLAIN產生附加資訊,可以用SHOW WARNINGS瀏覽。該資訊顯示優化器限定SELECT語句中的表和列名,重寫並且執行優化規則後SELECT語句是什麼樣子,並且還可能包括優化過程的其它註解。

    如果什麼都做不了,試試全索引掃描

  如果一個語句實在不能優化了,那麼還有一個方法可以試試:索引覆蓋。

  如果一個語句可以從索引上獲取全部資料,就不需要通過索引再去讀表,省了很多I/O。比如這樣一個表        

  如果我要統計每個學生每道題的得分情況,我們除了要給每個表的主鍵外來鍵建立索引,還要對【得分情況】的實際得分欄位索引,這樣,整個查詢就可以從索引得到資料了。

 三、資料庫引數配置

      最重要的引數就是記憶體,我們主要用的innodb引擎,所以下面兩個引數調的很大

  # Additional memory pool that is used by InnoDB to store metadata

  # information. If InnoDB requires more memory for this purpose it will

  # start to allocate it from the OS. As this is fast enough on most

  # recent operating systems, you normally do not need to change this

  # value. SHOW INNODB STATUS will display the current amount used.

  innodb_additional_mem_pool_size = 64M

  # InnoDB, unlike MyISAM, uses a buffer pool to cache both indexes and

  # row data. The bigger you set this the less disk I/O is needed to

  # access data in tables. On a dedicated database server you may set this

  # parameter up to 80% of the machine physical memory size. Do not set it

  # too large, though, because competition of the physical memory may

  # cause paging in the operating system. Note that on 32bit systems you

  # might be limited to 2-3.5G of user level memory per process, so do not

  # set it too high.

  innodb_buffer_pool_size = 5G

  對於myisam,需要調整key_buffer_size

  當然調整引數還是要看狀態,用show status語句可以看到當前狀態,以決定改調整哪些引數

  Cretated_tmp_disk_tables 增加tmp_table_size

  Handler_read_key 高表示索引正確 Handler_read_rnd高表示索引不正確

  Key_reads/Key_read_requests 應小於0.01 計算快取損失率,增加Key_buffer_size

  Opentables/Open_tables 增加table_cache

  select_full_join 沒有實用索引的連結的數量。如果不為0,應該檢查索引。

  select_range_check 如果不為0,該檢查表索引。

  sort_merge_passes 排序演算法已經執行的合併的數量。如果該值較大,應增加sort_buffer_size

  table_locks_waited 不能立即獲得的表的鎖的次數,如果該值較高,應優化查詢

  Threads_created 建立用來處理連線的執行緒數。如果Threads_created較大,要增加 thread_cache_size值。

  快取訪問率的計算方法Threads_created/Connections。

      四、合理的硬體資源和作業系統

  如果你的機器記憶體超過4G,那麼毋庸置疑應當採用64位作業系統和64位mysql

  讀寫分離

  如果資料庫壓力很大,一臺機器支撐不了,那麼可以用mysql複製實現多臺機器同步,將資料庫的壓力分散。  

  Master

  Slave1

  Slave2

  Slave3

   主庫master用來寫入,slave1—slave3都用來做select,每個資料庫分擔的壓力小了很多。

   要實現這種方式,需要程式特別設計,寫都操作master,讀都操作slave,給程式開發帶來了額外負擔。當然目前已經有中介軟體來實現這個代理,對程 序來讀寫哪些資料庫是透明的。官方有個mysql-proxy,但是還是alpha版本的。新浪有個amobe for mysql,也可達到這個目的,結構如下  

  使用方法可以看amobe的手冊。