1. 程式人生 > >MySQL/Oracle數據庫優化總結

MySQL/Oracle數據庫優化總結

sqlserve 可用 手動 取出 oracle數據庫 onf sele 評論 測試工程

MySQL數據庫優化的八種方式

1、選取最適用的字段屬性

  MySQL可以很好的支持大數據量的存取,但是一般說來,數據庫中的表越小,在它上面執行的查詢也就會越快。因此,在創建表的時候,為了獲得更好的性能,我們可以將表中字段的寬度設得盡可能小。

  例如,在定義郵政編碼這個字段時,如果將其設置為CHAR(255),顯然給數據庫增加了不必要的空間,甚至使用VARCHAR這種類型也是多余的,因為CHAR(6)就可以很好的完成任務了。同樣的,如果可以的話,我們應該使用MEDIUMINT而不是BIGIN來定義整型字段。

  另外一個提高效率的方法是在可能的情況下,應該盡量把字段設置為NOT NULL,這樣在將來執行查詢的時候,數據庫不用去比較NULL值。

對於某些文本字段,例如“省份”或者“性別”,我們可以將它們定義為ENUM類型。因為在MySQL中,ENUM(枚舉)類型被當作數值型數據來處理,而數值型數據被處理起來的速度要比文本類型快得多。這樣,我們又可以提高數據庫的性能。

2、使用連接(JOIN)來代替子查詢(Sub-Queries)

  MySQL從4.1開始支持SQL的子查詢。這個技術可以使用SELECT語句來創建一個單列的查詢結果,然後把這個結果作為過濾條件用在另一個查詢中。例如,我們要將客戶基本信息表中沒有任何訂單的客戶刪除掉,就可以利用子查詢先從銷售信息表中將所有發出訂單的客戶ID取出來,然後將結果傳遞給主查詢,如下所示:

  DELETE FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo)

  使用子查詢可以一次性的完成很多邏輯上需要多個步驟才能完成的SQL操作,同時也可以避免事務或者表鎖死,並且寫起來也很容易。但是,有些情況下,子查詢可以被更有效率的連接(JOIN)替代。例如,假設我們要將所有沒有訂單記錄的用戶取出來,可以用下面這個查詢完成:

  SELECT * FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo)

  如果使用連接(JOIN)來完成這個查詢工作,速度將會快很多。尤其是當salesinfo表中對CustomerID建有索引的話,性能將會更好,查詢如下:

  SELECT * FROM customerinfo LEFT JOIN salesinfo ON customerinfo.CustomerID = salesinfo.CustomerID WHERE salesinfo.CustomerID IS NULL

  連接(JOIN)之所以更有效率一些,是因為MySQL不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。

3、使用聯合(UNION)來代替手動創建的臨時表

  MySQL從4.0的版本開始支持union查詢,它可以把需要使用臨時表的兩條或更多的select查詢合並的一個查詢中。在客戶端的查詢會話結束的時候,臨時表會被自動刪除,從而保證數據庫整齊、高效。使用union來創建查詢的時候,我們只需要用UNION作為關鍵字把多個select語句連接起來就可以了,要註意的是所有select語句中的字段數目要想同。下面的例子就演示了一個使用UNION的查詢。

  SELECT Name,Phone FROM client UNION SELECT Name,BirthDate FROM author UNION SELECT Name,Supplier FROM product

4、事務

  盡管我們可以使用子查詢(Sub-Queries)、連接(JOIN)和聯合(UNION)來創建各種各樣的查詢,但不是所有的數據庫操作都可以只用一條或少數幾條SQL語句就可以完成的。更多的時候是需要用到一系列的語句來完成某種工作。但是在這種情況下,當這個語句塊中的某一條語句運行出錯的時候,整個語句塊的操作就會變得不確定起來。設想一下,要把某個數據同時插入兩個相關聯的表中,可能會出現這樣的情況:第一個表中成功更新後,數據庫突然出現意外狀況,造成第二個表中的操作沒有完成,這樣,就會造成數據的不完整,甚至會破壞數據庫中的數據。要避免這種情況,就應該使用事務,它的作用是:要麽語句塊中每條語句都操作成功,要麽都失敗。換句話說,就是可以保持數據庫中數據的一致性和完整性。

  事務以BEGIN關鍵字開始,COMMIT關鍵字結束。在這之間的一條SQL操作失敗,那麽,ROLLBACK命令就可以把數據庫恢復到BEGIN開始之前的狀態。

  BEGIN; INSERT INTO salesinfo SET CustomerID=14; UPDATE inventory SET Quantity = 11 WHERE item = ‘book‘; COMMIT;

  事務的另一個重要作用是當多個用戶同時使用相同的數據源時,它可以利用鎖定數據庫的方法來為用戶提供一種安全的訪問方式,這樣可以保證用戶的操作不被其它的用戶所幹擾。

5、鎖定表

  盡管事務是維護數據庫完整性的一個非常好的方法,但卻因為它的獨占性,有時會影響數據庫的性能,尤其是在很大的應用系統中。由於在事務執行的過程中,數據庫將會被鎖定,因此其它的用戶請求只能暫時等待直到該事務結束。如果一個數據庫系統只有少數幾個用戶來使用,事務造成的影響不會成為一個太大的問題;但假設有成千上萬的用戶同時訪問一個數據庫系統,例如訪問一個電子商務網站,就會產生比較嚴重的響應延遲。

  其實,有些情況下我們可以通過鎖定表的方法來獲得更好的性能。下面的例子就用鎖定表的方法來完成前面一個例子中事務的功能。

  LOCK TABLE inventory WRITE SELECT Quantity FROM inventory WHERE Item = ‘book‘;

  ...

  UPDATE inventory SET Quantity = 11 WHERE Item = ‘book‘; UNLOCK TABLES

  這裏,我們用一個select語句取出初始數據,通過一些計算,用update語句將新值更新到表中。包含有WRITE關鍵字的LOCK TABLE語句可以保證在UNLOCK TABLES命令被執行之前,不會有其它的訪問來對inventory進行插入、更新或者刪除的操作。

6、使用外鍵

  鎖定表的方法可以維護數據的完整性,但是它卻不能保證數據的關聯性。這個時候我們就可以使用外鍵。

  例如,外鍵可以保證每一條銷售記錄都指向某一個存在的客戶。在這裏,外鍵可以把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一條沒有合法CustomerID的記錄都不會被更新或插入到salesinfo中。

  CREATE TABLE customerinfo (CustomerID INT NOT NULL,PRIMARYKEY(CustomerID))TYPE = INNODB;

  CREATE TABLE salesinfo (SalesID INT NOT NULL,CustomerID INT NOT NULL,PRIMARYKEY(CustomerID,SalesID),FOREIGNKEY(CustomerID) REFERENCES customerinfo (CustomerID) ON DELETE CASCADE) TYPE = INNODB;

  註意例子中的參數“ON DELETE CASCADE”。該參數保證當customerinfo表中的一條客戶記錄被刪除的時候,salesinfo表中所有與該客戶相關的記錄也會被自動刪除。如果要在MySQL中使用外鍵,一定要記住在創建表的時候將表的類型定義為事務安全表InnoDB類型。該類型不是MySQL表的默認類型。定義的方法是在CREATE TABLE語句中加上TYPE = INNODB。如例中所示。

7、使用索引

  索引是提高數據庫性能的常用方法,它可以令數據庫服務器以比沒有索引快得多的速度檢索特定的行,尤其是在查詢語句當中包含有MAX(),MIN()和ORDERBY這些命令的時候,性能提高更為明顯。

  一般說來,索引應建立在那些將用於JOIN,WHERE判斷和ORDERBY排序的字段上。盡量不要對數據庫中某個含有大量重復的值的字段建立索引。對於一個ENUM類型的字段來說,出現大量重復值是很有可能的情況,例如customerinfo中的“province”字段,在這樣的字段上建立索引將不會有什麽幫助;相反,還有可能降低數據庫的性能。

  我們在創建表的時候可以同時創建合適的索引,也可以使用ALTER TABLE或CREATE INDEX在以後創建索引。此外,MySQL從版本3.23.23開始支持全文索引和搜索。全文索引在MySQL中是一個FULL TEXT類型索引,但僅能用於MyISAM類型的表。對於一個大的數據庫,將數據裝載到一個沒有FULL TEXT索引的表中,然後再使用ALTER TABLE或CREATE INDEX創建索引,將是非常快的。但如果將數據裝載到一個已經有FULL TEXT索引的表中,執行過程將會非常慢。

8、優化的查詢語句

  絕大多數情況下,使用索引可以提高查詢的速度,但如果SQL語句使用不恰當的話,索引將無法發揮它應有的作用。下面是應該註意的幾個方面:

  首先,最好是在相同類型的字段間進行比較的操作。例如不能將一個建有索引的INT字段和BIGINT字段進行比較;但是作為特殊的情況,在CHAR類型的字段和VARCHAR類型字段的字段大小相同的時候,可以將它們進行比較。

  其次,在建有索引的字段上盡量不要使用函數進行操作。例如,在一個DATE類型的字段上使用YEAE()函數時,將會使索引不能發揮應有的作用。所以,下面的兩個查詢雖然返回的結果一樣,但後者要比前者快得多。

  第三,在搜索字符型字段時,我們有時會使用LIKE關鍵字和通配符,這種做法雖然簡單,但卻也是以犧牲系統性能為代價的。

  例如下面的查詢將會比較表中的每一條記錄。

  SELECT * FROM books WHERE name like "MySQL%"

  但是如果換用下面的查詢,返回的結果一樣,但速度就要快上很多:

  SELECT * FROM books WHERE name >= "MySQL" and name < "MySQM"

  最後,應該註意避免在查詢中讓MySQL進行自動類型轉換,因為轉換過程也會使索引變得不起作用。

優化Mysql數據庫的8個方法

本文通過8個方法優化Mysql數據庫:創建索引、復合索引、索引不會包含有NULL值的列、使用短索引、排序的索引問題、like語句操作、不要在列上進行運算、不使用NOT IN和<>操作

1、創建索引
對於查詢占主要的應用來說,索引顯得尤為重要。很多時候性能問題很簡單的就是因為我們忘了添加索引而造成的,或者說沒有添加更為有效的索引導致。如果不加索引的話,那麽查找任何哪怕只是一條特定的數據都會進行一次全表掃描,如果一張表的數據量很大而符合條件的結果又很少,那麽不加索引會引起致命的性能下降。但是也不是什麽情況都非得建索引不可,比如性別可能就只有兩個值,建索引不僅沒什麽優勢,還會影響到更新速度,這被稱為過度索引。
2、復合索引
比如有一條語句是這樣的:select * from users where area=‘beijing‘ and age=22;
如果我們是在area和age上分別創建單個索引的話,由於mysql查詢每次只能使用一個索引,所以雖然這樣已經相對不做索引時全表掃描提高了很多效率,但是如果在area、age兩列上創建復合索引的話將帶來更高的效率。如果我們創建了(area, age, salary)的復合索引,那麽其實相當於創建了(area,age,salary)、(area,age)、(area)三個索引,這被稱為最佳左前綴特性。因此我們在創建復合索引時應該將最常用作限制條件的列放在最左邊,依次遞減。
3、索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那麽這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
4、使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字符內,多數值是惟一的,那麽就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
5、排序的索引問題
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麽order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。
6、like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
7、不要在列上進行運算
select * from users where YEAR(adddate)<2007;
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成
select * from users where adddate<‘2007-01-01‘;
8、不使用NOT IN和<>操作
NOT IN和<>操作都不會使用索引將進行全表掃描。NOT IN可以NOT EXISTS代替,id<>3則可使用id>3 or id<3來代替。

數據庫SQL優化大總結之 百萬級數據庫優化方案

網上關於SQL優化的教程很多,但是比較雜亂。近日有空整理了一下,寫出來跟大家分享一下,其中有錯誤和不足的地方,還請大家糾正補充。

這篇文章我花費了大量的時間查找資料、修改、排版,希望大家閱讀之後,感覺好的話推薦給更多的人,讓更多的人看到、糾正以及補充。

1.對查詢進行優化,要盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。


2.應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num is null

最好不要給數據庫留NULL,盡可能的使用 NOT NULL填充數據庫.

備註、描述、評論之類的可以設置為 NULL,其他的,最好不要使用NULL。

不要以為 NULL 不需要空間,比如:char(100) 型,在字段建立時,空間就固定了, 不管是否插入值(NULL也包含在內),都是占用 100個字符的空間的,如果是varchar這樣的變長字段, null 不占用空間。


可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:

select id from t where num = 0


3.應盡量避免在 where 子句中使用 != 或 <> 操作符,否則將引擎放棄使用索引而進行全表掃描。

4.應盡量避免在 where 子句中使用 or 來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or Name = ‘admin‘

可以這樣查詢:

select id from t where num = 10
union all
select id from t where Name = ‘admin‘


5.in 和 not in 也要慎用,否則會導致全表掃描,如:

select id from t where num in(1,2,3)

對於連續的數值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

很多時候用 exists 代替 in 是一個好的選擇:

select num from a where num in(select num from b)

用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num)

6.下面的查詢也將導致全表掃描:

select id from t where name like ‘%abc%’

若要提高效率,可以考慮全文檢索。

7.如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:

select id from t where num = @num

可以改為強制查詢使用索引:

select id from t with(index(索引名)) where num = @num

.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t where num/2 = 100

應改為:

select id from t where num = 100*2


9.應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t where substring(name,1,3) = ’abc’       -–name以abc開頭的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0    -–‘2005-11-30’    --生成的id

應改為:

select id from t where name like ‘abc%‘
select id from t where createdate >= ‘2005-11-30‘ and createdate < ‘2005-12-1‘


10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

11.在使用索引字段作為條件時,如果該索引是復合索引,那麽必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓字段順序與索引順序相一致。

12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:

select col1,col2 into #t from t where 1=0

這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(…)

13.Update 語句,如果只更改1、2個字段,不要Update全部字段,否則頻繁調用會引起明顯的性能消耗,同時帶來大量日誌。

14.對於多張大數據量(這裏幾百條就算大了)的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,性能很差。

15.select count(*) from table;這樣不帶任何條件的count會引起全表掃描,並且沒有任何業務意義,是一定要杜絕的。


16.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。

17.應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麽需要考慮是否應將該索引建為 clustered 索引。

18.盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連 接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。

19.盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

20.任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

21.盡量使用表變量來代替臨時表。如果表變量包含大量數據,請註意索引非常有限(只有主鍵索引)。

22. 避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件, 最好使用導出表。

23.在新建臨時表時,如果一次性插入數據量很大,那麽可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。

24.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

25.盡量避免使用遊標,因為遊標的效率較差,如果遊標操作的數據超過1萬行,那麽就應該考慮改寫。

26.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。

27.與臨時表一樣,遊標並不是不可使用。對小型數據集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。如果開發時 間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。

28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。

29.盡量避免大事務操作,提高系統並發能力。

30.盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

實際案例分析:拆分大的 DELETE 或INSERT 語句,批量提交SQL語句
  如果你需要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了。
  Apache 會有很多的子進程或線程。所以,其工作起來相當有效率,而我們的服務器也不希望有太多的子進程,線程和數據庫鏈接,這是極大的占服務器資源的事情,尤其是內存。
  如果你把你的表鎖上一段時間,比如30秒鐘,那麽對於一個有很高訪問量的站點來說,這30秒所積累的訪問進程/線程,數據庫鏈接,打開的文件數,可能不僅僅會讓你的WEB服務崩潰,還可能會讓你的整臺服務器馬上掛了。
  所以,如果你有一個大的處理,你一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)條件是一個好的方法。下面是一個mysql示例:

技術分享圖片

while(1){

   //每次只做1000條

   mysql_query(“delete from logs where log_date <= ’2012-11-01’ limit 1000”);

   if(mysql_affected_rows() == 0){

     //刪除完成,退出!
     break;
  }

//每次暫停一段時間,釋放表讓其他進程/線程訪問。
usleep(50000)

}

技術分享圖片

好了,到這裏就寫完了。我知道還有很多沒有寫到的,還請大家補充。後面有空會介紹一些SQL優化工具給大家。讓我們一起學習,一起進步吧!

運維角度淺談MySQL數據庫優化

一個成熟的數據庫架構並不是一開始設計就具備高可用、高伸縮等特性的,它是隨著用戶量的增加,基礎架構才逐漸完善。這篇博文主要談MySQL數據庫發展周期中所面臨的問題及優化方案,暫且拋開前端應用不說,大致分為以下五個階段:

1、數據庫表設計

項目立項後,開發部根據產品部需求開發項目,開發工程師工作其中一部分就是對表結構設計。對於數據庫來說,這點很重要,如果設計不當,會直接影響訪問速度和用戶體驗。影響的因素很多,比如慢查詢、低效的查詢語句、沒有適當建立索引、數據庫堵塞(死鎖)等。當然,有測試工程師的團隊,會做壓力測試,找bug。對於沒有測試工程師的團隊來說,大多數開發工程師初期不會太多考慮數據庫設計是否合理,而是盡快完成功能實現和交付,等項目有一定訪問量後,隱藏的問題就會暴露,這時再去修改就不是這麽容易的事了。

2、數據庫部署

該運維工程師出場了,項目初期訪問量不會很大,所以單臺部署足以應對在1500左右的QPS(每秒查詢率)。考慮到高可用性,可采用MySQL主從復制+Keepalived做雙擊熱備,常見集群軟件有Keepalived、Heartbeat。

雙機熱備博文:http://lizhenliang.blog.51cto.com/7876557/1362313

3、數據庫性能優化

如果將MySQL部署到普通的X86服務器上,在不經過任何優化情況下,MySQL理論值正常可以處理2000左右QPS,經過優化後,有可能會提升到2500左右QPS,否則,訪問量當達到1500左右並發連接時,數據庫處理性能就會變慢,而且硬件資源還很富裕,這時就該考慮軟件問題了。那麽怎樣讓數據庫最大化發揮性能呢?一方面可以單臺運行多個MySQL實例讓服務器性能發揮到最大化,另一方面是對數據庫進行優化,往往操作系統和數據庫默認配置都比較保守,會對數據庫發揮有一定限制,可對這些配置進行適當的調整,盡可能的處理更多連接數。

具體優化有以下三個層面:

3.1 數據庫配置優化

MySQL常用有兩種存儲引擎,一個是MyISAM,不支持事務處理,讀性能處理快,表級別鎖。另一個是InnoDB,支持事務處理(ACID),設計目標是為處理大容量數據發揮最大化性能,行級別鎖。

表鎖:開銷小,鎖定粒度大,發生死鎖概率高,相對並發也低。

行鎖:開銷大,鎖定粒度小,發生死鎖概率低,相對並發也高。

為什麽會出現表鎖和行鎖呢?主要是為了保證數據的完整性,舉個例子,一個用戶在操作一張表,其他用戶也想操作這張表,那麽就要等第一個用戶操作完,其他用戶才能操作,表鎖和行鎖就是這個作用。否則多個用戶同時操作一張表,肯定會數據產生沖突或者異常。

根據以上看來,使用InnoDB存儲引擎是最好的選擇,也是MySQL5.5以後版本中默認存儲引擎。每個存儲引擎相關聯參數比較多,以下列出主要影響數據庫性能的參數。

公共參數默認值:

1

2

3

4

5

6

max_connections = 151

#同時處理最大連接數,推薦設置最大連接數是上限連接數的80%左右

sort_buffer_size = 2M

#查詢排序時緩沖區大小,只對order by和group by起作用,可增大此值為16M

open_files_limit = 1024

#打開文件數限制,如果show global status like ‘open_files‘查看的值等於或者大於open_files_limit值時,程序會無法連接數據庫或卡死

MyISAM參數默認值:

1

2

3

4

5

6

7

8

9

10

key_buffer_size = 16M

#索引緩存區大小,一般設置物理內存的30-40%

read_buffer_size = 128K

#讀操作緩沖區大小,推薦設置16M或32M

query_cache_type = ON

#打開查詢緩存功能

query_cache_limit = 1M

#查詢緩存限制,只有1M以下查詢結果才會被緩存,以免結果數據較大把緩存池覆蓋

query_cache_size = 16M

#查看緩沖區大小,用於緩存SELECT查詢結果,下一次有同樣SELECT查詢將直接從緩存池返回結果,可適當成倍增加此值

InnoDB參數默認值:

1

2

3

4

5

6

7

8

9

10

innodb_buffer_pool_size = 128M

#索引和數據緩沖區大小,一般設置物理內存的60%-70%

innodb_buffer_pool_instances = 1

#緩沖池實例個數,推薦設置4個或8個

innodb_flush_log_at_trx_commit = 1

#關鍵參數,0代表大約每秒寫入到日誌並同步到磁盤,數據庫故障會丟失1秒左右事務數據。1為每執行一條SQL後寫入到日誌並同步到磁盤,I/O開銷大,執行完SQL要等待日誌讀寫,效率低。2代表只把日誌寫入到系統緩存區,再每秒同步到磁盤,效率很高,如果服務器故障,才會丟失事務數據。對數據安全性要求不是很高的推薦設置2,性能高,修改後效果明顯。

innodb_file_per_table = OFF

#默認是共享表空間,共享表空間idbdata文件不斷增大,影響一定的I/O性能。推薦開啟獨立表空間模式,每個表的索引和數據都存在自己獨立的表空間中,可以實現單表在不同數據庫中移動。

innodb_log_buffer_size = 8M

#日誌緩沖區大小,由於日誌最長每秒鐘刷新一次,所以一般不用超過16M

3.2 系統內核優化

大多數MySQL都部署在linux系統上,所以操作系統的一些參數也會影響到MySQL性能,以下對linux內核進行適當優化。

1

2

3

4

5

6

7

8

9

10

net.ipv4.tcp_fin_timeout = 30

#TIME_WAIT超時時間,默認是60s

net.ipv4.tcp_tw_reuse = 1

#1表示開啟復用,允許TIME_WAIT socket重新用於新的TCP連接,0表示關閉

net.ipv4.tcp_tw_recycle = 1

#1表示開啟TIME_WAIT socket快速回收,0表示關閉

net.ipv4.tcp_max_tw_buckets = 4096

#系統保持TIME_WAIT socket最大數量,如果超出這個數,系統將隨機清除一些TIME_WAIT並打印警告信息

net.ipv4.tcp_max_syn_backlog = 4096

#進入SYN隊列最大長度,加大隊列長度可容納更多的等待連接

在linux系統中,如果進程打開的文件句柄數量超過系統默認值1024,就會提示“too many files open”信息,所以要調整打開文件句柄限制。

1

2

3

4

# vi /etc/security/limits.conf #加入以下配置,*代表所有用戶,也可以指定用戶,重啟系統生效

* soft nofile 65535

* hard nofile 65535

# ulimit -SHn 65535 #立刻生效

3.3 硬件配置

加大物理內存,提高文件系統性能。linux內核會從內存中分配出緩存區(系統緩存和數據緩存)來存放熱數據,通過文件系統延遲寫入機制,等滿足條件時(如緩存區大小到達一定百分比或者執行sync命令)才會同步到磁盤。也就是說物理內存越大,分配緩存區越大,緩存數據越多。當然,服務器故障會丟失一定的緩存數據。

SSD硬盤代替SAS硬盤,將RAID級別調整為RAID1+0,相對於RAID1和RAID5有更好的讀寫性能(IOPS),畢竟數據庫的壓力主要來自磁盤I/O方面。

4、數據庫架構擴展

隨著業務量越來越大,單臺數據庫服務器性能已無法滿足業務需求,該考慮加機器了,該做集群了~~~。主要思想是分解單臺數據庫負載,突破磁盤I/O性能,熱數據存放緩存中,降低磁盤I/O訪問頻率。

4.1 主從復制與讀寫分離

因為生產環境中,數據庫大多都是讀操作,所以部署一主多從架構,主數據庫負責寫操作,並做雙擊熱備,多臺從數據庫做負載均衡,負責讀操作,主流的負載均衡器有LVS、HAProxy、Nginx。

怎麽來實現讀寫分離呢?大多數企業是在代碼層面實現讀寫分離,效率比較高。另一個種方式通過代理程序實現讀寫分離,企業中應用較少,常見代理程序有MySQL Proxy、Amoeba。在這樣數據庫集群架構中,大大增加數據庫高並發能力,解決單臺性能瓶頸問題。如果從數據庫一臺從庫能處理2000 QPS,那麽5臺就能處理1w QPS,數據庫橫向擴展性也很容易。

有時,面對大量寫操作的應用時,單臺寫性能達不到業務需求。如果做雙主,就會遇到數據庫數據不一致現象,產生這個原因是在應用程序不同的用戶會有可能操作兩臺數據庫,同時的更新操作造成兩臺數據庫數據庫數據發生沖突或者不一致。在單庫時MySQL利用存儲引擎機制表鎖和行鎖來保證數據完整性,怎樣在多臺主庫時解決這個問題呢?有一套基於perl語言開發的主從復制管理工具,叫MySQL-MMM(Master-Master replication managerfor Mysql,Mysql主主復制管理器),這個工具最大的優點是在同一時間只提供一臺數據庫寫操作,有效保證數據一致性。

主從復制博文:http://lizhenliang.blog.51cto.com/7876557/1290431

讀寫分離博文:http://lizhenliang.blog.51cto.com/7876557/1305083

MySQL-MMM博文:http://lizhenliang.blog.51cto.com/7876557/1354576

4.2 增加緩存

給數據庫增加緩存系統,把熱數據緩存到內存中,如果緩存中有要請求的數據就不再去數據庫中返回結果,提高讀性能。緩存實現有本地緩存和分布式緩存,本地緩存是將數據緩存到本地服務器內存中或者文件中。分布式緩存可以緩存海量數據,擴展性好,主流的分布式緩存系統有memcached、redis,memcached性能穩定,數據緩存在內存中,速度很快,QPS可達8w左右。如果想數據持久化就選擇用redis,性能不低於memcached。

工作過程:

技術分享圖片

4.3 分庫

分庫是根據業務不同把相關的表切分到不同的數據庫中,比如web、bbs、blog等庫。如果業務量很大,還可將切分後的庫做主從架構,進一步避免單個庫壓力過大。

4.4 分表

數據量的日劇增加,數據庫中某個表有幾百萬條數據,導致查詢和插入耗時太長,怎麽能解決單表壓力呢?你就該考慮是否把這個表拆分成多個小表,來減輕單個表的壓力,提高處理效率,此方式稱為分表。

分表技術比較麻煩,要修改程序代碼裏的SQL語句,還要手動去創建其他表,也可以用merge存儲引擎實現分表,相對簡單許多。分表後,程序是對一個總表進行操作,這個總表不存放數據,只有一些分表的關系,以及更新數據的方式,總表會根據不同的查詢,將壓力分到不同的小表上,因此提高並發能力和磁盤I/O性能。

分表分為垂直拆分和水平拆分:

垂直拆分:把原來的一個很多字段的表拆分多個表,解決表的寬度問題。你可以把不常用的字段單獨放到一個表中,也可以把大字段獨立放一個表中,或者把關聯密切的字段放一個表中。

水平拆分:把原來一個表拆分成多個表,每個表的結構都一樣,解決單表數據量大的問題。

4.5 分區

分區就是把一張表的數據根據表結構中的字段(如range、list、hash等)分成多個區塊,這些區塊可以在一個磁盤上,也可以在不同的磁盤上,分區後,表面上還是一張表,但數據散列在多個位置,這樣一來,多塊硬盤同時處理不同的請求,從而提高磁盤I/O讀寫性能,實現比較簡單。

註:增加緩存、分庫、分表和分區主要由程序猿來實現。

5、數據庫維護

數據庫維護是運維工程師或者DBA主要工作,包括性能監控、性能分析、性能調優、數據庫備份和恢復等。

5.1 性能狀態關鍵指標

QPS,Queries Per Second:每秒查詢數,一臺數據庫每秒能夠處理的查詢次數

TPS,Transactions Per Second:每秒處理事務數

通過show status查看運行狀態,會有300多條狀態信息記錄,其中有幾個值幫可以我們計算出QPS和TPS,如下:

Uptime:服務器已經運行的實際,單位秒

Questions:已經發送給數據庫查詢數

Com_select:查詢次數,實際操作數據庫的

Com_insert:插入次數

Com_delete:刪除次數

Com_update:更新次數

Com_commit:事務次數

Com_rollback:回滾次數

那麽,計算方法來了,基於Questions計算出QPS:

1

2

mysql> show global status like ‘Questions‘;

mysql> show global status like ‘Uptime‘;

QPS = Questions / Uptime

基於Com_commit和Com_rollback計算出TPS:

1

2

3

mysql> show global status like ‘Com_commit‘;

mysql> show global status like ‘Com_rollback‘;

mysql> show global status like ‘Uptime‘;

TPS = (Com_commit + Com_rollback) / Uptime

另一計算方式:基於Com_select、Com_insert、Com_delete、Com_update計算出QPS

1

mysql> show global status where Variable_name in(‘com_select‘,‘com_insert‘,‘com_delete‘,‘com_update‘);

等待1秒再執行,獲取間隔差值,第二次每個變量值減去第一次對應的變量值,就是QPS

TPS計算方法:

1

mysql> show global status where Variable_name in(‘com_insert‘,‘com_delete‘,‘com_update‘);

計算TPS,就不算查詢操作了,計算出插入、刪除、更新四個值即可。

經網友對這兩個計算方式的測試得出,當數據庫中myisam表比較多時,使用Questions計算比較準確。當數據庫中innodb表比較多時,則以Com_*計算比較準確。

5.2 開啟慢查詢日誌

MySQL開啟慢查詢日誌,分析出哪條SQL語句比較慢,使用set設置變量,重啟服務失效,可以在my.cnf添加參數永久生效。

1

2

3

4

mysql> set global slow-query-log=on #開啟慢查詢功能

mysql> set global slow_query_log_file=‘/var/log/mysql/mysql-slow.log‘; #指定慢查詢日誌文件位置

mysql> set global log_queries_not_using_indexes=on; #記錄沒有使用索引的查詢

mysql> set global long_query_time=1; #只記錄處理時間1s以上的慢查詢

分析慢查詢日誌,可以使用MySQL自帶的mysqldumpslow工具,分析的日誌較為簡單。

# mysqldumpslow -t 3 /var/log/mysql/mysql-slow.log #查看最慢的前三個查詢

也可以使用percona公司的pt-query-digest工具,日誌分析功能全面,可分析slow log、binlog、general log。

分析慢查詢日誌:pt-query-digest /var/log/mysql/mysql-slow.log

分析binlog日誌:mysqlbinlog mysql-bin.000001 >mysql-bin.000001.sql

pt-query-digest --type=binlog mysql-bin.000001.sql

分析普通日誌:pt-query-digest --type=genlog localhost.log

5.3 數據庫備份

備份數據庫是最基本的工作,也是最重要的,否則後果很嚴重,你懂得!但由於數據庫比較大,上百G,往往備份都很耗費時間,所以就該選擇一個效率高的備份策略,對於數據量大的數據庫,一般都采用增量備份。常用的備份工具有mysqldump、mysqlhotcopy、xtrabackup等,mysqldump比較適用於小的數據庫,因為是邏輯備份,所以備份和恢復耗時都比較長。mysqlhotcopy和xtrabackup是物理備份,備份和恢復速度快,不影響數據庫服務情況下進行熱拷貝,建議使用xtrabackup,支持增量備份。

Xtrabackup備份工具使用博文:http://lizhenliang.blog.51cto.com/7876557/1612800

5.4 數據庫修復

有時候MySQL服務器突然斷電、異常關閉,會導致表損壞,無法讀取表數據。這時就可以用到MySQL自帶的兩個工具進行修復,myisamchk和mysqlcheck。

myisamchk:只能修復myisam表,需要停止數據庫

常用參數:

-f --force 強制修復,覆蓋老的臨時文件,一般不使用

-r --recover 恢復模式

-q --quik 快速恢復

-a --analyze 分析表

-o --safe-recover 老的恢復模式,如果-r無法修復,可以使用此參數試試

-F --fast 只檢查沒有正常關閉的表

快速修復weibo數據庫:

# cd /var/lib/mysql/weibo

# myisamchk -r -q *.MYI

mysqlcheck:myisam和innodb表都可以用,不需要停止數據庫,如修復單個表,可在數據庫後面添加表名,以空格分割

常用參數:

-a --all-databases 檢查所有的庫

-r --repair 修復表

-c --check 檢查表,默認選項

-a --analyze 分析表

-o --optimize 優化表

-q --quik 最快檢查或修復表

-F --fast 只檢查沒有正常關閉的表

快速修復weibo數據庫:

mysqlcheck -r -q -uroot -p123 weibo

5.5 另外,查看CPU和I/O性能方法

#查看CPU性能

技術分享圖片

#參數-P是顯示CPU數,ALL為所有,也可以只顯示第幾顆CPU技術分享圖片

#查看I/O性能

技術分享圖片

#參數-m是以M單位顯示,默認K

#%util:當達到100%時,說明I/O很忙。

#await:請求在隊列中等待時間,直接影響read時間。

I/O極限:IOPS(r/s+w/s),一般RAID0/10在1200左右。(IOPS,每秒進行讀寫(I/O)操作次數)

I/O帶寬:在順序讀寫模式下SAS硬盤理論值在300M/s左右,SSD硬盤理論值在600M/s左右。

MySQL/Oracle數據庫優化總結