1. 程式人生 > >MYSQL資料庫優化技術

MYSQL資料庫優化技術

對Mysql的優化是一種綜合性的優化,主要包括
1:表的設計合理化(符合 3NF)
2: 新增適當的索引(index) 普通索引,主鍵索引,唯一索引,全文索引,空間索引
3:分表技術(水平分割),(垂直分割)
4:讀寫分離
5: 儲存過程 [模組化程式設計,可以提高程式設計]
6:對Mysql配置優化(配置最大併發數,調整快取大小)
7:Mysql伺服器硬體升級
8:要定時的去清除一些不需要的資料,要定時進行碎片整理(MyISAM)

一:資料庫表的設計
     什麼樣的表才是3NF(正規化)
     前面有寫到

關係型資料庫(mysql/oracle/db2/sysbase/sql server)
非關係型資料庫(特點是面向物件和集合的)

NoSQL資料庫(介於關係和非關係之間的,比如mongoDB,特點是面向文件的)

--慢查詢
sql語句本身的優化(定位慢查詢)
1:首先了解mysql資料庫的一些執行狀態如何查詢(比如想知道mysql執行的時間,一共執行了多少次相關語句,當前連線數)
show status
--例
 show status like 'uptime';
 show status like 'com_select' 
show status like 'com_insert' 類推

show [session|global] status like ...
如果不寫,預設是會話級別。如果想看所有的,則應該寫global。
show status like 'connections'
--顯示慢查詢
show status like 'slow_queries';
--如何定位慢查詢 
   構建一個大表(400萬)->儲存過程構建
 在預設情況下,mysql認為,10秒種為一個慢查詢
 --如何修改mysql的慢查詢
 show variables like 'long_query_time';

 set long_query_time=1;

#建立表DEPT

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 ;


#建立表EMP僱員
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);

#這裡我建立了一個函式. 

#rand_string(n INT) rand_string 是函式名 (n INT) //該函式接收一個整數
delimiter $$
create function(n INT) 
returns varchar(255)
begin 
 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 $$
delimiter ;
select rand_string(6);


# 隨機產生部門編號
delimiter $$
drop  function rand_num $$


#這裡我們又自定了一個函式
create function rand_num( )
returns int(5)
begin 
 declare i int default 0;
 set i = floor(10+rand()*500);
return i;
  end $$

delimiter ;
select rand_num();

#******************************************
#向emp表中插入記錄(海量的資料)

delimiter $$
drop procedure insert_emp $$

#隨即新增僱員[游標]  400w
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0; 
 set autocommit = 0;  
 repeat
 set i = i + 1;
 insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
  until i = max_num
 end repeat;
   commit;
 end $$
delimiter ;
#呼叫剛剛寫好的函式, 1800000條記錄,從100001號開始
call insert_emp(100001,40000);

--這時我們如果出現一條語句執行時間超過一秒,就會統計到
show status like 'slow_queries';
--如果把慢查詢的sql記錄到我們的日誌中去,預設情況下,我們你的Mysql不會記錄慢查詢,需要在啟動mysql時候,指定記錄慢查詢才可以

--先關閉mysql,再重新啟動。(服務裡啟動)
--如果啟用了慢查詢日誌,預設把這個檔案放在my.ini檔案中記錄的位置(datadir=...)
--切換至bin\mysqld.exe --safe-mode --slow-query-log
--在測試即可。

--優化問題
  --通過 explain 語句可以分析,mysql如何執行你的sql語句。
  --索引
     --4種索引(主鍵索引,唯一索引,全文索引,普通索引)
     --1:主鍵索引
         --當一張表中,把某個列設為主鍵的時候,則該列為主鍵索引。如:
create table aaa(id int unsigned primary key auto_increment,
 name varchar(32) not null default '') charset=utf8;
 --這時,id列就是主鍵索引
 --如果建立表時,未指定主鍵索引,也可以在建立表後新增,指令如下
 alter table 表名 add primary key (列名)
 --例
  create table bbb(
        id int ,
name varchar(20) not null default '')charset utf8;
      alter table bbb add primary key (id);
     --1.2 普通索引
               --create index 索引名on 表(列)
          --普通索引的建立,先建立表,然後再建立普通索引
 create table ccc
 (
    id int unsigned,
    name varchar(32)
 );
      --1.3 全文索引
          --全文索引主要是針對文字或者文字的檢索,比如文章,全文索引只能針對MyISAM有用。
  create table articles(
     id int unsigned auto_increment not null primary key,
     title varchar(200),
     body text,
     fulltext(title,body) 
  )engine=myisam charset=utf8;
 
INSERT INTO articles (title,body) VALUES
    ('MySQL Tutorial','DBMS stands for DataBase ...'),
    ('How To Use MySQL Well','After you went through a ...'),
    ('Optimizing MySQL','In this tutorial we will show ...'),
    ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    ('MySQL vs. YourSQL','In the following database comparison ...'),
    ('MySQL Security','When configured properly, MySQL ...');
 
  --如何使用全文索引
     select *from articles where body like '%mysql%'; [不會使用全文索引]。
     --證明:
       explain  select *from articles where body like '%mysql%';
    --正確用法
        select *from articles where match(title,body) against('database');
     
      --1.4 唯一索引
         --當表的某列被指定為unique,這列就是一個唯一索引了
--方式一: create table ddd(
id int primary key auto_increment,
name varchar(32) not null unique
)
       --這時,name列就是唯一索引了
                                                                   
       --name 列是否可以為null。 可以為空。
        --方式二:
   --在建立表後,再去建立唯一索引
   create table eee (id int primary key auto_increment,
   name varchar(32)
   );
   create unique index 索引名 on表名 (欄位);

      --2:查詢索引
           desc 表名 [該方法的缺點是:不能夠顯示索引名]
  show index from 表名;
           show keys from 表名;
       --3:刪除索引
     
           --alter table 表名 drop index 索引名;(唯一索引也是這樣刪除)
  --如果刪除的是主鍵索引
            --alter table 表名 drop primary key; 
       --4:修改索引
       -- 先刪除,後建立
        
    --建立索引的注意事項
      --索引的代價:
         1:佔用磁碟空間
         2:對DML語句的操作有影響,變慢(比如:刪除一條記錄之後,要重建二叉樹)。
在哪些列上適合新增索引?
     (1):較為頻繁的作為查詢條件欄位應該建立索引
                select *from emp where empno=1
     (2):唯一性太差的欄位不適合建立索引,即使頻繁作為查詢條件
 select *from emp where sex='男';
              (3):更新非常頻繁的欄位也不適合建立索引。
                   select *from emp where logincount=1;


              (4): 不會出現在where子句中欄位不該建立索引
        
    總結:滿足一下條件的欄位,才可以建立索引
     a:肯定在where條件中經常使用。
     b:該欄位的內容不是唯一的幾個值。
     c:欄位本身不是經常(頻繁)變化的。
  --使用索引的注意事項


   在dept表中增加幾個部門;


   測試: 如果我們的表中有複合索引,此時,我們注意到:
      1:對於建立的多列索引,只要查詢條件使用了最左邊的列,索引一般就會被使用。
      
      explain 結果中的欄位含義
        id:查詢序列號
select_type :查詢型別
type:掃描的方式,all表示全表掃描
possible keys: 可能使用到的索引
key : 實際使用的索引
rows:執行該sql語句掃描了多少行,可能得到多少記錄
Extra:代表sql語句的額外資訊(比如排序資訊)

        explain 可以幫助我們在不真正執行某個sql語句時,就告訴我們Mysql將怎麼樣執行,這樣利於我們去分析sql指令

    --例
        先建立複合索引
alter table dept add index myind (dname,loc);
      explain  select *from dept where loc='tmgSVaqC';   不會使用索引
      explain  select *from dept where dname='oJozPbOOC'; 會使用索引


     2:對於使用like的查詢,查詢如果是 ’%aaa‘ 不會使用到索引 ’aaa%‘會使用到索引。
      比如:
         explain  select *from dept where dname like 'oJozPbOOC'; 使用了索引
         explain  select *from dept where dname like '%oJozPbOOC';不會使用索引(因為使用了%或者_)
         explain  select *from dept where dname like 'oJozPbOOC%';使用了索引
    即用like 查詢時,關鍵字的前面,不能使用%或者_這樣的字元,如果一定要前面有變化的值,考慮使用全文索引
    
      3:如果條件中有or,即使其中有條件帶索引,也不會使用,換言之,就是要求使用到的所有欄位都必須建索引
         select *from dept where dname='aaa' or loc='ddd'\G; 不會使用索引
         select *from dept where dname='aaa' or empno=890\G; 會使用索引
      4:如果列型別是字串,那一定要在條件中使用引號引起來,否則不會使用索引。
      
      5: 如果mysql 發現全表掃描比索引還快,則不會使用索引。
    
    如何檢視索引使用的情況:
show status like 'Handler_read%';   
             這個值越高,說明索引使用率越高
  Handler_read_key越高 ,索引使用率高
  Hnadler_rnd_nex越高越不好。
          
#**************************************************************
#  向dept表中插入記錄
delimiter $$
drop procedure insert_dept $$
create procedure insert_dept(in start int(10),in max_num int(10))
begin
declare i int default 0; 
 set autocommit = 0;  
 repeat
 set i = i + 1;
 insert into dept values ((start+i) ,rand_string(10),rand_string(8));
  until i = max_num
 end repeat;
   commit;
 end $$

delimiter ;
call insert_dept(100,10);

#------------------------------------------------
#向salgrade 表插入資料
delimiter $$
drop procedure insert_salgrade $$
create procedure insert_salgrade(in start int(10),in max_num int(10))
begin
declare i int default 0; 
 set autocommit = 0;
 ALTER TABLE emp DISABLE KEYS;  
 repeat
 set i = i + 1;
 insert into salgrade values ((start+i) ,(start+i),(start+i));
  until i = max_num
 end repeat;
   commit;
 end $$
delimiter ;
#測試不需要了
#call insert_salgrade(10000,1000000);

#----------------------------------------------


SQL語句的小技巧
  1:在使用group by 分組查詢時,預設分組後, 還會排序,可能會降低速度。
 可以在 gorup by 後增加order by null 可以加快速度。
  2:有些情況下,可以使用連線來替代子查詢。因為使用join,Mysql不需要在記憶體中建立臨時表
   select *from dept,emp where dept.deptno=emp.deptno; [簡單處理連線]
   select *from dept left join emp on dept.depono=emp.deptno;


  3:選擇合適的搜尋引擎
   --Myisam儲存:如果表對事務要求不高,同時是以查詢和新增為主的,我們考慮使用Myisam儲存引擎。
                 支援全文索引(InnDB不支援) ,表鎖
 
   --InnoDB: 對事務要求高,儲存的資料都是重要資料,建議InnoDB
               支援事務,支援外來鍵(Myisam不支援)   行鎖
   
   --Memory: 資料變化頻繁,不需要入庫,同時又頻繁的查詢和修改,我們考慮使用memory引擎。
   
   4:如果你的資料庫的儲存引擎為myisam,則要定期進行碎片整理。
       
             optimize table 表名;


-- 資料庫的定時備份
   --手動備份資料庫(表的)方法
     --cmd控制檯
       mysqldump -u root -p 5678 資料庫的名字[表名 1 表名 2..] >檔案路徑
        比如:把temp資料庫備份到d:temp.bak
       mysqldump -u root -p 5678 temp >d:\temp.bak
        如果希望備份的是資料庫的某幾張表
       mysqldump -u root -p 5678 temp dept >d:\temp.dept.bak


       --若要恢復資料,則:
              source  d:\temp.dept.bak
      --定時完成備份
          1:把備份資料庫的指令,寫入到bat檔案,然後通過工作管理員(linux下用crontab)去定時呼叫檔案。
  寫mytask.bat檔案
   C:\myenv\mysql5.5.27\bin/mysqldump -u root -p5678 temp dept >d:\temp.dept.bak


--表的分割技術
   --水平分割,垂直分離
--增量備份
--定義:mysql資料庫會以二進位制的形式,自動把使用者對mysql資料庫的操作,記錄到檔案,當用戶希望
恢復的時候可以使用備份檔案,進行恢復。
--增量備份會記錄(dml語句,建立表的語句, 不會記錄select)
--記錄的是操作語句本身,操作的時間和位置。
--實際案例,如何進行增量備份,和恢復。
步驟:
 (1)配置my.ini 或者my.conf,啟用二進位制備份。 
   找到mysqld
    port=3306
    新增:log-bin=d:/binlog/mylog(此名字可以任意)
  (2)重新啟動mysql 
      這時會得到檔案,mylog.index(索引檔案):記錄有哪些增量備份檔案
                      mylog.000001:存放使用者對資料庫操作的檔案


  可以使用mysqlbinlog(mysql下的bin目錄)這個程式來檢視備份檔案的內容
  進入到cmd控制檯
  cmd>mysqlbinlog 檔案路徑 | mysql -uroot -p;