Mysql 雜記(一)

回表:回到主鍵索引樹搜索的過程,稱爲回表  

覆蓋索引:某索引已經覆蓋了查詢需求,稱爲覆蓋索引,例如:select ID from T where k be  tween 3 and 5  在引擎內部使用覆蓋索引在索引K上其實讀了三個記錄,R3~R5(對應的索引k上的記錄項),但  對於MySQL的Server層來說,它就是找引擎拿到了兩條記錄,因此MySQL認爲掃描行數是2  

最左前綴原則:B+Tree這種索引結構,可以利用索引的"最左前綴"來定位記錄  只要滿足最左前綴,就可以利用索引來加速檢索。  最左前綴可以是聯合索引的最左N個字段,也可以是字符串索引的最左M個字符  第一原則是:如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮  採用的。  

索引下推:在MySQL5.6之前,只能從根據最左前綴查詢到ID開始一個個回表。到主鍵索引上  找出數據行,再對比字段值。  MySQL5.6引入的索引下推優化,可以在索引遍歷過程中,對索引中包含的字段先做判斷,直  接過濾掉不滿足條件的記錄,減少回表次數。

二級索引(普通索引)比主鍵索引快:前提是普通索引不需要回表

總結一

1. 表的邏輯結構 ,表 —> 段 —> 段中存在數據段(leaf node segment) ,索引段( Non-leaf  node segment),請問數據段就是主鍵索引的數據, 索引段就是二級索引的數據麼

 2. 建立的每個索引都有要維護一個數據段麼 ?? 那麼新插入一行值 , 豈不是每個索引段都  會維護這個值  

3. 索引的n階表示n個數據頁麼。那是不是插入第一行數據樹高1 ,就是一個數據頁, 插入二  三行,樹高是二,那就是兩個數據頁,而且B+樹只有leaf node存數據,所以父節點實際上有  沒有數據,但是佔一個頁 ,好浪費 , 是我理解有誤麼  

4. 樹高取決於數據頁的大小麼 , 這個不是很能理解 ,數據頁爲16k 。 那麼樹高不是一個定  值了麼,難道還和里面存數據的大小有關麼  

5. 查詢數據的時候,大致的流程細化來說 ,我這麼理解對麼 。 通過優化器到表里的數據段/  索引段取數據 ,數據是按照段->區->頁維度去取 , 取完後先放到數據緩衝池中, 分法查詢葉結點的有序鏈表數組找到行數據返回給用戶 。 當數據量大的時候,會存在不同的  區,取範圍值的時候會到不同的區取頁的數據返回用戶。

回答:

1. 這樣理解也算對,不過要記得 主鍵也是索引的一種哈

2. 是的,所以說索引越多,“維護成本”越大

3. 如果是幾百個兒子節點共用一個父節點,是不是就不會看上去那麼浪費啦

4. 樹高其實取決於葉子樹(數據行數)和“N叉樹”的N。 而N是由頁大小和索引大小決定的。

5. 基本是你說的流程。不過不是“優化器”去取的,是執行器調用引擎,引擎內部才管理了你 說的 段、頁這些數據

總結二 TRUNCATE、Drop、Delete區別


1.drop和delete只是刪除表的數據(定義),drop語句將刪除表的結構、被依賴的約束(constrain)、觸發器 (trigger)、索引(index);依賴於該表的存儲過程/函數將保留,但是變爲invalid狀態。

2.delete語句是DML語言,這個操作會放在rollback segement中,事物提交後才生效;如果有相應的觸發器(trigger),執行的時候將被觸發。truncate、drop是DDL語言,操作後即 生效,原數據不會放到rollback中,不能回滾,操作不會觸發trigger。

3.delete語句不影響表所佔用的extent、高水線(high watermark)保持原位置不動。drop語句將表所佔用的空間全部釋放。truncate語句缺省情況下將空間釋放到minextents的 extent,除非使用reuse storage。truncate會將高水線復位(回到最初)。

4.效率方面:drop > truncate > delete

5.安全性:小心使用drop與truncate,尤其是在 沒有備份的時候,想刪除部分數據可使用delete需要帶上where子句,回滾段要足夠大,想刪除表可以用drop,想保留表只是想刪除表的所有數據、 如果跟事物無關可以使用truncate,如果和事物有關、又或者想觸發 trigger,還是用delete,如果是整理表內部的碎片,可以用truncate跟上reuse stroage,再重新導入、插入數據。

6.delete是DML語句,不會自動提交。drop/truncate都是DDL語句,執行後會自動提交。

7、drop一般用於刪除整體性數據 如表,模式,索引,視圖,完整性限制等;delete用於刪除局部性數據 如表中的某一元組

8、DROP把表結構都刪了;DELETE只是把數據清掉

9、當你不再需要該表時, 用 drop;當你仍要保留該表,但要刪除所有記錄時, 用 truncate;當你要刪除部分記錄時(always with a WHERE clause), 用 delete.

 

總結三

根據加鎖範圍:MySQL里面的鎖可以分爲:全局鎖、表級鎖、行級鎖

一、全局鎖: 對整個數據庫實例加鎖。

MySQL提供加全局讀鎖的方法:Flush tables with read lock(FTWRL) 這個命令可以使整個庫處於只讀狀態。

使用該命令之後,數據更新語句、數據定義語句和更新 類事務的提交語句等操作都會被阻塞。

使用場景:全庫邏輯備份。

風險:

1.如果在主庫備份,在備份期間不能更新,業務停擺

2.如果在從庫備份,備份期間不能執行主庫同步的binlog,導致主從延遲 官方自帶的邏輯備份工具mysqldump,當mysqldump使用參數--single-transaction的時 候,會啓動一個事務,確保拿到一致性視圖。而由於MVCC的支持,這個過程中數據是可以正 常更新的。

一致性讀是好,但是前提是引擎要支持這個隔離級別。 如果要全庫只讀,爲什麼不使用set global readonly=true的方式?

1.在有些系統中,readonly的值會被用來做其他邏輯,比如判斷主備庫。所以修改global變量 的方式影響太大。

2.在異常處理機制上有差異。如果執行FTWRL命令之後由於客戶端發生異常斷開,那麼MySQ L會自動釋放這個全局鎖,整個庫回到可以正常更新的狀態。

而將整個庫設置爲readonly之 後,如果客戶端發生異常,則數據庫就會一直保持readonly狀態,這樣會導致整個庫長時間處 於不可寫狀態,風險較高。

二、表級鎖 MySQL里面表級鎖有兩種

一種是表鎖,一種是元數據所(meta data lock,MDL) 表鎖的語法是:lock tables ... read/write 可以用unlock tables主動釋放鎖,也可以在客戶端斷開的時候自動釋放。lock tables語法除 了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象。

對於InnoDB這種支持行鎖的引擎,一般不使用lock tables命令來控制併發,畢竟鎖住整個表 的影響面還是太大。

MDL:不需要顯式使用,在訪問一個表的時候會被自動加上。 MDL的作用:保證讀寫的正確性。 在對一個表做增刪改查操作的時候,加MDL讀鎖;當要對錶做結構變更操作的時候,加MDL 寫鎖。 讀鎖之間不互斥。讀寫鎖之間,寫鎖之間是互斥的,用來保證變更表結構操作的安全性。 MDL 會直到事務提交纔會釋放,在做表結構變更的時候,一定要小心不要導致鎖住線上查詢 和更新。

總結四

如果mysqldump 備份的是整個schema,某個小表t1只是該schema上其中有一張表  

情況1:  master上對小表t1的DDL傳輸到slave去應用的時刻,mysqldump已經備份完了t1表的數據,  此時slave 同步正常,不會有問題。  

情況2:  master上對小表t1的DDL傳輸到slave去應用的時刻,mysqldump正在備份t1表的數據,此時會  發生MDL 鎖,從庫上t1表的所有操作都會Hang 住。  

情況3:  master 上對小表t1的DDL傳輸到slave去應用的時刻,mysqldump 還沒對t1表進行備份,該D  DL會在slave的t1表應用成功,但是當導出到t1表的時候會報“ERROR 1412 (HY000): Table d  efinition has changed, please retry transaction” 錯誤,導致導出失敗

總結五 update 一個沒有索引的列會怎樣

innodb行級鎖是通過鎖索引記錄實現的。如果update的列沒建索引,即使只update一條記錄  也會鎖定整張表嗎?比如update t set t.name='abc' where t.name='cde'; name字段無索  引。爲何innodb不優化一下,只鎖定name='cde'的列?

第一個問題是好問題,我加到答疑文章中。簡單的回答:是的。但是你可以再往前考慮一下,  如果是 你的update 語句後面加個limit 1, 會怎麼鎖?  Innodb支持行鎖,沒有支持“列鎖” 哈

 

總結六  怎麼減少行鎖對性能的影響?

兩階段鎖:在 InnoDB 事務中,行鎖是在需要的時候才加上的,但並不是不需要了就立刻釋 放, 而是要等到事務結束時才釋放。  建議:如果你的事務中需要鎖多個行,要把最可能造成鎖衝突、最可能影響併發度的鎖盡量往 後放。  

死鎖:當併發系統中不同線程出現循環資源依賴,涉及的線程都在等待別的線程釋放資源時,  就會導致這幾個線程都進入無限等待的狀態。  

     解決方案:  

          1、通過參數 innodb_lock_wait_timeout 根據實際業務場景來設置超時時間,InnoDB引擎默  認值是50s。  

          2、發起死鎖檢測,發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執  行。將參數 innodb_deadlock_detect 設置爲 on,表示開啓這個邏輯(默認是開啓狀態)。  

如何解決熱點行更新導致的性能問題?  

      1、如果你能確保這個業務一定不會出現死鎖,可以臨時把死鎖檢測關閉掉。一般不建議採用

      2、控制併發度,對應相同行的更新,在進入引擎之前排隊。這樣在InnoDB內部就不會有大量  的死鎖檢測工作了。

     3、將熱更新的行數據拆分成邏輯上的多行來減少鎖衝突,但是業務複雜度可能會大大提高。  innodb行級鎖是通過鎖索引記錄實現的,如果更新的列沒建索引是會鎖住整個表的。

 

總結七 普通索引和唯一索引應該怎麼選擇

 

這兩類索引在查詢能力上  是沒差別的,主要考慮的是對更新性能的影響。所以,盡量選擇普通索引

 

對於唯一索引來說,所有的更新操作都要先判斷這個操作是否違反唯一性約束。

比如,要插入  (4,400) 這個記錄,就要先判斷現在表中是否已經存在 k=4 的記錄,而這必須要將數據頁讀入內 存才能判斷。如果都已經讀入到內存了,那直接更新內存會更快,就沒必要使用 change buffer  了。  因此,唯一索引的更新就不能使用 change buffer,實際上也只有普通索引可以使用。  

change buffer 用的是 buffer pool 里的內存,因此不能無限增大。change buffer 的大小,可以通過參數innodb_change_buffer_max_size 來動態設置。這個參數設置爲 50 的時候,表示  change buffer 的大小最多隻能佔用 buffer pool 的 50%。  

現在,你已經理解了 change buffer 的機制,那麼我們再一起來看看如果要在這張表中插入一個  新記錄 (4,400) 的話,InnoDB 的處理流程是怎樣的。  

第一種情況是,這個記錄要更新的目標頁在內存中。

這時,InnoDB 的處理流程如下:  對於唯一索引來說,找到 3 和 5 之間的位置,判斷到沒有衝突,插入這個值,語句執行結  束;  對於普通索引來說,找到 3 和 5 之間的位置,插入這個值,語句執行結束。  這樣看來,普通索引和唯一索引對更新語句性能影響的差別,只是一個判斷,只會耗費微小的  CPU 時間。  但,這不是我們關注的重點。

第二種情況是,這個記錄要更新的目標頁不在內存中。

這時,InnoDB 的處理流程如下:  對於唯一索引來說,需要將數據頁讀入內存,判斷到沒有衝突,插入這個值,語句執行結  束;  對於普通索引來說,則是將更新記錄在 change buffer,語句執行就結束了。 將數據從磁盤讀入內存涉及隨機 IO 的訪問,是數據庫里面成本最高的操作之一。change buffer  因爲減少了隨機磁盤訪問,所以對更新性能的提升是會很明顯的。  之前我就碰到過一件事兒,某個業務的庫內存命中率突(使用hit rate 查看命中率)  然從 99% 降低到了 75%,整個系統處於阻塞狀態,更新語句全部堵住。而探究其原因後,發現這個業務有大量插入數據的操作,是在前一天把其中的某個普通索引改成了唯一索引。

如果某次寫入使用了 change buffer 機制,之後主機異常  重啓,是否會丟失 change buffer 和數據。  這個問題的答案是不會丟失,留言區的很多同學都回答對了。雖然是隻更新內存,但是在事務提  交的時候,我們把 change buffer 的操作也記錄到 redo log 里了,所以崩潰恢復的時候,  change buffer 也能找回來。

總結8  mysql爲什麼有時候會選擇錯索引

一個表有兩個索引  index(a),index(b) 十萬條數據

是優化器的索引選擇,從而牽引出索引統計的  更新機制,以及通過Analyze tabe來進行分析索引。當然有時避免不了可以選擇簡單粗暴的用  Force index來進行索引選擇。 

索引選擇方案,

一、通過Force index強行選擇一  個索引。

二、修改語 句,引導 MySQL 使用我們期望的索引。

現在 order by b,a 這種寫法,要求按照 b,a 排序,就意味着使用這兩個索引都需要排序。因  此,掃描行數成了影響決策的主要條件,於是此時優化器選了只需要掃描 1000 行的索引 a。 

三、通過提供一個更合適的索引來給優化器做出  選擇。

創建一個新的索引或者是刪除索引b。

 

總結9 怎麼給字符串字段加索引

 

要嘛直接加 index(String)  但是這樣索引所佔的空間就會很大,如果使用前綴索引 index2(String(length))  索引佔的空間就會更小,但是相應的掃描行數就會變多,而且不能使用覆蓋索引

使用這個sql來確定length的長度,length合適時查出來的數據越少就越好這樣執行sql時掃描行數就更少

> select count(distinct String(length)) as L from SUser;

但是:

select id,name,email from SUser where email='[email protected]';

就算是index(email(length) ,name) 覆蓋了 name email id的信息,也會進行回表查詢,並不能使用覆蓋索引

 還有就是如果前綴索引不行,區分度不高,可以把 字段(如 身份證 身份證前幾位號碼區分度不高)搞成一個hash值存儲爲一個新字段,缺點是不支持範圍查詢

總結10 爲什麼mysql會抖一下

redo-log內存滿了,不停的要刷髒頁回磁盤。現象就會是發現機器io不高,但是mysql明顯的  卡頓。

 redo-log(保障了 ACID  中的D  也就是持久性)

 

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