国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

DBA整理的萬字詳解MySQL性能優(yōu)化,值得收藏!

共 17794字,需瀏覽 36分鐘

 ·

2021-08-03 23:49

點擊下方“IT牧場”,選擇“設為星標”

作者:LanceToBigData

來源:cnblogs.com/zhangyinhua/p/7620964.html

說起MySQL的查詢優(yōu)化,相信大家積累一堆技巧:不能使用SELECT *、不使用NULL字段、合理創(chuàng)建索引、為字段選擇合適的數(shù)據(jù)類型….. 你是否真的理解這些優(yōu)化技巧?是否理解其背后的工作原理?在實際場景下性能真有提升嗎?

我想未必。因而理解這些優(yōu)化建議背后的原理就尤為重要,希望本文能讓你重新審視這些優(yōu)化建議,并在實際業(yè)務場景下合理的運用。


MySQL邏輯架構

如果能在頭腦中構建一幅MySQL各組件之間如何協(xié)同工作的架構圖,有助于深入理解MySQL服務器。下圖展示了MySQL的邏輯架構圖。

MySQL邏輯架構,來自:高性能MySQL

MySQL邏輯架構整體分為三層,最上層為客戶端層,并非MySQL所獨有,諸如:連接處理、授權認證、安全等功能均在這一層處理。

MySQL大多數(shù)核心服務均在中間這一層,包括查詢解析、分析、優(yōu)化、緩存、內(nèi)置函數(shù)(比如:時間、數(shù)學、加密等函數(shù))。所有的跨存儲引擎的功能也在這一層實現(xiàn):存儲過程、觸發(fā)器、視圖等。

最下層為存儲引擎,其負責MySQL中的數(shù)據(jù)存儲和提取。和Linux下的文件系統(tǒng)類似,每種存儲引擎都有其優(yōu)勢和劣勢。中間的服務層通過API與存儲引擎通信,這些API接口屏蔽了不同存儲引擎間的差異。


MySQL查詢過程

我們總是希望MySQL能夠獲得更高的查詢性能,最好的辦法是弄清楚MySQL是如何優(yōu)化和執(zhí)行查詢的。一旦理解了這一點,就會發(fā)現(xiàn):很多的查詢優(yōu)化工作實際上就是遵循一些原則讓MySQL的優(yōu)化器能夠按照預想的合理方式運行而已。

當向MySQL發(fā)送一個請求的時候,MySQL到底做了些什么呢?

MySQL查詢過程

客戶端/服務端通信協(xié)議

MySQL客戶端/服務端通信協(xié)議是“半雙工”的:在任一時刻,要么是服務器向客戶端發(fā)送數(shù)據(jù),要么是客戶端向服務器發(fā)送數(shù)據(jù),這兩個動作不能同時發(fā)生。一旦一端開始發(fā)送消息,另一端要接收完整個消息才能響應它,所以我們無法也無須將一個消息切成小塊獨立發(fā)送,也沒有辦法進行流量控制。

客戶端用一個單獨的數(shù)據(jù)包將查詢請求發(fā)送給服務器,所以當查詢語句很長的時候,需要設置max_allowed_packet參數(shù)。但是需要注意的是,如果查詢實在是太大,服務端會拒絕接收更多數(shù)據(jù)并拋出異常。

與之相反的是,服務器響應給用戶的數(shù)據(jù)通常會很多,由多個數(shù)據(jù)包組成。但是當服務器響應客戶端請求時,客戶端必須完整的接收整個返回結果,而不能簡單的只取前面幾條結果,然后讓服務器停止發(fā)送。因而在實際開發(fā)中,盡量保持查詢簡單且只返回必需的數(shù)據(jù),減小通信間數(shù)據(jù)包的大小和數(shù)量是一個非常好的習慣,這也是查詢中盡量避免使用SELECT *以及加上LIMIT限制的原因之一。

查詢緩存

在解析一個查詢語句前,如果查詢緩存是打開的,那么MySQL會檢查這個查詢語句是否命中查詢緩存中的數(shù)據(jù)。如果當前查詢恰好命中查詢緩存,在檢查一次用戶權限后直接返回緩存中的結果。這種情況下,查詢不會被解析,也不會生成執(zhí)行計劃,更不會執(zhí)行。

MySQL將緩存存放在一個引用表(不要理解成table,可以認為是類似于HashMap的數(shù)據(jù)結構),通過一個哈希值索引,這個哈希值通過查詢本身、當前要查詢的數(shù)據(jù)庫、客戶端協(xié)議版本號等一些可能影響結果的信息計算得來。所以兩個查詢在任何字符上的不同(例如:空格、注釋),都會導致緩存不會命中。

如果查詢中包含任何用戶自定義函數(shù)、存儲函數(shù)、用戶變量、臨時表、mysql庫中的系統(tǒng)表,其查詢結果都不會被緩存。比如函數(shù)NOW()或者CURRENT_DATE()會因為不同的查詢時間,返回不同的查詢結果,再比如包含CURRENT_USER或者CONNECION_ID()的查詢語句會因為不同的用戶而返回不同的結果,將這樣的查詢結果緩存起來沒有任何的意義。

既然是緩存,就會失效,那查詢緩存何時失效呢? MySQL的查詢緩存系統(tǒng)會跟蹤查詢中涉及的每個表,如果這些表(數(shù)據(jù)或結構)發(fā)生變化,那么和這張表相關的所有緩存數(shù)據(jù)都將失效。正因為如此,在任何的寫操作時,MySQL必須將對應表的所有緩存都設置為失效。如果查詢緩存非常大或者碎片很多,這個操作就可能帶來很大的系統(tǒng)消耗,甚至導致系統(tǒng)僵死一會兒。而且查詢緩存對系統(tǒng)的額外消耗也不僅僅在寫操作,讀操作也不例外:

  • 任何的查詢語句在開始之前都必須經(jīng)過檢查,即使這條SQL語句永遠不會命中緩存
  • 如果查詢結果可以被緩存,那么執(zhí)行完成后,會將結果存入緩存,也會帶來額外的系統(tǒng)消耗

基于此,我們要知道并不是什么情況下查詢緩存都會提高系統(tǒng)性能,緩存和失效都會帶來額外消耗,只有當緩存帶來的資源節(jié)約大于其本身消耗的資源時,才會給系統(tǒng)帶來性能提升。但要如何評估打開緩存是否能夠帶來性能提升是一件非常困難的事情,也不在本文討論的范疇內(nèi)。如果系統(tǒng)確實存在一些性能問題,可以嘗試打開查詢緩存,并在數(shù)據(jù)庫設計上做一些優(yōu)化,比如:

  • 用多個小表代替一個大表,注意不要過度設計
  • 批量插入代替循環(huán)單條插入
  • 合理控制緩存空間大小,一般來說其大小設置為幾十兆比較合適
  • 可以通過SQL_CACHESQL_NO_CACHE來控制某個查詢語句是否需要進行緩存

最后的忠告是不要輕易打開查詢緩存,特別是寫密集型應用。如果你實在是忍不住,可以將query_cache_type設置為DEMAND,這時只有加入SQL_CACHE的查詢才會走緩存,其他查詢則不會,這樣可以非常自由地控制哪些查詢需要被緩存。

當然查詢緩存系統(tǒng)本身是非常復雜的,這里討論的也只是很小的一部分,其他更深入的話題,比如:緩存是如何使用內(nèi)存的?如何控制內(nèi)存的碎片化?事務對查詢緩存有何影響等等,讀者可以自行閱讀相關資料,這里權當拋磚引玉吧。

語法解析和預處理

MySQL通過關鍵字將SQL語句進行解析,并生成一顆對應的解析樹。這個過程解析器主要通過語法規(guī)則來驗證和解析。比如SQL中是否使用了錯誤的關鍵字或者關鍵字的順序是否正確等等。預處理則會根據(jù)MySQL規(guī)則進一步檢查解析樹是否合法。比如檢查要查詢的數(shù)據(jù)表和數(shù)據(jù)列是否存在等等。

查詢優(yōu)化

經(jīng)過前面的步驟生成的語法樹被認為是合法的了,并且由優(yōu)化器將其轉(zhuǎn)化成查詢計劃。多數(shù)情況下,一條查詢可以有很多種執(zhí)行方式,最后都返回相應的結果。優(yōu)化器的作用就是找到這其中最好的執(zhí)行計劃。

MySQL使用基于成本的優(yōu)化器,它嘗試預測一個查詢使用某種執(zhí)行計劃時的成本,并選擇其中成本最小的一個。在MySQL可以通過查詢當前會話的last_query_cost的值來得到其計算當前查詢的成本。

Mysql代碼

mysql> select * from t_message limit 10;    
...省略結果集    
  
mysql> show status like 'last_query_cost';    
+-----------------+-------------+    
| Variable_name   | Value       |    
+-----------------+-------------+    
| Last_query_cost | 6391.799000 |    
+-----------------+-------------+    

示例中的結果表示優(yōu)化器認為大概需要做6391個數(shù)據(jù)頁的隨機查找才能完成上面的查詢。這個結果是根據(jù)一些列的統(tǒng)計信息計算得來的,這些統(tǒng)計信息包括:每張表或者索引的頁面?zhèn)€數(shù)、索引的基數(shù)、索引和數(shù)據(jù)行的長度、索引的分布情況等等。

有非常多的原因會導致MySQL選擇錯誤的執(zhí)行計劃,比如統(tǒng)計信息不準確、不會考慮不受其控制的操作成本(用戶自定義函數(shù)、存儲過程)、MySQL認為的最優(yōu)跟我們想的不一樣(我們希望執(zhí)行時間盡可能短,但MySQL值選擇它認為成本小的,但成本小并不意味著執(zhí)行時間短)等等。

MySQL的查詢優(yōu)化器是一個非常復雜的部件,它使用了非常多的優(yōu)化策略來生成一個最優(yōu)的執(zhí)行計劃:

  • 重新定義表的關聯(lián)順序(多張表關聯(lián)查詢時,并不一定按照SQL中指定的順序進行,但有一些技巧可以指定關聯(lián)順序)
  • 優(yōu)化MIN()MAX()函數(shù)(找某列的最小值,如果該列有索引,只需要查找B+Tree索引最左端,反之則可以找到最大值,具體原理見下文)
  • 提前終止查詢(比如:使用Limit時,查找到滿足數(shù)量的結果集后會立即終止查詢)
  • 優(yōu)化排序(在老版本MySQL會使用兩次傳輸排序,即先讀取行指針和需要排序的字段在內(nèi)存中對其排序,然后再根據(jù)排序結果去讀取數(shù)據(jù)行,而新版本采用的是單次傳輸排序,也就是一次讀取所有的數(shù)據(jù)行,然后根據(jù)給定的列排序。對于I/O密集型應用,效率會高很多)

隨著MySQL的不斷發(fā)展,優(yōu)化器使用的優(yōu)化策略也在不斷的進化,這里僅僅介紹幾個非常常用且容易理解的優(yōu)化策略,其他的優(yōu)化策略,大家自行查閱吧。

查詢執(zhí)行引擎

在完成解析和優(yōu)化階段以后,MySQL會生成對應的執(zhí)行計劃,查詢執(zhí)行引擎根據(jù)執(zhí)行計劃給出的指令逐步執(zhí)行得出結果。整個執(zhí)行過程的大部分操作均是通過調(diào)用存儲引擎實現(xiàn)的接口來完成,這些接口被稱為handlerAPI。查詢過程中的每一張表由一個handler實例表示。

實際上,MySQL在查詢優(yōu)化階段就為每一張表創(chuàng)建了一個handler實例,優(yōu)化器可以根據(jù)這些實例的接口來獲取表的相關信息,包括表的所有列名、索引統(tǒng)計信息等。存儲引擎接口提供了非常豐富的功能,但其底層僅有幾十個接口,這些接口像搭積木一樣完成了一次查詢的大部分操作。

返回結果給客戶端

查詢執(zhí)行的最后一個階段就是將結果返回給客戶端。即使查詢不到數(shù)據(jù),MySQL仍然會返回這個查詢的相關信息,比如改查詢影響到的行數(shù)以及執(zhí)行時間等等。

如果查詢緩存被打開且這個查詢可以被緩存,MySQL也會將結果存放到緩存中。

結果集返回客戶端是一個增量且逐步返回的過程。有可能MySQL在生成第一條結果時,就開始向客戶端逐步返回結果集了。這樣服務端就無須存儲太多結果而消耗過多內(nèi)存,也可以讓客戶端第一時間獲得返回結果。需要注意的是,結果集中的每一行都會以一個滿足①中所描述的通信協(xié)議的數(shù)據(jù)包發(fā)送,再通過TCP協(xié)議進行傳輸,在傳輸過程中,可能對MySQL的數(shù)據(jù)包進行緩存然后批量發(fā)送。

回頭總結一下MySQL整個查詢執(zhí)行過程,總的來說分為5個步驟:

  • 客戶端向MySQL服務器發(fā)送一條查詢請求
  • 服務器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲在緩存中的結果。否則進入下一階段
  • 服務器進行SQL解析、預處理、再由優(yōu)化器生成對應的執(zhí)行計劃
  • MySQL根據(jù)執(zhí)行計劃,調(diào)用存儲引擎的API來執(zhí)行查詢
  • 將結果返回給客戶端,同時緩存查詢結果

性能優(yōu)化建議

看了這么多,你可能會期待給出一些優(yōu)化手段,是的,下面會從3個不同方面給出一些優(yōu)化建議。但請等等,還有一句忠告要先送給你:不要聽信你看到的關于優(yōu)化的“絕對真理”,包括本文所討論的內(nèi)容,而應該是在實際的業(yè)務場景下通過測試來驗證你關于執(zhí)行計劃以及響應時間的假設。

Scheme設計與數(shù)據(jù)類型優(yōu)化

選擇數(shù)據(jù)類型只要遵循小而簡單的原則就好,越小的數(shù)據(jù)類型通常會更快,占用更少的磁盤、內(nèi)存,處理時需要的CPU周期也更少。越簡單的數(shù)據(jù)類型在計算時需要更少的CPU周期,比如,整型就比字符操作代價低,因而會使用整型來存儲ip地址,使用DATETIME來存儲時間,而不是使用字符串。

在公眾號后端架構師后臺回復“面試”,獲取一份驚喜禮包。

這里總結幾個可能容易理解錯誤的技巧:

  • 通常來說把可為NULL的列改為NOT NULL不會對性能提升有多少幫助,只是如果計劃在列上創(chuàng)建索引,就應該將該列設置為NOT NULL。
  • 對整數(shù)類型指定寬度,比如INT(11),沒有任何卵用。INT使用16為存儲空間,那么它的表示范圍已經(jīng)確定,所以INT(1)INT(20)對于存儲和計算是相同的。
  • UNSIGNED表示不允許負值,大致可以使正數(shù)的上限提高一倍。比如TINYINT存儲范圍是通常來講,沒有太大的必要使用DECIMAL數(shù)據(jù)類型。即使是在需要存儲財務數(shù)據(jù)時,仍然可以使用BIGINT。比如需要精確到萬分之一,那么可以將數(shù)據(jù)乘以一百萬然后使用TIMESTAMP使用4個字節(jié)存儲空間,DATETIME使用8個字節(jié)存儲空間。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范圍小得多,而且TIMESTAMP的值因時區(qū)不同而不同。
  • 大多數(shù)情況下沒有使用枚舉類型的必要,其中一個缺點是枚舉的字符串列表是固定的,添加和刪除字符串(枚舉選項)必須使用ALTER TABLE(如果只只是在列表末尾追加元素,不需要重建表)。
  • schema的列不要太多。原因是存儲引擎的API工作時需要在服務器層和存儲引擎層之間通過行緩沖格式拷貝數(shù)據(jù),然后在服務器層將緩沖內(nèi)容解碼成各個列,這個轉(zhuǎn)換過程的代價是非常高的。如果列太多而實際使用的列又很少的話,有可能會導致CPU占用過高。
  • 大表ALTER TABLE非常耗時,MySQL執(zhí)行大部分修改表結果操作的方法是用新的結構創(chuàng)建一個張空表,從舊表中查出所有的數(shù)據(jù)插入新表,然后再刪除舊表。尤其當內(nèi)存不足而表又很大,而且還有很大索引的情況下,耗時更久。當然有一些奇淫技巧可以解決這個問題,有興趣可自行查閱。

創(chuàng)建高性能索引

索引是提高MySQL查詢性能的一個重要途徑,但過多的索引可能會導致過高的磁盤使用率以及過高的內(nèi)存占用,從而影響應用程序的整體性能。應當盡量避免事后才想起添加索引,因為事后可能需要監(jiān)控大量的SQL才能定位到問題所在,而且添加索引的時間肯定是遠大于初始添加索引所需要的時間,可見索引的添加也是非常有技術含量的。

接下來將向你展示一系列創(chuàng)建高性能索引的策略,以及每條策略其背后的工作原理。但在此之前,先了解與索引相關的一些算法和數(shù)據(jù)結構,將有助于更好的理解后文的內(nèi)容。推薦:帶你從頭到尾捋一遍MySQL索引結構!

索引相關的數(shù)據(jù)結構和算法

通常我們所說的索引是指B-Tree索引,它是目前關系型數(shù)據(jù)庫中查找數(shù)據(jù)最為常用和有效的索引,大多數(shù)存儲引擎都支持這種索引。使用B-Tree這個術語,是因為MySQL在CREATE TABLE或其它語句中使用了這個關鍵字,但實際上不同的存儲引擎可能使用不同的數(shù)據(jù)結構,比如InnoDB就是使用的B+Tree

B+Tree中的B是指balance,意為平衡。需要注意的是,B+樹索引并不能找到一個給定鍵值的具體行,它找到的只是被查找數(shù)據(jù)行所在的頁,接著數(shù)據(jù)庫會把頁讀入到內(nèi)存,再在內(nèi)存中進行查找,最后得到要查找的數(shù)據(jù)。

在介紹B+Tree前,先了解一下二叉查找樹,它是一種經(jīng)典的數(shù)據(jù)結構,其左子樹的值總是小于根的值,右子樹的值總是大于根的值,如下圖①。如果要在這課樹中查找值為5的記錄,其大致流程:先找到根,其值為6,大于5,所以查找左子樹,找到3,而5大于3,接著找3的右子樹,總共找了3次。同樣的方法,如果查找值為8的記錄,也需要查找3次。所以二叉查找樹的平均查找次數(shù)為(3 + 3 + 3 + 2 + 2 + 1) / 6 = 2.3次,而順序查找的話,查找值為2的記錄,僅需要1次,但查找值為8的記錄則需要6次,所以順序查找的平均查找次數(shù)為:(1 + 2 + 3 + 4 + 5 + 6) / 6 = 3.3次,因為大多數(shù)情況下二叉查找樹的平均查找速度比順序查找要快。

二叉查找樹和平衡二叉樹

由于二叉查找樹可以任意構造,同樣的值,可以構造出如圖②的二叉查找樹,顯然這棵二叉樹的查詢效率和順序查找差不多。若想二叉查找數(shù)的查詢性能最高,需要這棵二叉查找樹是平衡的,也即平衡二叉樹(AVL樹)。

平衡二叉樹首先需要符合二叉查找樹的定義,其次必須滿足任何節(jié)點的兩個子樹的高度差不能大于1。顯然圖②不滿足平衡二叉樹的定義,而圖①是一課平衡二叉樹。平衡二叉樹的查找性能是比較高的(性能最好的是最優(yōu)二叉樹),查詢性能越好,維護的成本就越大。比如圖①的平衡二叉樹,當用戶需要插入一個新的值9的節(jié)點時,就需要做出如下變動。

平衡二叉樹旋轉(zhuǎn)

通過一次左旋操作就將插入后的樹重新變?yōu)槠胶舛鏄涫亲詈唵蔚那闆r了,實際應用場景中可能需要旋轉(zhuǎn)多次。至此我們可以考慮一個問題,平衡二叉樹的查找效率還不錯,實現(xiàn)也非常簡單,相應的維護成本還能接受,為什么MySQL索引不直接使用平衡二叉樹?

隨著數(shù)據(jù)庫中數(shù)據(jù)的增加,索引本身大小隨之增加,不可能全部存儲在內(nèi)存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產(chǎn)生磁盤I/O消耗,相對于內(nèi)存存取,I/O存取的消耗要高幾個數(shù)量級。可以想象一下一棵幾百萬節(jié)點的二叉樹的深度是多少?如果將這么大深度的一顆二叉樹放磁盤上,每讀取一個節(jié)點,需要一次磁盤的I/O讀取,整個查找的耗時顯然是不能夠接受的。那么如何減少查找過程中的I/O存取次數(shù)?

一種行之有效的解決方法是減少樹的深度,將二叉樹變?yōu)閙叉樹(多路搜索樹),而B+Tree就是一種多路搜索樹。理解B+Tree時,只需要理解其最重要的兩個特征即可:第一,所有的關鍵字(可以理解為數(shù)據(jù))都存儲在葉子節(jié)點(Leaf Page),非葉子節(jié)點(Index Page)并不存儲真正的數(shù)據(jù),所有記錄節(jié)點都是按鍵值大小順序存放在同一層葉子節(jié)點上。其次,所有的葉子節(jié)點由指針連接。如下圖為高度為2的簡化了的B+Tree。

簡化B+Tree

怎么理解這兩個特征?MySQL將每個節(jié)點的大小設置為一個頁的整數(shù)倍(原因下文會介紹),也就是在節(jié)點空間大小一定的情況下,每個節(jié)點可以存儲更多的內(nèi)結點,這樣每個結點能索引的范圍更大更精確。所有的葉子節(jié)點使用指針鏈接的好處是可以進行區(qū)間訪問,比如上圖中,如果查找大于20而小于30的記錄,只需要找到節(jié)點20,就可以遍歷指針依次找到25、30。如果沒有鏈接指針的話,就無法進行區(qū)間查找。這也是MySQL使用B+Tree作為索引存儲結構的重要原因。

MySQL為何將節(jié)點大小設置為頁的整數(shù)倍,這就需要理解磁盤的存儲原理。磁盤本身存取就比主存慢很多,在加上機械運動損耗(特別是普通的機械硬盤),磁盤的存取速度往往是主存的幾百萬分之一,為了盡量減少磁盤I/O,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節(jié),磁盤也會從這個位置開始,順序向后讀取一定長度的數(shù)據(jù)放入內(nèi)存,預讀的長度一般為頁的整數(shù)倍。

頁是計算機管理存儲器的邏輯塊,硬件及OS往往將主存和磁盤存儲區(qū)分割為連續(xù)的大小相等的塊,每個存儲塊稱為一頁(許多OS中,頁的大小通常為4K)。主存和磁盤以頁為單位交換數(shù)據(jù)。當程序要讀取的數(shù)據(jù)不在主存中時,會觸發(fā)一個缺頁異常,此時系統(tǒng)會向磁盤發(fā)出讀盤信號,磁盤會找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內(nèi)存中,然后異常返回,程序繼續(xù)運行。

MySQL巧妙利用了磁盤預讀原理,將一個節(jié)點的大小設為等于一個頁,這樣每個節(jié)點只需要一次I/O就可以完全載入。為了達到這個目的,每次新建節(jié)點時,直接申請一個頁的空間,這樣就保證一個節(jié)點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現(xiàn)了讀取一個節(jié)點只需一次I/O。假設B+Tree的高度為h,一次檢索最多需要h-1I/O(根節(jié)點常駐內(nèi)存),復雜度O(h)=O(logMN)。實際應用場景中,M通常較大,常常超過100,因此樹的高度一般都比較小,通常不超過3。

最后簡單了解下B+Tree節(jié)點的操作,在整體上對索引的維護有一個大概的了解,雖然索引可以大大提高查詢效率,但維護索引仍要花費很大的代價,因此合理的創(chuàng)建索引也就尤為重要。

仍以上面的樹為例,我們假設每個節(jié)點只能存儲4個內(nèi)節(jié)點。首先要插入第一個節(jié)點28,如下圖所示。

leaf page和index page都沒有滿

接著插入下一個節(jié)點70,在Index Page中查詢后得知應該插入到50 - 70之間的葉子節(jié)點,但葉子節(jié)點已滿,這時候就需要進行也分裂的操作,當前的葉子節(jié)點起點為50,所以根據(jù)中間值來拆分葉子節(jié)點,如下圖所示。

Leaf Page拆分

最后插入一個節(jié)點95,這時候Index PageLeaf Page都滿了,就需要做兩次拆分,如下圖所示。

Leaf Page與Index Page拆分

拆分后最終形成了這樣一顆樹。

最終樹

B+Tree為了保持平衡,對于新插入的值需要做大量的拆分頁操作,而頁的拆分需要I/O操作,為了盡可能的減少頁的拆分操作,B+Tree也提供了類似于平衡二叉樹的旋轉(zhuǎn)功能。當LeafPage已滿但其左右兄弟節(jié)點沒有滿的情況下,B+Tree并不急于去做拆分操作,而是將記錄移到當前所在頁的兄弟節(jié)點上。通常情況下,左兄弟會被先檢查用來做旋轉(zhuǎn)操作。就比如上面第二個示例,當插入70的時候,并不會去做頁拆分,而是左旋操作。

左旋操作

通過旋轉(zhuǎn)操作可以最大限度的減少頁分裂,從而減少索引維護過程中的磁盤的I/O操作,也提高索引維護效率。需要注意的是,刪除節(jié)點跟插入節(jié)點類型,仍然需要旋轉(zhuǎn)和拆分操作,這里就不再說明。

高性能策略

通過上文,相信你對B+Tree的數(shù)據(jù)結構已經(jīng)有了大致的了解,但MySQL中索引是如何組織數(shù)據(jù)的存儲呢?以一個簡單的示例來說明,假如有如下數(shù)據(jù)表:

Mysql代碼

CREATE TABLE People(    
    last_name varchar(50not null,    
    first_name varchar(50not null,    
    dob date not null,    
    gender enum(`m`,`f`not null,    
    key(last_name,first_name,dob)    
);    

對于表中每一行數(shù)據(jù),索引中包含了last_name、first_namedob列的值,下圖展示了索引是如何組織數(shù)據(jù)存儲的。

索引如何組織數(shù)據(jù)存儲,來自:高性能MySQL

可以看到,索引首先根據(jù)第一個字段來排列順序,當名字相同時,則根據(jù)第三個字段,即出生日期來排序,正是因為這個原因,才有了索引的“最左原則”。

1、MySQL不會使用索引的情況:非獨立的列

“獨立的列”是指索引列不能是表達式的一部分,也不能是函數(shù)的參數(shù)。比如:

select * from where id + 1 = 5  

我們很容易看出其等價于 id = 4,但是MySQL無法自動解析這個表達式,使用函數(shù)是同樣的道理。

2、前綴索引

如果列很長,通??梢运饕_始的部分字符,這樣可以有效節(jié)約索引空間,從而提高索引效率。

3、多列索引和索引順序

在多數(shù)情況下,在多個列上建立獨立的索引并不能提高查詢性能。理由非常簡單,MySQL不知道選擇哪個索引的查詢效率更好,所以在老版本,比如MySQL5.0之前就會隨便選擇一個列的索引,而新的版本會采用合并索引的策略。舉個簡單的例子,在一張電影演員表中,在actor_idfilm_id兩個列上都建立了獨立的索引,然后有如下查詢:


select film_id,actor_id from film_actor where actor_id = 1 or film_id = 1 

老版本的MySQL會隨機選擇一個索引,但新版本做如下的優(yōu)化:

select film_id,actor_id from film_actor where actor_id = 1    
union all   
select film_id,actor_id from film_actor where film_id = 1 and actor_id <> 1  
  • 當出現(xiàn)多個索引做相交操作時(多個AND條件),通常來說一個包含所有相關列的索引要優(yōu)于多個獨立索引。
  • 當出現(xiàn)多個索引做聯(lián)合操作時(多個OR條件),對結果集的合并、排序等操作需要耗費大量的CPU和內(nèi)存資源,特別是當其中的某些索引的選擇性不高,需要返回合并大量數(shù)據(jù)時,查詢成本更高。所以這種情況下還不如走全表掃描。

因此explain時如果發(fā)現(xiàn)有索引合并(Extra字段出現(xiàn)Using union),應該好好檢查一下查詢和表結構是不是已經(jīng)是最優(yōu)的,如果查詢和表都沒有問題,那只能說明索引建的非常糟糕,應當慎重考慮索引是否合適,有可能一個包含所有相關列的多列索引更適合。

前面我們提到過索引如何組織數(shù)據(jù)存儲的,從圖中可以看到多列索引時,索引的順序?qū)τ诓樵兪侵陵P重要的,很明顯應該把選擇性更高的字段放到索引的前面,這樣通過第一個字段就可以過濾掉大多數(shù)不符合條件的數(shù)據(jù)。

索引選擇性是指不重復的索引值和數(shù)據(jù)表的總記錄數(shù)的比值,選擇性越高查詢效率越高,因為選擇性越高的索引可以讓MySQL在查詢時過濾掉更多的行。唯一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。

理解索引選擇性的概念后,就不難確定哪個字段的選擇性較高了,查一下就知道了,比如:

SELECT * FROM payment where staff_id = 2 and customer_id = 584  

是應該創(chuàng)建(staff_id,customer_id)的索引還是應該顛倒一下順序?執(zhí)行下面的查詢,哪個字段的選擇性更接近1就把哪個字段索引前面就好。

select count(distinct staff_id)/count(*) as staff_id_selectivity,  
       count(distinct customer_id)/count(*) as customer_id_selectivity,  
       count(*) from payment  

多數(shù)情況下使用這個原則沒有任何問題,但仍然注意你的數(shù)據(jù)中是否存在一些特殊情況。舉個簡單的例子,比如要查詢某個用戶組下有過交易的用戶信息:

select user_id from trade where user_group_id = 1 and trade_amount > 0  

MySQL為這個查詢選擇了索引(user_group_id,trade_amount),如果不考慮特殊情況,這看起來沒有任何問題,但實際情況是這張表的大多數(shù)數(shù)據(jù)都是從老系統(tǒng)中遷移過來的,由于新老系統(tǒng)的數(shù)據(jù)不兼容,所以就給老系統(tǒng)遷移過來的數(shù)據(jù)賦予了一個默認的用戶組。這種情況下,通過索引掃描的行數(shù)跟全表掃描基本沒什么區(qū)別,索引也就起不到任何作用。

推廣開來說,經(jīng)驗法則和推論在多數(shù)情況下是有用的,可以指導我們開發(fā)和設計,但實際情況往往會更復雜,實際業(yè)務場景下的某些特殊情況可能會摧毀你的整個設計。

4、避免多個范圍條件

實際開發(fā)中,我們會經(jīng)常使用多個范圍條件,比如想查詢某個時間段內(nèi)登錄過的用戶:

select user.* from user where login_time > '2017-04-01' and age between 18 and 30;  

這個查詢有一個問題:它有兩個范圍條件,login_time列和age列,MySQL可以使用login_time列的索引或者age列的索引,但無法同時使用它們。

5、覆蓋索引

如果一個索引包含或者說覆蓋所有需要查詢的字段的值,那么就沒有必要再回表查詢,這就稱為覆蓋索引。覆蓋索引是非常有用的工具,可以極大的提高性能,因為查詢只需要掃描索引會帶來許多好處:

  • 索引條目遠小于數(shù)據(jù)行大小,如果只讀取索引,極大減少數(shù)據(jù)訪問量
  • 索引是有按照列值順序存儲的,對于I/O密集型的范圍查詢要比隨機從磁盤讀取每一行數(shù)據(jù)的IO要少的多

6、使用索引掃描來排序

MySQL有兩種方式可以生產(chǎn)有序的結果集,其一是對結果集進行排序的操作,其二是按照索引順序掃描得出的結果自然是有序的。如果explain的結果中type列的值為index表示使用了索引掃描來做排序。

掃描索引本身很快,因為只需要從一條索引記錄移動到相鄰的下一條記錄。但如果索引本身不能覆蓋所有需要查詢的列,那么就不得不每掃描一條索引記錄就回表查詢一次對應的行。這個讀取操作基本上是隨機I/O,因此按照索引順序讀取數(shù)據(jù)的速度通常要比順序地全表掃描要慢。

在設計索引時,如果一個索引既能夠滿足排序,又滿足查詢,是最好的。

只有當索引的列順序和ORDER BY子句的順序完全一致,并且所有列的排序方向也一樣時,才能夠使用索引來對結果做排序。如果查詢需要關聯(lián)多張表,則只有ORDER BY子句引用的字段全部為第一張表時,才能使用索引做排序。ORDER BY子句和查詢的限制是一樣的,都要滿足最左前綴的要求(有一種情況例外,就是最左的列被指定為常數(shù),下面是一個簡單的示例),其他情況下都需要執(zhí)行排序操作,而無法利用索引排序。

-- 最左列為常數(shù),索引:(date,staff_id,customer_id)  
select  staff_id,customer_id from demo where date = '2015-06-01' ``order by staff_id,customer_id

7、冗余和重復索引

冗余索引是指在相同的列上按照相同的順序創(chuàng)建的相同類型的索引,應當盡量避免這種索引,發(fā)現(xiàn)后立即刪除。比如有一個索引(A,B),再創(chuàng)建索引(A)就是冗余索引。冗余索引經(jīng)常發(fā)生在為表添加新索引時,比如有人新建了索引(A,B),但這個索引不是擴展已有的索引(A)

大多數(shù)情況下都應該盡量擴展已有的索引而不是創(chuàng)建新索引。但有極少情況下出現(xiàn)性能方面的考慮需要冗余索引,比如擴展已有索引而導致其變得過大,從而影響到其他使用該索引的查詢。

8、刪除長期未使用的索引

定期刪除一些長時間未使用過的索引是一個非常好的習慣。

關于索引這個話題打算就此打住,最后要說一句,索引并不總是最好的工具,只有當索引幫助提高查詢速度帶來的好處大于其帶來的額外工作時,索引才是有效的。對于非常小的表,簡單的全表掃描更高效。對于中到大型的表,索引就非常有效。對于超大型的表,建立和維護索引的代價隨之增長,這時候其他技術也許更有效,比如分區(qū)表。最后的最后,explain后再提測是一種美德。

特定類型查詢優(yōu)化

優(yōu)化COUNT()查詢

COUNT()可能是被大家誤解最多的函數(shù)了,它有兩種不同的作用,其一是統(tǒng)計某個列值的數(shù)量,其二是統(tǒng)計行數(shù)。統(tǒng)計列值時,要求列值是非空的,它不會統(tǒng)計NULL。如果確認括號中的表達式不可能為空時,實際上就是在統(tǒng)計行數(shù)。最簡單的就是當使用COUNT(*)時,并不是我們所想象的那樣擴展成所有的列,實際上,它會忽略所有的列而直接統(tǒng)計行數(shù)。

在公眾號前端技術精選后臺回復“手冊”,獲取一份驚喜禮包。

我們最常見的誤解也就在這兒,在括號內(nèi)指定了一列卻希望統(tǒng)計結果是行數(shù),而且還常常誤以為前者的性能會更好。但實際并非這樣,如果要統(tǒng)計行數(shù),直接使用COUNT(*),意義清晰,且性能更好。

有時候某些業(yè)務場景并不需要完全精確的COUNT值,可以用近似值來代替,EXPLAIN出來的行數(shù)就是一個不錯的近似值,而且執(zhí)行EXPLAIN并不需要真正地去執(zhí)行查詢,所以成本非常低。通常來說,執(zhí)行COUNT()都需要掃描大量的行才能獲取到精確的數(shù)據(jù),因此很難優(yōu)化,MySQL層面還能做得也就只有覆蓋索引了。如果不還能解決問題,只有從架構層面解決了,比如添加匯總表,或者使用redis這樣的外部緩存系統(tǒng)。

優(yōu)化關聯(lián)查詢

在大數(shù)據(jù)場景下,表與表之間通過一個冗余字段來關聯(lián),要比直接使用JOIN有更好的性能。如果確實需要使用關聯(lián)查詢的情況下,需要特別注意的是:

  • 確保ON和USING字句中的列上有索引。在創(chuàng)建索引的時候就要考慮到關聯(lián)的順序。當表A和表B用列c關聯(lián)的時候,如果優(yōu)化器關聯(lián)的順序是A、B,那么就不需要在A表的對應列上創(chuàng)建索引。沒有用到的索引會帶來額外的負擔,一般來說,除非有其他理由,只需要在關聯(lián)順序中的第二張表的相應列上創(chuàng)建索引(具體原因下文分析)。
  • 確保任何的GROUP BYORDER BY中的表達式只涉及到一個表中的列,這樣MySQL才有可能使用索引來優(yōu)化。

要理解優(yōu)化關聯(lián)查詢的第一個技巧,就需要理解MySQL是如何執(zhí)行關聯(lián)查詢的。當前MySQL關聯(lián)執(zhí)行的策略非常簡單,它對任何的關聯(lián)都執(zhí)行嵌套循環(huán)關聯(lián)操作,即先在一個表中循環(huán)取出單條數(shù)據(jù),然后在嵌套循環(huán)到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行為為止。然后根據(jù)各個表匹配的行,返回查詢中需要的各個列。

太抽象了?以上面的示例來說明,比如有這樣的一個查詢:

SELECT A.xx,B.yy   
FROM A INNER JOIN B USING(c)  
WHERE A.xx IN (5,6)  

假設MySQL按照查詢中的關聯(lián)順序A、B來進行關聯(lián)操作,那么可以用下面的偽代碼表示MySQL如何完成這個查詢:

outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);  
outer_row = outer_iterator.next;  
while(outer_row) {  
    inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;  
    inner_row = inner_iterator.next;  
    while(inner_row) {  
        output[inner_row.yy,outer_row.xx];  
        inner_row = inner_iterator.next;  
    }  
    outer_row = outer_iterator.next;  

可以看到,最外層的查詢是根據(jù)A.xx列來查詢的,A.c上如果有索引的話,整個關聯(lián)查詢也不會使用。再看內(nèi)層的查詢,很明顯B.c上如果有索引的話,能夠加速查詢,因此只需要在關聯(lián)順序中的第二張表的相應列上創(chuàng)建索引即可。

優(yōu)化LIMIT分頁

當需要分頁操作時,通常會使用LIMIT加上偏移量的辦法實現(xiàn),同時加上合適的ORDER BY字句。如果有對應的索引,通常效率會不錯,否則,MySQL需要做大量的文件排序操作。

一個常見的問題是當偏移量非常大的時候,比如:LIMIT 10000 20這樣的查詢,MySQL需要查詢10020條記錄然后只返回20條記錄,前面的10000條都將被拋棄,這樣的代價非常高。

優(yōu)化這種查詢一個最簡單的辦法就是盡可能的使用覆蓋索引掃描,而不是查詢所有的列。然后根據(jù)需要做一次關聯(lián)查詢再返回所有的列。對于偏移量很大時,這樣做的效率會提升非常大??紤]下面的查詢:

SELECT film_id,description FROM film ORDER BY title LIMIT 50,5;  

如果這張表非常大,那么這個查詢最好改成下面的樣子:

SELECT film.film_id,film.description  
FROM film INNER JOIN (  
    SELECT film_id FROM film ORDER BY title LIMIT 50,5  
AS tmp USING(film_id);  

這里的延遲關聯(lián)將大大提升查詢效率,讓MySQL掃描盡可能少的頁面,獲取需要訪問的記錄后在根據(jù)關聯(lián)列回原表查詢所需要的列。

有時候如果可以使用書簽記錄上次取數(shù)據(jù)的位置,那么下次就可以直接從該書簽記錄的位置開始掃描,這樣就可以避免使用OFFSET,比如下面的查詢:

SELECT id FROM t LIMIT 1000010;  

改為:

SELECT id FROM t WHERE id > 10000 LIMIT 10;  

其他優(yōu)化的辦法還包括使用預先計算的匯總表,或者關聯(lián)到一個冗余表,冗余表中只包含主鍵列和需要做排序的列。

優(yōu)化UNION

MySQL處理UNION的策略是先創(chuàng)建臨時表,然后再把各個查詢結果插入到臨時表中,最后再來做查詢。因此很多優(yōu)化策略在UNION查詢中都沒有辦法很好的時候。經(jīng)常需要手動將WHERE、LIMIT、ORDER BY等字句“下推”到各個子查詢中,以便優(yōu)化器可以充分利用這些條件先優(yōu)化。

除非確實需要服務器去重,否則就一定要使用UNION ALL,如果沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這會導致整個臨時表的數(shù)據(jù)做唯一性檢查,這樣做的代價非常高。當然即使使用ALL關鍵字,MySQL總是將結果放入臨時表,然后再讀出,再返回給客戶端。雖然很多時候沒有這個必要,比如有時候可以直接把每個子查詢的結果返回給客戶端。

結語

理解查詢是如何執(zhí)行以及時間都消耗在哪些地方,再加上一些優(yōu)化過程的知識,可以幫助大家更好的理解MySQL,理解常見優(yōu)化技巧背后的原理。希望本文中的原理、示例能夠幫助大家更好的將理論和實踐聯(lián)系起來,更多的將理論知識運用到實踐中。

其他也沒啥說的了,給大家留兩個思考題吧,可以在腦袋里想想答案,這也是大家經(jīng)常掛在嘴邊的,但很少有人會思考為什么?

  • 有非常多的程序員在分享時都會拋出這樣一個觀點:盡可能不要使用存儲過程,存儲過程非常不容易維護,也會增加使用成本,應該把業(yè)務邏輯放到客戶端。既然客戶端都能干這些事,那為什么還要存儲過程?
  • JOIN本身也挺方便的,直接查詢就好了,為什么還需要視圖呢?

參考資料

[1] 姜承堯 著;MySQL技術內(nèi)幕-InnoDB存儲引擎;機械工業(yè)出版社,2013
[2] Baron Scbwartz 等著;寧海元 周振興等譯;高性能MySQL(第三版); 電子工業(yè)出版社, 2013
[3] 由 B-/B+樹看 MySQL索引結構


干貨分享

最近將個人學習筆記整理成冊,使用PDF分享。關注我,回復如下代碼,即可獲得百度盤地址,無套路領?。?/p>

?001:《Java并發(fā)與高并發(fā)解決方案》學習筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學習筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領域驅(qū)動設計速成)》?007:全部?008:加技術群討論

加個關注不迷路

喜歡就點個"在看"唄^_^

瀏覽 39
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報
評論
圖片
表情
推薦
點贊
評論
收藏
分享

手機掃一掃分享

分享
舉報

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 免费看毛片的网站| 国产午夜福利在线| 青在线视频| 一级黄色电影免费在线观看| 人操人人人操| aa人人操夜夜操人人| 色婷婷色| 大色网小色网| 天天视频黄色| 日本欧美在线视频| 免费观看一级毛一片| 在线免费观看av片| 三级网址大全| AV天堂影视在线观看| 亚洲日韩精品中文字幕在线| 打炮影院| 69精品无码成人久久久久久| 国产毛片毛片毛片毛片毛片| 激情久久AV一区AV二区AV三区 | 3D动漫精品啪啪一区二区竹笋 | 在线观看免费成人网站| www.五月丁香| 青青草逼视频| 久久综合色色| 亚洲成人欧美| 无码婬片A片AAA毛片艳谭| 国产麻豆电影在线观看| 欧美99| 欧美天堂在线| 一级爱爱免费视频| 懂色av,蜜臀AV粉嫩av| 一级特黄AA片| 亚洲精品久久久蜜桃| 亚洲性爱在线视频| 五月天婷婷影院影院| 午夜传媒一区二区三区| 久久久久亚洲AV无码专区成人| 91色五月| 欧美成人色| 最新AV在线播放| 亚洲精品无码在线观看| 人妻少妇精品视频| 亚洲成人高清无码| 中国一级A片| 成人无码视频在线观看| 97人妻精品一区二区三区视频 | 亚洲黄色网址| 国产久久这里只有精品视频| 成人第一页| 久久综合久久鬼| 男女www视频| 91黄色在线观看| 性一区| 中文字幕在线中文| A片大香蕉| www久久99| 国产三级视频在线| 国产A级视频| 九色欧美| 成人色色| 国产精品成人在线视频| 黄色在线观看国产| 免费黄色av网址| 91免费成人视频| 欧美日韩在线视频免费播放| 午夜精品久久久久久久99热精东| 熟妇精品| 99视频免费在线| 91视频熟女| 午夜免费AV| 夜夜网站| 国产精品无码一区二区三| 在线啪| 黄色片久久久| 国产A级成人婬片1976| 欧美色视频网| 艹逼电影| 超碰在线人人干| 国产喷潮| 四季AV一区二区凹凸懂色桃花| 苍井空无码一区二区三区| 国产精品国内自产拍| 久久成人三级| 18禁网站网址| 小早川怜子精品一区二区| 人人爱人人看| 亚洲中文视频在线| 国产成人免费观看视频| 中文字幕在线无码观看| 亚洲精品在线观看免费| 高圆圆一区二区三区| 国产一级婬乱片免费| 欧美国产中文| 国产八区| 91干干| 无码免费毛片一区二区三区古代| a一级黄片| 97无码精品人妻一区二区三区| 日韩操逼电影| 特級西西444WWw高清大膽| 成人做爰A片免费看网站| 福利毛片| 日韩成人一区二区三区| 在线看色| 久久av电影| 中文字幕免费高清| 超碰199| 国产性交网站| 欧美成人性爱网址| 亚洲一级婬片A片AAAA网址| 午夜成人黄色电影| 一本色道无码人妻精品| 韩国无码视频在线观看| 黄色毛片在线| 国产毛片777777| 热久在线| 亚洲中文娱乐| 久久久噜噜噜久久中文字幕色伊伊| 无码中文av| 日逼视频免费看| 无码视频免费观看| 周晓琳AV| 黄色成人网站大全| 91成人区| 一区二区三区四区五区| 97人人色| 日韩一区在线视频| 日本黄网站| 手机免费av| 欧美日韩视频| 亚洲天堂av在线免费观看| www.伊人网| 91天天在线| 国产精品同| 日韩免费小视频| 久久久8| 九九精品国产| 亚洲成人视频网站| 人人爽人人爽| 日韩三级在线免费观看| 日韩欧美国产一区二区| 水蜜桃成人网| 黄色在线免费观看网站| 中文字幕国产av| 伊人久艹| 黄片av| 日本黄A级A片国产免费| 丁香花免费高清视频小说完整| 久久国产热| 人妻av无码| 大茄子熟女AV导航| 色婷婷一区二区| 亚洲成人一区二区三区| 亚洲高清国产欧美综合s8| 国产高清精品在线| 波多野结衣无码一区| 午夜性爱视频| 亚洲无码字幕| 亚洲日韩中文字幕在线| 一区二区三区久久久| 俺来也俺去也www色| 青青草在线观看免费| 国产精品久久久久久99| 黄色电影一区二区| 欧美成人手机在线| 牛牛在线视频| 国产一级a毛一级a毛片视频黑人| 亚洲日韩精品欧美一区二区yw| 骚逼免费观看| 久久久久久婷婷| 人人操在线播放| 日韩天天| 色色色网站| 亚洲va国产va天堂va久久 | 日韩99在线| 肉片无遮挡一区二区三区免费观看视频 | 国产一级片免费视频| 一级大片免费看| 久久免费国产视频| 蜜臀一区二区三区| 强伦轩人妻一区二区三区最新版本更新内容 | 五月婷婷俺也去| 中文字幕精品视频在线| 国产黄网站| 国产熟妇婬乱A片免费看牛牛| 中文黄片| 亚洲欧美在线综合| 亚洲香蕉视频网站| 天堂网在线播放| 男女啪啪| 亚洲乱伦网| 五月天亚洲激情| 黄色三极片| 一级黄色片网站| 黄色激情在线| 免费一级a片| 日本精品电影| 国产无遮挡又黄又爽又色视频软件 | 亚洲免费小黄片| 牛牛精品一区二区| av三级片在线观看| 成人网站在线| 久久久久久久极品内射| 91亚洲精品国偷拍自产在线观看 | 婷婷日逼| 亚洲一区二区三区在线播放| 午夜无码人妻AV大片| 77777色婷婷| 亚洲日韩免费在线观看| 亚洲色图88| 91视频网站在线| 大香蕉网视频| gogogo视频在线观看黑人| 黄色高清无码| 操逼综合网| 五月丁香无码| 黄色午夜| 小黄片免费| 国产天天操| 午夜福利AV电影| 久久婷婷成人综合色怡春院| 一级特黄AAAA片| 大香蕉在线观看视频| 干干影院| 四虎成人无码A片观看| 久久久亚洲熟妇熟女| 国产精品9999| 91破处网站| 中文字幕成人在线观看| 毛片网站在线| 国色天香一区二区| 日本A在线播放| 91免费高清视频| 色婷婷久久综合久色| 99久久精品一区二区成人| 黄色录像毛片| 影音先锋成人资源网| 国产无码一区二区| 欧美色精品| 中文字幕人妻系列| 亚韩无码| 午夜免费AV| 黄色天堂| 韩国高清无码视频| 亚洲av性爱| 91热爆在线| 最近2019中文字幕mv第三季歌词| 欧洲精品码一区二区三区免费看| 亚洲图片在线| 亚洲色偷精品一区二区三区| 电影91久久久| 狠狠色噜噜狠狠狠7777米奇网| 日韩第1页| 亚洲无码A片在线观看| 亚洲国产精品成人va在线观看| 中文字幕+乱码+中文乱码视频在线观看 | 亚洲免费黄色电影| 777免费观看成人电影视频 | 巨爆乳肉感一区二区三区| 黄色视频网站免费| 无码视频免费播放| 在线观看黄A片免费网站| 91人妻人人操人人爽| 亚洲综合免费观看高清完整| 澳门无码视频| 免费一级无码成人片| 国产黄色不卡| 人人摸人人看人人草| 欧美成人版| 老司机AV91| 中文字幕天堂在线| 99爱在线观看| 91人妻无码一区二区久久| 豆花视频一区二区| 日日爱爱| 裸体黄色一极大片| 大香蕉东京热| 国产综合久久| 免费观看黄色在线视频| 婷婷五月丁香激情| 欧美日逼超碰| 色五月激情小说| 天天干干| 操比免费视频| 日韩成人观看| 亚洲精品三级| 无码一区二区区| 欧美午夜三级| 国产黄色AV片| 中文字幕人妻在线中文乱码怎么解决| 超碰777| 超碰在线最新| 一区二区三区无码高清| 伊人黄色| 色婷在线| 18一20女一片毛片| 91精品国产乱码| 亚州高清无码视频| 亚洲日韩一区| 性爱免费视频网站| 国产性生活| 久久天堂影院| 国产黄色片视频| 青青操在线视频| 久久永久免费| 熟女人妻人妻HD| 射射AV| 国产精品乱子伦视频一区二区| 动图综合亚洲综合欧美男男| 国产欧美成人在线| 狠狠撸狠狠干| 亚洲第一黄| 人妻无码高清| 婷婷激情中文字幕| 囯产精品99久久久久久WWW| 大香蕉伊人在线视频| 中文字幕av网| 日韩国产高清无码| 国产在线你懂得| 亚洲综合色色| 人人爽亚洲AV人人爽AV人人片| 欧美久久性爱| 亚洲无码久久| 乱伦视频91| 91人人妻| 国产av天天| 嫩草亚洲小泬久久夂| aV无码av天天aV天天爽第一| 91亚洲一线产区二线产区| 日日干天天日| 欧美另类色| 日韩欧AV| 人人摸人人操人人射| 91成人在线观看国产| 国产酒店自拍| 毛片二区| 日韩精品一区二区亚洲AV观看| 免费成人黄片| 欧美韩日一区二区| 国产婷婷久久| 东京热av一区二区| 国产精品一卡| 亚洲人人爱| 国产午夜福利在线| 二区三区无码| 国产亚洲激情| 国产精品2025| 牛牛影视av| 99国产精品久久久久久久| 中文无码一区| 国产高清无码免费视频| 无码精品电影| 亚洲成年人网| 欧美一区不卡| 丁香六月婷婷激情| 怡红院一区| 中文原创麻豆传媒md0052| 丝袜一区| 四色婷婷| 国产成人午夜福利在线| 夜色精品视频| 中文在线观看视频| 国产一区在线观看视频| 国产精品久久久久久久久久两年半 | 天天操狠狠操| 欧美日韩一级二级三级| 高清无码视频在线免费观看| 日逼高清无码| 国产一级a爱做片免费☆观看| 91逼| 潮喷在线| 亚洲AV无码蜜桃| 日韩国产一区| 日韩A片在线| 日韩不卡| 婷婷五月天色色| 日日操天天操夜夜操| 无码三| 夜色福利在线看| 中文字幕AⅤ在线| 一级免费爱爱视频| AV无码免费观看| 91在线无码精品秘入口电车| 亚洲天堂成人| 色爱av| 超碰在线免费| 91超碰久久在线| 国产91一区在线精品| 国产亚洲日韩在线| 免费看黄色大全| 婷婷五月天在线播放| 乱伦小说五月天| 91伊人网| 呦小性Free小U女HD| 午夜亚洲| 国产69页| 欧美性爱动态| 久久久久91| 天堂8在线视频| 日韩黄色一级片| 99久久夜色精品国产亚洲| aa人人操夜夜操人人| 国产aⅴ激情无码久久久无码| 午夜精品18视频国产| 午夜AV影院| 国产av小电影| 人人看人人摸人人操| 偷拍-91爱爱| 欧美在线国产| 国产精品久久久久久久久久久久久久| 另类老妇奶性生BBwBBw偷拍| 中文字幕不卡+婷婷五月| 久久精品一二三| 黄色电影免费网站| 农村一级婬片A片AAA毛片古装 | 久久久久久亚洲Av无码精品专口| 加勒比人妻| 亚洲黄色在线| 日日夜夜AV| 日本黄色视频。| 大香蕉伊人在线观看| 婷婷五月天性爱| 一级艹逼| 中文字幕免费在线视频| 久久久久久久艹| 日逼黄片| 黄色大片免费在线观看| 婷婷五月天免费视频| 日本色色视频| 在线国产福利| 青草久在线| 人人妻人人操人人| 91网站在线观看视频| 大鸡巴黄色视频免费观看| 久久不射| 家庭乱伦影视| 69无码| 欧美精品日韩| 无码AV天堂| 日韩人妻无码专区| 无码在线观看免费视频| 日本黄色大全| 东京热综合网| 人妻无码不卡| 中文字幕资源在线| 大香蕉久草| 欧美性爱怡红院| 黄色一级片免费在线观看| 蜜桃传媒一区二区亚洲AV| h片在线免费观看视频| 99热日| 91精品视频在线播放| 色婷婷久久综合| 加勒比日韩在线| 国产午夜在线观看| 9l视频自拍九色9l视频成人| 99cao| 9I免费看片黄| 中文字幕高清无码在线| 国产一级性爱视频| 国产激情在线播放| 日本成人黄色电影| 国产精品久久久久久最猛| 久久久精品国产| 免费无码又爽又黄又刺激网站 | 日韩美女毛片| 国产免费一区二区| 国产成人精品免费看视频| 中文大香蕉视频| 久久久久国产视频| 做爰视频毛片下载蜜桃视频| 青青草手机视频在线| 午夜无码福利| 无码中文AV| 色五月天激情| 欧美一卡二卡| 久久aa| 免费人成视频在线| 日本无码视频在线观看毒| 欧美精品秘一区二区三区蜜臀 | 国产又爽又黄免费网站在| 国产婷婷内射| 91精品久久久久久| 欧美性夜黄A片爽爽免费视频| 欧美色图在线观看视频| 不迷路福利视频| 亚洲久草| 国产又大又粗又黄| 美女AV网站| 波多野结衣黄色| 亚洲无码AV在线播放| 成人精品一区日本无码网站suv | 黄a在线观看| 2024AV在线| 日本一本视频| 嫩草在线精品| 欧美成人视屏| 国产中文字幕免费| 亚日韩视频| 日日精品| 综合一区二区三区| 大香蕉最新视频| 91蝌蚪视频在线| 人妻少妇精品视频| 婷婷五月丁香花| 亚韩av| 120分钟婬片免费看| 黄色在线免费看| 欧美丰满老熟妇XXXXX性| 在线播放www| 青青激情视频| 亚洲激情| av无码在线播放| 激情成人五月天| 亚洲第一黄色视频| 亚洲无码人妻一区| 色老板免费视频| 国产综合精品久久久久成人AV| 日本在线一区二区| 无码AV天堂| 视频一视频二在线视频| 五月丁香激情视频| 久久一卡二卡| 国产视频成人| 亚洲无码在线高清| www.亚洲无码| 7777av| 国产精品婷婷午夜在线观看| 成人无码区亚洲AV久久| 九九九色视频| 中文字幕av在线播放| 女生自慰网站在线观看| 欧美亚洲综合在线| 国产成人三级视频| 亚洲综合五月天| 91久久爽久久爽爽久久片| 91久久婷婷国产| 日韩不卡视频在线| 亚洲最新AV网站| 欧美性之站| 无码一区二区三| 无码人妻精品一区二区三| 操日本少妇| 亚洲精品自拍偷拍| 国产一区2区| 色人阁人妻中文字幕| 口爆在线观看| 亚洲成人黄色网| 激情人妻av| 国产又爽又黄视频在线看| 成人精品永久免费视频99久久精品| 成人免费观看的毛视频| 青青草综合网| 亚洲图片在线观看| 亚洲视频91| 天天干欧美| 色五月视频在线| 人妻电影亚洲av| 欧美一级特黄A片免费看视频小说| 色超碰| 欧美日韩中文字幕在线视频| 西欧超碰在线| 小泽玛利亚一区二区免费| 欧美一级在线| 91超碰在线免费观看| 日韩欧美国产成人| 亚洲91网站| 天堂视频在线观看亚洲美女| 亚洲人做受| 日韩在线| 天堂一区二区三区| 91丨熟女丨首页| 在线中文字幕AV| 97视频国产| 九九九av| 在线看的av| 青草网在线观看| 欧美色性乐汇操日本娘们| 清清草视频| 一级黄色电影在线观看| av天堂中文在线| 精品乱子伦一区二区三区在线播放 | 欧美狠狠干| 草逼网站| AV三级片在线观看| 国产精品夜夜爽7777777| 黄色在线免费观看网站| 欧美午夜爱爱| 高清无码在线免费视频| 国产区视频| 成人黄色性视频| 国产系列每日更新| 水蜜桃一区二区| 91口爆| 超碰97久久| 91超碰人人操| 亚洲成人视频免费在线观看 | 2025四虎在线视频观看| 欧美成人网站在线观看| 国产精品国产成人国产三级| 日本熟妇一区二区三区| 中文无码日本一级A片人| 亚洲第九页| 一个人看的www日本高清视频 | 日韩无码第四页| 人妻人人干| 亚洲vs天堂vs成人vs无码| 精品成人| 婷婷五月天久久| 国产AV一级片| 亚洲黄色录像| 亚洲一级AV| A片免费在线播放| 国产一级婬乱片AV片AAA毛片| 一本大道东京热AV| 国产网站免费| 黄色电影视频在线| 午夜丁香婷婷| 色欲色欲一区二区三区| 日本大香蕉在线视频| 青青草大香蕉在线| www日韩无码| 久草视频在线资源| 青青五月天| 91九色丨国产丨爆乳| 爱爱网址| 国产精品揄拍一区二区| 蝌蚪窝视频在线观看| 欧美黄色免费看| 小處女末发育嫩苞AV| 成人三级无码| 69国产精品成人无码| www.777av| 嫩BBB槡BBBB槡BBBB撒尿| 青草视频在线免费观看| 成人片免费看| 无码精品人妻| 少妇厨房愉情理伦BD在线观看| 国內精品久久久久久久| 亚洲精品在线视频观看| 粉嫩99精品99久久久久久特污| 欧美日韩黄色极品| 亚洲AV无码成人精品区www| 四虎影院最新地址| 在线不卡中文字幕| 亚洲中文字幕无码在线观看 | 国产亚洲欧美一区二区| 国产男女性爱视频播放| 撸一撸成人在线做爱视频。| 逼特逼| 动图综合亚洲综合欧美男男| 亚洲激情视频| 在线观看无码| 影音先锋成人| 逼特逼| 丁月婷婷五香天日五月天| 亚洲福利女神成人福利| 国产成人电影| 996热| 欧美日韩午夜福利视频| 麻豆免费版在线观看| 德美日三级片在线观看| 一级a片激情啪啪免费观| 久久午夜无码鲁片午夜精品男男 | 天堂一区二区三区18| 黄色免费观看网站| 自拍偷拍精品视频| 曰逼视频| 亚洲不卡在线观看| 日韩A√| 日日夜夜AV| 高清无码做爱视频| 热热AV| 一级成人A片| 97热热| 国产免费麻豆| 亚洲性爱视频在线观看| 日本色色视频| 日韩欧美久久| www.四虎成人网站| 中文字幕++中文字幕明步| 白虎高清无码大尺度免费在线观看| 亚洲综合社区在线| 中文字幕一区三区三A片密月| 麻豆午夜福利| 狠狠操狠狠插| 91大屁股| 高清日韩欧美| 日本老女人视频| 无码人妻在线播放| 一级特黄录像免费播放下载软件| 爱搞国产| 69色综合| a在线观看视频| 第四色大香蕉| 在线黄色网| 黄色在线免费看| 中文视频免费播放| 久久99久久视频| 欧美一道本在线| 五月天福利网| 超碰人人爱人人操| 欧美高潮| 免费十无码| 国产AV久久| 玖玖成人电影| 男插女青青影院| 免费观看av| 东京热综合| 成人片成人网久久蜜桃臀| 亚洲一级免费在线观看| 中文字幕www一区| 丁香五月激情啪啪| 日韩性爱一区| 国产精品久久久久久精| 五月天亚洲色图| 久久99久久99精品免视看婷婷 | 丁香在线视频| 玖玖精品| 噜噜视频| 中文字幕你懂的在线三级| 天天综合字幕一区二区| 一本色道久久88亚洲精品综合| 国产成人片在线观看| 老熟女伦一区二区三区| 国产九九在线视频| 无码人妻精品一区二区蜜桃91| av天堂中文字幕| 亚洲AV无码成人精品涩涩麻豆| 色网在线| 国产精品色在线回看| 日韩va亚洲va欧美va高清| 成人片网站在线观看| 精品91在线视频| 人人看人人搂人人摸| 91足浴店按摩漂亮少妇| 88AV在线| 国产黄色三级片| 亚洲AV官方网站| 特级毛片AAAAAA蜜桃| 亚州在线播放| 久久久久黄片| 日韩欧美成人网站| 日韩免费在线播放| 影音先锋成人资源站| 一区二区三区在线播放| 中文字幕码精品视频网站| 日本親子亂子倫XXXX| 波多野结衣久久精品| 欧美日韩中文字幕在线视频| 欧美一区二区三区成人片下载| 黄色一级片视频| 91久久精品日日躁夜夜躁国产| 中文字幕av高清片,中文在线观看| 中文字幕97| 香蕉视频成人在线观看| 久久国产热视频| 一区二区三区无码免费| 欧美在线综合| 色婷婷视频在线观看| 就去色色五月天| 操老骚逼视频| 日韩成人三级片| 欧美黄色录像| 欧美群交在线| 久久精品网| 日本不卡在线观看| AV五月| 一级黄色操逼视频| 亚洲无码视频在线看| 国产精品一区在线观看| 欧美日本激情| 无码三级午夜久久人妻| 亚洲精品视频免费在线观看| 色99视频| 色五月婷婷中文字幕| AV免费激情影院| 欧美性爱在线网站| 好男人WWW社区在线视频夜恋| 国产熟女一区二区| 无码精品人妻一区二区三刘亦菲 | 91丝袜一区在线观看| 手机看片日韩| 亚洲一级二级| 操逼资源| 国产精品久久毛片| 男女啊啊啊| 精品成人电影| 青草社区在线观看| 拍拍拍免费视频| 成人免费三级| 青青草原成人| 日本人妻视频| 麻豆疯狂做受XXXX高潮视频| 国产18水真多18精品| 97天天干| 熟妇槡BBBB槡BBBB| 久久久久久久久久久久久自慰小片 | 蜜桃av无码一区三区| 五月婷亚洲精品AV天堂| 操b视频网站| 亚洲一级免费在线观看| AAA片视频| 嫩苞又嫩又紧AV无码| 青娱乐99| 淫乱人妻| 三级影片在线观看性| 日韩不卡视频在线| 婷婷无码视频| 久久久一区二区三区四曲免费听| 国产日韩欧美成人| 操操操综合网| 欧美午夜网站| 亚洲秘AV无码一区二区qq群| 成人片网站在线观看| 久久精品一区二区三区不卡牛牛| 黄色A片在线观看| 九色PORNY国产成人蝌蚪| 欧美日韩国产三级| 一级日逼视频| 操逼一级| 高清欧美日韩第一摸| 国产婬片lA片www777| 亚洲成人视频免费在线观看 | 成人AV在线资源| 日韩黄色电影网址| 中文字幕巨乱亚洲高清A片28| 大香蕉久久久久久久| 综合操逼网| 俺来也网| 日韩人妻无码专区一区二区| 毛片小电影| 九九视频免费观看| 18XXX亚洲HD护士JD| 日韩和的一区二区| 日韩成人免费在线观看| 国产淫语| 精品人妻一区二区三区四区不卡在 | 欧美成人在线视频网站| 久久系列| 99久久久久久久无码| 夜夜撸日日| 亚洲中文字幕2025| 人人操AV在线| 亚洲AV无码久久久| 黄色A片免费看| 欧美精产国品一二三区| 成年人黄色视频免费观看| 日韩欧美国产精品综合嫩V| 东北嫖老熟女一区二区视频网站| 蜜桃久久久久久久| 伊人久久香蕉网| 欧美性爱-熊猫成人网| 久久久久久97电影院电影院无码| 九九内射| 91色综合| 97在线观看视频| 看免费操逼视频| 亚洲天堂精品视频| 国产色视频| 性猛交AAAA片免费观看直播| 天堂AV在线免费观看| 国产一级AAAAA片免费| 大香蕉第一页| 国产主播中文字幕| 黄色免费在线观看视频| 欧美日韩AV| 亚洲黄在线观看| 国产精品无码无套在线| 翔田千里在线一区二区三区| 国产成人精品av在线观看| 在线观看黄色电影| 韩国三级无码| 麻豆一区在线观看| 中文字幕在线免费视频| 夜夜夜操操操| 国产精品自拍偷拍| 国产高清在线视频| 午夜狠狠操| 加勒比无码在线播放| 亚洲色视频| 亚洲最新AV网站| 日韩精品久久久久久久酒店| 人人摸人人摸| 婷婷五月综合中文字幕| 91欧美视频| 黄色av无码| 国产精品99视频| 成人精品在线视频| 深爱开心激情| 在线观看免费国产| 翔田千里无码| 69性爱视频| 久久国产精品久久| 综合五月婷婷| 人人看人人色| 亚洲性爱一区| 黑人精品XXX一区一二区| 亚洲欧洲成人| 丝瓜视频污APP|