Mysql 進階&細節

數據庫引擎

MyISAM與InnoDB選擇問題

MyISAM

  • 很多很多的select/insert操作
  • 一丁點的update/delete操作
  • 不需要事務
  • OLAP(聯機分析處理)
  • 經常查詢數量select count(*) 
  • 需要本地全文搜索
INNODB

  • 混合的查詢操作( selects/updates/deletes/inserts )
  • OLTP(聯機事務處理)
  • 需要事務
  • 通用/多用
  • 極好的數據恢復性
原文:http://www.bigdbahead.com/?p=130

總結下:

當大部分的數據表操作(90%+)是select、insert時,選MyISAM

當數據表CURD四種類型操作都有時,且併發較多時,選InnoDB


  • MyISAM
    • 特性
      1. 不支持事務:MyISAM存儲引擎不支持事務,所以對事務有要求的業務場景不能使用
      2. 表級鎖定:其鎖定機制是表級索引,這雖然可以讓鎖定的實現成本很小但是也同時大大降低了其併發性能
      3. 讀寫互相阻塞:不僅會在寫入的時候阻塞讀取,MyISAM還會在讀取的時候阻塞寫入,但讀本身並不會阻塞另外的讀
      4. 只會緩存索引:MyISAM可以通過key_buffer緩存以大大提高訪問性能減少磁盤IO,但是這個緩存區只會緩存索引,而不會緩存數據
    • 適用場景
      1. 不需要事務支持(不支持)
      2. 併發相對較低(鎖定機制問題)
      3. 數據修改相對較少(阻塞問題)
      4. 以讀爲主
      5. 數據一致性要求不是非常高
    • 最佳實踐
      1. 儘量索引(緩存機制)
      2. 調整讀寫優先級,根據實際需求確保重要操作更優先
      3. 啓用延遲插入改善大批量寫入性能
      4. 儘量順序操作讓insert數據都寫入到尾部,減少阻塞
      5. 分解大的操作,降低單個操作的阻塞時間
      6. 降低併發數,某些高併發場景通過應用來進行排隊機制
      7. 對於相對靜態的數據,充分利用Query Cache可以極大的提高訪問效率
      8. MyISAM的Count只有在全表掃描的時候特別高效,帶有其他條件的count都需要進行實際的數據訪問
  • InnoDB
    • 特性
      1. 具有較好的事務支持:支持4個事務隔離級別,支持多版本讀
      2. 行級鎖定:通過索引實現,全表掃描仍然會是表鎖,注意間隙鎖的影響
      3. 讀寫阻塞與事務隔離級別相關
      4. 具有非常高效的緩存特性:能緩存索引,也能緩存數據
      5. 整個表和主鍵以Cluster方式存儲,組成一顆平衡樹
      6. 所有Secondary Index都會保存主鍵信息
    • 適用場景
      1. 需要事務支持(具有較好的事務特性)
      2. 行級鎖定對高併發有很好的適應能力,但需要確保查詢是通過索引完成
      3. 數據更新較爲頻繁的場景
      4. 數據一致性要求較高
      5. 硬件設備內存較大,可以利用InnoDB較好的緩存能力來提高內存利用率,儘可能減少磁盤 IO
    • 最佳實踐
      1. 主鍵儘可能小,避免給Secondary index帶來過大的空間負擔
      2. 避免全表掃描,因爲會使用表鎖
      3. 儘可能緩存所有的索引和數據,提高響應速度
      4. 在大批量小插入的時候,儘量自己控制事務而不要使用autocommit自動提交
      5. 合理設置innodb_flush_log_at_trx_commit參數值,不要過度追求安全性
      6. 避免主鍵更新,因爲這會帶來大量的數據移動 


MyISAM引擎

缺省情況下,寫操作的優先級要高於讀操作的優先級,即便是先發送的讀請求,後發送的寫請求,此時也會優先處理寫請求,然後再處理讀請求。這就造成一個問題:一旦我發出若干個寫請求,就會堵塞所有的讀請求,直到寫請求全都處理完,纔有機會處理讀請求。此時可以考慮使用

max_write_lock_count=1
有了這樣的設置,當系統處理一個寫操作後,就會暫停寫操作,給讀操作執行的機會。

我們還可以更乾脆點,直接降低寫操作的優先級,給讀操作更高的優先級。
low-priority-updates=1


innodb引擎

l  共享鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。

l  排他鎖(X):允許獲得排他鎖的事務更新數據,阻止其他事務取得相同數據集的共享讀鎖和排他寫鎖。

l  意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。

l  意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

意向鎖是InnoDB自動加的,不需用戶干預。對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖;事務可以通過以下語句顯示給記錄集加共享鎖或排他鎖。

共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。

排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE。



 



優化插入操作:

alter table table_name disable keys;#先關閉表的索引檢查,注意是非唯一索引!

load data infile ‘/path/file’ into table table_name;

alter table table_name anable keys;#再打開索引

1)使用LOAD DATA INFILE從文本下載數據這將比使用插入語句快20倍。

2)使用帶有多個VALUES列表的INSERT語句一次插入幾行這將比使用一個單行插入語句快幾倍。調整bulk_insert_buffer_size變量也能提高(向包含行的表格中)插入的速度。

3)可以對myisam表並行插入Concurrent_insert系統變量可以被設置用於修改concurrent-insert處理。該變量默認設置爲1。如果concurrent_insert被設置爲0,並行插入就被禁用。如果該變量被設置爲2,在表的末端可以並行插入,即便該表的某些行已經被刪除。

4)使用插入延遲

  如果你的客戶不能或無需等待插入完成的時候,這招很有用。當你使用MySQL存儲,並定期運行需要很長時間才能完成的SELECT和UPDATE語句的時候,你會發現這種情況很常見。當客戶使用插入延遲,服務器立刻返回,如果表沒有被其他線程調用,則行會列隊等待被插入。使用插入延遲的另一個好處就是從多個客戶插入的情況會被綁定並記錄在同一個block中。這將比處理多個獨立的插入要快得多。

使用延遲插入操作
DELAYED調節符應用於INSERTREPLACE語句。當DELAYED插入操作到達的時候,

服務器把數據行放入一個隊列中,並立即給客戶端返回一個狀態信息,這樣客戶

端就可以在數據表被真正地插入記錄之前繼續進行操作了。如果讀取者從該數據

表中讀取數據,隊列中的數據就會被保持着,直到沒有讀取者爲止。接着服務器

開始插入延遲數據行(delayed-row)隊列中的數據行。在插入操作的同時,服務器

還要檢查是否有新的讀取請求到達和等待。如果有,延遲數據行隊列就被掛起,

允許讀取者繼續操作。當沒有讀取者的時候,服務器再次開始插入延遲的數據行。

這個過程一直進行,直到隊列空了爲止。
幾點要注意事項:

· INSERT DELAYED應該僅用於指定值清單的INSERT語句。服務器忽略用於INSERT DELAYED...SELECT語句的DELAYED

· 服務器忽略用於INSERT DELAYED...ON DUPLICATE UPDATE語句的DELAYED

· 因爲在行被插入前,語句立刻返回,所以您不能使用LAST_INSERT_ID()來獲取AUTO_INCREMENT值。AUTO_INCREMENT值可能由語句生成。

· 對於SELECT語句,DELAYED行不可見,直到這些行確實被插入了爲止。

· DELAYED在從屬複製服務器中被忽略了,因爲DELAYED不會在從屬服務器中產生與主服務器不一樣的數據。
注意,目前在隊列中的各行只保存在存儲器中,直到它們被插入到表中爲止。這意味着,如果您強行中止了mysqld(例如,使用kill -9

或者如果mysqld意外停止,則所有沒有被寫入磁盤的行都會丟失。

優化插入記錄的速度

插入記錄時,索引,唯一性校驗等都會影響到插入記錄的速度。而且,一次插入多條記錄和多次插入記錄所耗費的時間也不同。

  1.禁用索引

插入數據時,mysql會根據表的索引對插入的記錄進行排序,降低插入速度。解決這個問題可以在插入記錄之前禁用索引,等到插入完畢後在開啓。

alter table 表名 disable keys;    //禁用索引

alter table 表名 enable keys;    //開啓索引

2.禁用唯一性檢查

插入數據時,mysql會對插入的記錄進行唯一性檢查,會降低插入速度。

set unique_checks=0;     //禁用唯一性

set unique_checks=1;    //開啓唯一性

3.優化insert語句

insert into 表名 values 

(......),

(......);

insert into 表名 values (......);

insert into 表名 values (......);

上面兩種插入方法,第一種與數據庫的連接等操作,明顯比第二種快。



5)插入之前將表鎖定(只針對非事務處理型的表)

  這將提高數據庫性能,因爲索引緩衝區只是在所有的插入語句完成後纔對磁盤進行一次刷新。通常情況下,有多少個插入語句就會有多少次索引緩衝區刷新。如果你可以用一個插入語句實現所有行的插入,則無需使用顯式鎖定語句。

  要想更快地對事務型表插入,你應該使用START TRANSACTION和COMMIT語句,而不是LOCK TABLES語句。

 

innodb插入優化

innodb表,上面方法不怎麼湊效 

set unique_checks=0; //關閉唯一性校驗

set autocommit=0; //關閉自動提交



默認設置下,MyISAM表的讀和寫是串行的。

MyISAM中讀寫是相互阻塞的鎖

MyISAM存儲引擎有一個 系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別可以爲0、1或2。

當concurrent_insert設置爲0時,不允許併發插入。

當concurrent_insert設置爲1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設置。

當concurrent_insert設置爲2時,無論MyISAM表中有沒有空洞,都允許在表尾併發插入記錄。

從以上的賦值說明可以得出,在刪除操作不是很多的情況下,可以選用 concurrent_insert =2  ,這樣可以增大併發速度,但是會浪費一部分數據文件的空間,因爲中間刪除的空隙得不到數據填充。這個浪費在刪除操作較多的情況下是非常可觀的,所以在刪除操作較多的情況下應該選用concurrent_insert =1,當然,假如你的對空間不是特別在乎,而且能很好的處理備份問題,選用concurrent_insert =2顯然能提高你的MyISAM的併發讀寫能力。

查看當前concurrent_insert:SHOW GLOBAL VARIABLES LIKE '%concurrent_insert%';

設置concurrent_insert,配置文件(my.cnf)設置concurrent_insert = 2

 

在沒有索引的情況下 myisam的select速度比innodb快30-50%。

索引

對於CHAR和VARCHAR列,建議索引列的前綴(前N個字符)。這更快並且比索引整個列需要較少的磁盤空間。
對於BLOB和TEXT列,必須索引列的前綴,不能索引列的全部。
多列索引(聯合索引)
一個聯合列索引可以認爲是包含通過合併(concatenate)索引列值創建的值的一個排序數組。所以在where條件中只有順序利用索引字段的查詢才能利用聯合索引。


前綴的索引可以使索引更小並且更快,但是它也有明顯的缺點。MySQL不能在ORDER BY或GROUP BY語句中使用前綴索引,也不能把它們作爲覆蓋索引進行使用。


查詢優化

顯示查詢詳細時間
set profiling=1;
show profiles;
show profile;

強制使用索引和禁止使用索引

1、mysql強制使用索引:force index(索引名或者主鍵)

select * from table force index(PRI,ziduan1_index) limit 2;(強制使用索引"PRI和ziduan1_index")

2、mysql禁止某個索引:ignore index(索引名或者主鍵)

select * from table ignore index(PRI,ziduan1_index) limit 2;(禁止使用索引"PRI,ziduan1_index")



當只顯示一個表的數據,關係條件只一個時,使用IN更快

當只顯示一個表的數據如A,關係條件不只一個如ID,col1時,使用IN就不方便了,可以使用EXISTS:  

儘量使用Join 而不是Where來列出關聯條件,特別是多個表聯合的時候

用where進行多表連接的比較時,先全部進行迪卡爾積後,最後再統一過濾。

而inner join是一邊Descartes迪卡爾積,同時這濾,所以臨時記錄會少一些。

limit

select * from mytable where index_col = xxx limit offset, limit;

在偏移量offset較大時,變爲:

select * from mytable where id > offset and id < offset + limit and index_col = xxx

速度槓槓滴(id 不連續會出現問題


 

配置文件優化

Innodb類型的my.cnf:http://www.bigdbahead.com/?p=115

Myisam類型的my.cnf:http://www.bigdbahead.com/?p=643

單個服務的連接數

讓一臺機器多跑幾個MYSQL服務分擔。將服務均衡的規劃到多個MYSQL服務端口上:比如app_1 ==> 3301 app_2 ==> 3302...app_9 ==> 3309。不要害怕機器是否可以支撐,一般情況下,一個1G內存的機器跑上10個MYSQL是很正常的。讓10個MYSQLD承擔1000個併發連接效率要比讓2個MYSQLD承擔1000個效率高的多。當然,這樣也會帶來一些應用編程上的複雜度;


*隨機選取一條,保證效率

1.SELECT t1.* FROM `myisam` as t1
WHERE t1.id >= (floor( RAND() * ((SELECT MAX(id) FROM `myisam`)-(SELECT MIN(id) FROM `myisam`)) + (SELECT MIN(id) FROM `myisam`))) order by t1.id LIMIT 1;(有問題)

2.SELECT t1.* FROM `myisam` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `myisam`)-(SELECT MIN(id) FROM `myisam`))+(SELECT MIN(id) FROM `myisam`)) AS id) AS t2 WHERE t1.id >= t2.id ORDER BY t1.id LIMIT 1;


*去除重複行


*刪除無法連接的髒數據(常用於刪除沒有保持數據一致性的記錄)

delete from wl_askill where inborn = 1 and not exists(select * from wl_pet where wl_pet.pet_id = wl_askill.pet_id)
delete wl_askill from wl_askill left join wl_pet on wl_askill.pet_id = wl_pet.pet_id where wl_askill.inborn = 1 and wl_pet.pet_id is null


//選取不重複記錄
select * from code where id in(select id from code group by item having count(*)=1)

//選取所有重複的記錄 =_,=
select * from code where id not in(select id from code group by item having count(*)=1)

//刪除重複的記錄
1.刪除 指定字段全部重複 的記錄
select distinct * into #Tmp from tableName
drop table tableName
select * into tableName from #Tmp
drop table #Tmp
2.保留最小值
delete from people where peopleId in (select peopleId from people group by peopleId having count(peopleId)>1)  and rowid not in (select min(rowid) from people group by peopleId having count(peopleId )>1)
3.實踐
1.建個臨時表,避免alias別名問題。
create table tmp as select um_id from wl_user_material where um_id in
(SELECT um_id FROM `wl_user_material` b1 where b1.um_id not in(select b2.um_id from wl_user_material b2 group by b2.user_id,b2.prototype_id having count(*) = 1))
and um_id not in(select c1.um_id from (SELECT * FROM `wl_user_material` c2 where c2.um_id not in(select um_id from wl_user_material c3 group by c3.user_id,c3.prototype_id having count(*) = 1) order by c2.user_id asc,c2.num desc) c1 group by c1.user_id,c1.prototype_id);
2.刪除
delete from wl_user_material where um_id in (select um_id from tmp);
3.收尾
drop table tmp;

insert not exists防止插入重複記錄
INSERT INTO clients 
(client_id, client_name, client_type) 
SELECT 10345, 'IBM', 'advertising' 
FROM dual 
WHERE not exists (select * from clients 
where clients.client_id = 10345); 
使用 dual 做表名可以讓你在 select 語句後面直接跟上要插入字段的值,即使這些值還不存在當前表中。


數據庫複製

主從同步、備份

在配置文件里加replicate-do-db and replicate-do-table這兩個參數
複製多個表就寫多行replicate-do-table

 

存儲過程

存儲過程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章