mysql優化技巧

mysql 數據庫優化 包括 a.表的設計合理化(符合3NF) b.添加適當索引(index[4種:普通索引 主鍵索引 惟一索引unique  全文索引]) c.分表技術(水平分割,垂直分割) d.讀寫[寫:update/delete/add]分離 e.存儲過程[模塊化編程 能夠提升速度] 數據庫的三層結構 orale MySQL db2 sql server php程序經過dbms(數據庫管理系統)操做數據庫文件,數據庫執行相關操做返回給dbms,而後再返回給PHP dbms 首先編譯PHP代碼,而後執行操做,而後緩存結果,可是編譯很費時間 直接編譯耗時嚴重,因此能夠直接編程寫存儲過程(分頁存儲/觸發器) PHP中執行 call proc('參數')調用存儲過程 利於模塊化編程 f.對MySQL配置優化[配置最大併發數 my.ini] # 最大併發數 # 通常網站調整到 1000 左右 max_connections = 100  # 調整緩存大小 g.MySQL 服務器硬件升級 h.定時的去清除不須要的數據,而且定時進行碎片整理(尤爲對搜索引擎是MyISAM) 數據庫設計的三範式 3NF 表的範式,是首先符合 1NF 才能知足2NF 進一步知足3NF 1NF:     是對屬性的原子性約束,要求屬性(列)具備原子性,不可再分解     只要是關係型數據庫都知足1NF          數據庫的分類:         關係型數據庫   :MySQL/Oracle/db2/informix/sysbase/sql server         非關係型數據庫 :特色是面向對象或者集合的         NoSql數據庫    :MongoDB(特色是面向文檔) 2NF:     是對記錄的惟一性約束,要求記錄有惟一標識,即實體的惟一性 3NF:     是對字段冗餘性的約束,要求字段沒有冗餘,即 表中不要有冗餘數據     也就是說,表的信息,若是可以被推導出來,就不該該單獨的設計一個字段來存放 反 3NF 實際上必定的冗餘是容許的 就是反 3NF 在表的 1對N 狀況下,爲了提升效率,可能會在 1 這表中設計字段 提速 譬如 相冊的瀏覽量  相冊 字段 id name views 相片 字段 id name path views 這裏的views字段,在兩個表中都有,能夠避免顯示相冊瀏覽量的時候再去查詢計算相冊內相片的瀏覽量總和 以此冗餘提升查詢效率 SQL優化的通常步驟 ① 經過 show status 命令瞭解各類SQL的執行頻率 ② 定位執行效率較低的SQL語句 (重點select) ③ 經過 explain 分析低效率的SQL語句的執行狀況 ④ 肯定問題並採起相應的優化措施 SQL語句自己的優化 問題:若是從一個大型項目中,迅速的定位執行速度慢的語句 ① 首先了解MySQL數據庫的一些運行狀態如何查詢 show status     好比想知道當前MySQL運行時間,一共執行了多少次select/update/delete 當前鏈接 等等     經常使用的     show status like 'uptime' 當前MySQL運行時間     show status like 'com_select' 當前MySQL執行了多少次查詢     show status like 'com_insert' 當前MySQL執行了多少次添加     show status like 'com_update' 當前MySQL執行了多少次更新     show status like 'com_delete' 當前MySQL執行了多少次刪除     show status 語法:     show [session|global] status like '';     若是不寫 [session|global] 表示默認是 session 指取出當前窗口的執行狀況     若是想看全部(mysql啓動到如今)的狀況 加上 global     show global status like 'com_insert';     show status like 'connections';試圖鏈接MySQL的鏈接數     顯示慢查詢次數     show status like 'slow_queries'; ② 如何去定位慢查詢     構建一個大表(400萬數據)->存儲過程構建     默認狀況下,MySQL認爲 10秒鐘 纔是一個慢查詢     修改MySQL的慢查詢時間     // 顯示慢查詢時間     show variables like 'long_query_time';     // 修改慢查詢時間     set long_query_time=2;// 即修改慢查詢時間爲2秒     構建大表->大表中記錄有要求,記錄是不一樣纔有用,不然測試效果與真實的相差很大     爲了存儲過程能正常執行,須要修改命令執行結束符修改一下     語法 delimiter $$ $$表示修改後的結束符     當你想使用函數時,能夠指向一個dual表,這個表是亞元表,就是個空表     select rand_string(6) from dual; ③ 這時咱們若是出現一條語句執行時間超過1秒,就會被統計到     call insert_emp 執行存儲過程這個也會被記錄 ④ 若是把慢查詢的 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指定     先關閉MySQL 再從新啓動      若是啓用了慢查詢日誌,默認存放在 my.ini 文件中記錄的位置 即 datadir設置的目錄 ⑤ 測試,能夠看到在日誌中就記錄下咱們的MySQL慢sql語句 優化問題 經過 explain 語句能夠分析,MySQL如何執行你的SQL語句 添加索引 四種索引 主鍵索引 惟一索引 全文索引 普通索引 1.添加 1.1 主鍵索引的添加     當一張表中,把某個列設爲主鍵的時候,則該列就是主鍵索引     若是你建立表時,沒有指定主鍵索引,也能夠在建立表後添加索引     語句:alter table 表名 add primary key (列名); 1.2 普通索引     通常來講,普通索引的建立,是先建立表,而後再建立索引     語句:create index 索引名 on 表名 (列名); 1.3 全文索引     全文索引:主要是針對文件,文本的索引,好比文章     全文索引針對MyISAM有用     如何使用全文索引     錯誤用法:     select * from articles where body like '%mysql%';     這種方法是不會用到全文索引的     正確用法:     // title,body是全文索引,匹配database的句子     select * from articles where match(title,body) against('database');     說明:     1.在MySQL中fulltext索引只針對myISAM生效     2.針對英文生效,對中文須要sphinx(coreseek)技術處理     3.使用方法是match(字段名) against('關鍵字')     4.全文索引有一箇中止詞概念:         由於在一個文本中,建立索引是一個無窮大的數,所以,對一些經常使用詞和字符,         就不會建立,這些詞,稱爲 中止詞。 1.4 惟一索引     當表的某列被指定爲unique約束,這列就是一個惟一索引     惟一索引的列能夠爲null,而且能夠有多個     在建立表後,再去建立惟一索引     建立語法:create unique index 索引名 on 表名 (列名); 2.查詢     ① desc 表名 該方法缺點:不可以顯示索引的名字     ② show index(es) from 表名     ③ show keys from 表名 3.刪除     語法:alter table 表名 drop index 索引名;     主鍵索引刪除:alter table 表名 drop primary key; 4.修改     先刪除,再從新建立 索引注意事項:     索引佔用磁盤空間     對dml(insert/update/delete)語句效率有影響 在哪些列上適合添加索引?     較頻繁的做爲查詢條件字段建立索引     例如 select * from emp where empno=1;     惟一性太差的字段不適合單首創建索引,即便頻繁做爲查詢條件     例如 select * from emp where sex='男';     更新很是頻繁的字段不適合建立索引     例如 select * from emp where logincount=1;     不會出如今where子句中字段不應建立索引 總結:知足如下條件的字段,才能建立索引 a.確定在where條件中常用的 b.該字段的內容不是惟一的幾個值 c.字段內容變化不能太頻繁 使用索引的注意事項 alter table dept add index myind (dname,loc); // dname就是左邊的列,loc是右邊的列 下列狀況有可能使用到索引 a.對於建立的多列索引,只要查詢條件使用了最左邊的列,索引通常就會被使用 explain select * from dept where dname='aaa'; b.對於使用like的查詢,查詢條件若是是'%aaa'則不會使用到索引,'aaa%'會使用到索引 下列狀況不會使用索引 a.若是條件中有or,即便其中有條件帶索引也不會使用 換言之,就是要求使用的全部字段都建立索引,建議:儘可能避免使用or關鍵字 b.對於多列索引,不是使用的第一部分,則不會使用索引 explain select * from dept where loc='aaa';// 多列索引時,loc爲右邊列,索引不會使用到 c.like查詢是以%開頭 若是必定要使用,則使用全文索引去查詢 d.若是列類型是字符串,那必定要在條件中將數據使用引號引發來,不然不使用索引 e.若是MySQL估計使用全表掃描要比使用索引塊,則不使用索引 explain select * from dept where loc='aaa'; explain 語句詳解: 告訴咱們MySQL將使用怎樣的執行計劃來優化query id:1                    查詢序列號 select_type:SIMPLE      查詢類型 table:dept              查詢的表名 type:ALL                掃描的方式 all表示全表掃描 possible_keys:null      可能使用到的索引 key:null                實際使用的索引 key_len:null ref:null rows:10                 該SQL語句掃描了多少行,可能獲得結果數 Extra:Using where       SQL語句的額外信息,好比排序方式filesort等等 select_type 類型 primary  : 子查詢中最外層查詢 subquery : 子查詢內層第一個select,結果不依賴於外部查詢 dependent subquery : 子查詢內層第一個select,依賴於外部查詢 union : union語句中第二個select開始後面全部select simple : 簡單模式 union result : union中合併結果 type 類型 all : 完整的表掃描 一般很差 system : 表僅有一行(=系統表) 這是const聯接類型的一個特例 const : 表最多有一個匹配行 extra 類型 no table : query語句中使用 from dual 或不含任何from子句 Using filesort : 當query中包含 order by 操做,並且沒法利用索引完成排序 impossible WHERE noticed after reading const tables:Mysql query optimizer  經過收集統計信息不可能存在結果 Using temporary : 某些操做必須使用臨時表,常見 group by ,order by Using where : 不用讀取表中全部信息,僅經過索引就能夠獲取所需數據 explain能夠幫助咱們在不真正執行某個SQL語句時,就知道MySQL怎樣執行,利於咱們去分析SQL指令 查看索引的使用狀況 show status like 'Handler_read%'; handler_read_key:這個值越高越好,表明使用索引查詢到的次數 handler_read_rnd_next:這個值越高,說明查詢低效 SQL語句的小技巧 ① 優化 group by 語句 默認狀況下,MySQL對全部的group by col1,col2 進行排序,這與在查詢中指定 order by col1,col2 相似 若是查詢中包括 group by 但用戶想盡可能避免排序結果的消耗,則可使用 order by null 禁止排序 ② 有些狀況下,可使用鏈接來替代子查詢     由於使用 join MySQL不須要在內存中建立臨時表 ③ 若是想要在含有 or 的查詢語句中利用索引,則 or 之間的每一個條件列都必須用到索引,     若是沒有索引,則應該考慮增長索引。 如何選擇MySQL的存儲引擎 1.myISAM:     若是表對事務要求不高,同時是以查詢和添加爲主的。     好比 BBS中的發帖表,回覆表 2.InnoDB:     對事務要求高,保存的數據都是重要數據     好比 訂單表,帳戶表 3.Memory:     數據變化頻繁,不須要入庫同時又常常查詢和修改 myISAM 與 InnoDB 主要區別 1.myisam 批量插入速度快,InnoDB慢,myisam插入數據時不排序 2.InnoDB支持事務 3.myisam支持全文索引 4.鎖機制,myisam是表鎖,InnoDB是行鎖 5.myisam不支持外鍵,InnoDB支持外鍵 外鍵 classes表 create table classes(         id int unsigned not null auto_increment primary key,         name varchar(64) not null     )engine=innoDB; insert into classes values (1,'aaa'); stu表 create table student(         id int unsigned not null auto_increment primary key,         name varchar(64) not null,         classid int unsigned not null,         foreign key (classid) references classes(id) /* 外鍵 */     )engine=innoDB; 當設置了外鍵的時候,企圖添加一個外鍵沒有的數據,會報錯,沒法插入數據 insert into student values (1,'hello',1); 這個是正確的  insert into student values (1,'hello',2); 當classes表中id=2不存在時,這個是錯誤的  在PHP開發中,一般不設置外鍵,一般在程序中保持數據的一致性。 選擇合適的數據類型 ① 在精度要求高的應用中,建議使用定點數來存儲數值,以保證數據的準確性。deciaml精度比float高,儘可能使用 ② 對於存儲引擎是myisam的數據庫,若是常常作刪除和修改記錄的操做,     要定時執行optimize table table_name;功能對錶進行碎片整理。 ③ 日期類型要根據實際須要選擇可以知足應用的最小存儲的早期類型     create table bbs (id int unsigned not null ,con varchar(1024) , pub_time int ); php備份數據庫 ① 手動備份數據庫(表)的方法     cmd控制檯     mysqldump -uroot -proot 數據庫[表名1 表名2 ...] > 文件路徑     例如 把 temp 數據庫備份到 d:/temp.bak      mysqldump -uroot -proot temp > d:/temp.bak     若是但願備份數據庫中某幾張表     mysqldump -uroot -proot temp dept > d:/temp.dept.bak     如何使用備份文件恢復數據     在MySQL控制檯     source d:/temp.dept.bak ② 使用定時器自動完成     a.把備份數據庫的指令,寫入到bat文件,而後經過任務 crontab     mytask.bat 內容是     d:/myweb/mysql/bin/mysqldump -uroot -p2012o912@ bigtest dept > d:/bigtest.dept.bak     注意事項:若是mysqldump.exe 文件路徑有空格,則必定要使用雙引號包起來     把 mytask.bat 作成一個任務,並定時調用 例如 天天 2:00 調用一次     windows下:打開控制面板--任務計劃--添加任務計劃--下一步--瀏覽--找到mytask.bat--選擇執行任務時間                 --下一步--起始時間--下一步--輸入密碼--下一步--完成     如今的問題是,每次都是覆蓋原來的備份文件,不利於分時段備份,解決這種問題     能夠採用以下方式解決:     b.創建一個 mytask.php 文件     內容是     <?php         date_default_timezone_set('PRC');         $bakfilename = date('YmdHis',time());         $command = "d:\myweb\mysql\bin\mysqldump -uroot -p2012o912@ bigtest dept > f:\\{$bakfilename}.bak";         exec($command);     ?>     創建一個bat文件 mytask2.bat,內容是     d:\myweb\php\php.exe d:\myweb\apache\htdocs\mytask.php     該方法是利用PHP自身的php.exe執行PHP文件     而後將mytask2.bat作成一個任務,定時的去執行     linux 下 使用 crontab命令     crontab 0 0 0 0 0 mytask.sh mysql中當前時間戳函數 unix_timestamp(); 案例 定時發送郵件 1.怎樣能夠定時的去檢索哪些郵件該發送:     只能每隔必定時間就看看哪些郵件該發送 mailtask.php     在PHP中,有一個函數mail,用於發送郵件,實際中經過phpmailer進行發送郵件     要正確使用phpmailer發送郵件,須要知足以下條件     a.自己機器是能夠聯網的     b.須要搭建本身的 SMTP 郵件服務器 表的分割 1.水平分割 案例 大數據量的用戶表 三張表:qqlogin0,qqlogin1,qqlogin2 將用戶id%3,按結果放入不一樣的表當中 create table qqlogin0(         id int unsigned not null primary key,/* 這個id不能設置自增加 */         name varchar(32) not null default '',         pwd varchar(32) not null default ''     )engine=myisam default charset=utf8; create table qqlogin1(         id int unsigned not null primary key,/* 這個id不能設置自增加 */         name varchar(32) not null default '',         pwd varchar(32) not null default ''     )engine=myisam default charset=utf8; create table qqlogin2(         id int unsigned not null primary key,/* 這個id不能設置自增加 */         name varchar(32) not null default '',         pwd varchar(32) not null default ''     )engine=myisam default charset=utf8; 開發 addUser.php ,由於在添加用戶時,各個用戶id應該確認下,一般咱們使用一個輔助表 uuid 表, 它能夠幫助咱們生成一個編號 uuid表: create table uuid (         id int unsigned not null auto_increment primary key     )engine=myisam default charset=utf8; 分享一句話: 咱們在提供檢索時,應該根據業務的需求,找到分表的標準,並在檢索頁面約束用戶的檢索方式,並且要配合分頁 若是有大表檢索的需求,也是少數的。 添加用戶時:addUser.php <?php $conn = mysql_connect('localhost','root','2012o912@'); if (!$conn)  {     die('mysql connect error'); } mysql_select_db('temp',$conn); $sql = "insert into uuid values (null)"; $res = mysql_query($sql,$conn); if ($res)  {     $uuid = mysql_insert_id();     $tablename = 'qqlogin'.$uuid%4;     $sql = "insert into $tablename values ($uuid,'abc','abc')";     $res = mysql_query($sql,$conn);     if ($res)      {         echo 'insert success';     }else      {         echo 'insert user error';     } }else  {     die('insert error'); } ?> 查詢用戶時,checkUser.php <?php $conn = mysql_connect('localhost','root','2012o912@'); if (!$conn)  {     die('mysql connect error'); } mysql_select_db('temp',$conn); $qqid = intval($_GET['id']); $tablename = 'qqlogin'.$qqid%4; $sql = "select * from $tablename where id='$qqid'"; $res = mysql_query($sql,$conn); if ($res)  {     $row = mysql_fetch_assoc($res);     print_r($row);  }else  {     die('no user'); } ?> 2.垂直分割 案例 學生答題系統 考試結果表 id  stuno   questionid       answer(text)      grade 1   1       20               [結果。。。]      30 問題表 id     question 20     請寫一篇散文 需求: 查處1號學生20題得分狀況,但answer字段內容很是大,對查詢速度有影響 解決:把answer(對查詢速度影響較大的字段)單獨的提出來,放到另一張表 回答表 answer id answer 1  結果。。。 相應的修改考試結果表 id  stuno  questionid grade 1   1      20         30 總結:把某個表的某些字段,這些字段,在查詢時,並不實時關心,但數據量很大, 咱們建議你們能夠 把這些字段單獨的放到另一張表,從而提升效率。可是不要忘記關聯關係 表的字段定義原則是保小不保大,儘可能節省空間 查看索引 mysql> show index from tblname; mysql> show keys from tblname; · Table 表的名稱。 · Non_unique 若是索引不能包括重複詞,則爲0。若是能夠,則爲1。 · Key_name 索引的名稱。 · Seq_in_index 索引中的列序列號,從1開始。 · Column_name 列名稱。 · Collation 列以什麼方式存儲在索引中。在MySQL中,有值‘A’(升序)或NULL(無分類)。 · Cardinality 索引中惟一值的數目的估計值。經過運行ANALYZE TABLE或myisamchk -a能夠更新。基數根據被存儲爲整數的統計數據來計數,因此即便對於小型表,該值也沒有必要是精確的。基數越大,當進行聯合時,MySQL使用該索引的機 會就越大。 · Sub_part 若是列只是被部分地編入索引,則爲被編入索引的字符的數目。若是整列被編入索引,則爲NULL。 · Packed 指示關鍵字如何被壓縮。若是沒有被壓縮,則爲NULL。 · Null 若是列含有NULL,則含有YES。若是沒有,則該列含有NO。 · Index_type 用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。 · Comment 關於網站的圖片和視頻的存放: 咱們的數據表中,通常只是存放圖片或者視頻的路徑,真正的資源是放在文件系統上的,每每會配合獨立的服務器 優化MySQL的配置 my.ini port=3306 默認端口是3306, 若是想修改端口 port=3309,在mysql_connect('localhost:3309','root','root');要注意 修改最大鏈接數 max_connections=100 最大鏈接數能夠修改到2000,再大沒有用 query_cache_size=15M 這個是查詢緩存的大小 innodb參數也能夠調大如下兩個參數 innodb_additional_mem_pool_size=64M innodb_buffer_pool_size=1G myisam須要調整 key_buffer_size 調整參數還要看狀態,用 show status 能夠看到當前狀態,以決定該調整哪些參數 若是你的機器內存超過4G,則應當採用64位操做系統和64位MySQL5.5.19 讀寫分離 若是數據庫壓力很大,一臺機器支撐不了,能夠用MySQL複製實現多臺機器同步,將數據庫壓力分散 增量備份 定義:MySQL數據庫會以二進制的形式,把用戶對MySQL數據庫的操做,記錄到文件       當用戶但願恢復的時候,可使用備份文件進行恢復。 增量備份會記錄 dml語句,建立表的語句,但不會記錄select語句 記錄的是 a.操做語句自己 b.操做的時間 c.操做的位置 position 案例:如何進行增量備份和恢復 步驟: 1.配置my.ini 文件 或者 my.conf 啓用二進制備份 在my.ini 中增長一句話 #指定備份文件放在哪一個目錄下 log-bin="d:/backup/mylog" 2.重啓MySQL獲得文件      d:/backup/mylog.index 索引文件 有哪裏增量備份文件     d:/backup/mylog.000001 存放用戶對數據庫操做的文件 3.當咱們進行操做(除了select)     可使用 mysql/bin/mysqlbinlog 程序來查看備份文件的內容     進入到 cmd 控制檯 cmd>mysqlbinlog 備份文件路徑     在這裏 end_log_pos 526表示執行某個命令在文件中的位置,能夠根據這個位置恢復相應的數據     set timestamp=xxxxxxx 這個表示命令執行時間     MySQL把每個操做的時間記錄下來,同時分配了一個位置position     咱們能夠根據時間或者位置來恢復     a.根據時間點恢復         在 mylog.000001 文件開始 到 2013-05-15 14:25:00 結束         mysqlbinlog --stop-datetime="2013-05-15 14:25:00"         d:/backup/mylog.000001 | mysql -uroot -p         在 mylog.000001 文件 2013-05-15 14:25:00 開始到文件結束         mysqlbinlog --start-datetime="2013-05-15 14:25:00"          d:/backup/mylog.000001 | mysql -uroot -p         恢復某個時間段數據         mysqlbinlog --start-datetime="2013-05-15 14:24:00" --stop-datetime="2013-05-15 14:25:00"          d:\backup\mylog.000001 | mysql -uroot -p     b.根據位置恢復         在 mylog.000001 文件開始->21114         mysqlbinlog --stop-position="21114" d:/backup/mylog.000001 | mysql -uroot -p                  在mylog.000001 文件2111開始->最後         mysqlbinlog --start-position="2111" d:/backup/mylog.000001 | mysql -uroot -p         在mylog.000001 文件 751->1195 之間         mysqlbinlog --start-position="751" --stop-position="1195" | mysql -uroot -p 4.如何在工做中將全備份和增量備份配合使用     方案:每週一作一個全備份:mysqldump,           而後啓用增量備份,把過時時間設爲>=7,最好設大一點           若是出現數據庫崩潰,能夠經過時間或者位置恢復 須要去看增量日誌文件
相關文章
相關標籤/搜索