1. 程式人生 > >Mysql效能優化--慢查詢、索引、分表、增量備份還原

Mysql效能優化--慢查詢、索引、分表、增量備份還原

轉自  http://www.cnblogs.com/jiekzou/p/5371085.html

Mysql資料庫的優化技術

對mysql優化是一個綜合性的技術,主要包括

  • 表的設計合理化(符合3NF)
  • 新增適當索引(index) [四種: 普通索引、主鍵索引、唯一索引unique、全文索引]
  • 分表技術(水平分割、垂直分割)
  • 讀寫[寫: update/delete/add]分離
  • 儲存過程 [模組化程式設計,可以提高速度]
  • 對mysql配置優化 [配置最大併發數my.ini, 調整快取大小 ]
  • mysql伺服器硬體升級
  • 定時的去清除不需要的資料,定時進行碎片整理(MyISAM)

資料庫優化工作

對於一個以資料為中心的應用,資料庫的好壞直接影響到程式的效能,因此資料庫效能至關重要。一般來說,要保證資料庫的效率,要做好以下四個方面的工作:

① 資料庫設計

② sql語句優化

③ 資料庫引數配置

④ 恰當的硬體資源和作業系統

此外,使用適當的儲存過程,也能提升效能。

這個順序也表現了這四個工作對效能影響的大小

資料庫表設計             

通俗地理解三個正規化,對於資料庫設計大有好處。在資料庫設計中,為了更好地應用三個正規化,就必須通俗地理解三個正規化(通俗地理解是夠用的理解,並不是最科學最準確的理解):

第一正規化:1NF是對屬性的原子性約束,要求屬性(列)具有原子性,不可再分解;(只要是關係型資料庫都滿足1NF)

第二正規化:2NF是對記錄的惟一性約束,要求記錄有惟一標識,即實體的惟一性;

第三正規化:3NF是對欄位冗餘性的約束,它要求欄位沒有冗餘。 沒有冗餘的資料庫設計可以做到。

但是,沒有冗餘的資料庫未必是最好的資料庫,有時為了提高執行效率,就必須降低正規化標準,適當保留冗餘資料。具體做法是: 在概念資料模型設計時遵守第三正規化,降低正規化標準的工作放到物理資料模型設計時考慮。降低正規化就是增加欄位,允許冗餘。

☞ 資料庫的分類

關係型資料庫: mysql/oracle/db2/informix/sysbase/sql server

非關係型資料庫: (特點: 面向物件或者集合)

NoSql資料庫: MongoDB(特點是面向文件)

舉例說明什麼是適度冗餘,或者說有理由的冗餘!

 

上面這個就是不合適的冗餘,原因是:

在這裡,為了提高學生活動記錄的檢索效率,把單位名稱冗餘到學生活動記錄表裡。單位資訊有500條記錄,而學生活動記錄在一年內大概有200萬資料量。 如果學生活動記錄表不冗餘這個單位名稱欄位,只包含三個int欄位和一個timestamp欄位,只佔用了16位元組,是一個很小的表。而冗餘了一個 varchar(32)的欄位後則是原來的3倍,檢索起來相應也多了這麼多的I/O。而且記錄數相差懸殊,500 VS 2000000 ,導致更新一個單位名稱還要更新4000條冗餘記錄。由此可見,這個冗餘根本就是適得其反。

 

訂單表裡面的Price就是一個冗餘欄位,因為我們可以從訂單明細表中統計出這個訂單的價格,但是這個冗餘是合理的,也能提升查詢效能。

從上面兩個例子中可以得出一個結論:

1---n 冗餘應當發生在1這一方.

SQL語句優化        

SQL優化的一般步驟

  1. 通過show status命令瞭解各種SQL的執行頻率。
  2. 定位執行效率較低的SQL語句-(重點select)
  3. 通過explain分析低效率的SQL
  4. 確定問題並採取相應的優化措施
複製程式碼
-- select語句分類
Select
Dml資料操作語言(insert update delete)
dtl 資料事物語言(commit rollback savepoint)
Ddl資料定義語言(create alter drop..)
Dcl(資料控制語言) grant revoke

-- Show status 常用命令
--查詢本次會話
Show session status like 'com_%';     //show session status like 'Com_select'

--查詢全域性
Show global status like 'com_%';

-- 給某個使用者授權
grant all privileges on *.* to 'abc'@'%';
--為什麼這樣授權 'abc'表示使用者名稱  '@' 表示host, 檢視一下mysql->user表就知道了

--回收許可權
revoke all on *.* from 'abc'@'%';

--重新整理許可權[也可以不寫]
flush privileges;
複製程式碼

SQL語句優化-show引數        

MySQL客戶端連線成功後,通過使用show [session|global] status 命令可以提供伺服器狀態資訊。其中的session來表示當前的連線的統計結果,global來表示自資料庫上次啟動至今的統計結果。預設是session級別的。
下面的例子:
show status like 'Com_%';
其中Com_XXX表示XXX語句所執行的次數。
重點注意:Com_select,Com_insert,Com_update,Com_delete通過這幾個引數,可以容易地瞭解到當前資料庫的應用是以插入更新為主還是以查詢操作為主,以及各類的SQL大致的執行比例是多少。

還有幾個常用的引數便於使用者瞭解資料庫的基本情況。
Connections:試圖連線MySQL伺服器的次數
Uptime:伺服器工作的時間(單位秒)
Slow_queries:慢查詢的次數 (預設是慢查詢時間10s)

show status like 'Connections'
show status like 'Uptime'
show status like 'Slow_queries'

如何查詢mysql的慢查詢時間

Show variables like 'long_query_time';

修改mysql 慢查詢時間

set long_query_time=2

SQL語句優化-定位慢查詢                

問題是: 如何從一個大專案中,迅速的定位執行速度慢的語句. (定位慢查詢)

首先我們瞭解mysql資料庫的一些執行狀態如何查詢(比如想知道當前mysql執行的時間/一共執行了多少次select/update/delete.. / 當前連線)

為了便於測試,我們構建一個大表(400 萬)-> 使用儲存過程構建

預設情況下,mysql認為10秒才是一個慢查詢.

修改mysql的慢查詢.

show variables like 'long_query_time' ; //可以顯示當前慢查詢時間
set long_query_time=1 ;//可以修改慢查詢時間

構建大表->大表中記錄有要求, 記錄是不同才有用,否則測試效果和真實的相差大.建立:

複製程式碼
CREATE TABLE dept( /*部門表*/
deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,  /*編號*/
dname VARCHAR(20)  NOT NULL  DEFAULT "", /*名稱*/
loc VARCHAR(13) NOT NULL DEFAULT "" /*地點*/
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;


CREATE TABLE emp
(empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, /*編號*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上級編號*/
hiredate DATE NOT NULL,/*入職時間*/
sal DECIMAL(7,2)  NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*紅利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部門編號*/
)ENGINE=MyISAM DEFAULT CHARSET=utf8 ;


CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2)  NOT NULL,
hisal DECIMAL(17,2)  NOT NULL
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
複製程式碼

測試資料

INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);

為了儲存過程能夠正常執行,我們需要把命令執行結束符修改delimiter $$

建立函式,該函式會返回一個指定長度的隨機字串

複製程式碼
create function rand_string(n INT) 
returns varchar(255) #該函式會返回一個字串
begin 
#chars_str定義一個變數 chars_str,型別是 varchar(100),預設值'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
 declare chars_str varchar(100) default
   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
 declare return_str varchar(255) default '';
 declare i int default 0;
 while i < n do 
   set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
   set i = i + 1;
   end while;
  return return_str;
  end 
複製程式碼

建立一個儲存過程

複製程式碼
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0; 
#set autocommit =0 把autocommit設定成0
 set autocommit = 0;  
 repeat
 set i = i + 1;
 insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand());
  until i = max_num
 end repeat;
   commit;
 end 
複製程式碼

#呼叫剛剛寫好的函式, 1800000條記錄,從100001號開始

call insert_emp(100001,4000000);

這時我們如果出現一條語句執行時間超過1秒中,就會統計到.

如果把慢查詢的sql記錄到我們的一個日誌中

在預設情況下,低版本的mysql不會記錄慢查詢,需要在啟動mysql時候,指定記錄慢查詢才可以

    bin\mysqld.exe - -safe-mode  - -slow-query-log [mysql5.5 可以在my.ini指定]

    bin\mysqld.exe –log-slow-queries=d:/abc.log [低版本mysql5.0可以在my.ini指定]

該慢查詢日誌會放在data目錄下[在mysql5.0這個版本中時放在 mysql安裝目錄/data/下],在 mysql5.5.19下是需要檢視

my.ini 的 datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.5/Data/“來確定.

在mysql5.6中,預設是啟動記錄慢查詢的,my.ini的所在目錄為:C:\ProgramData\MySQL\MySQL Server 5.6,其中有一個配置項

slow-query-log=1

針對 mysql5.5啟動慢查詢有兩種方法

bin\mysqld.exe - -safe-mode  - -slow-query-log

也可以在my.ini 檔案中配置:

[mysqld]
# The TCP/IP Port the MySQL Server will listen on
port=3306
slow-query-log

通過慢查詢日誌定位執行效率較低的SQL語句。慢查詢日誌記錄了所有執行時間超過long_query_time所設定的SQL語句。
show variables like 'long_query_time';
set long_query_time=2;

為dept表新增資料

複製程式碼
desc dept;
ALTER table  dept add id int PRIMARY