源碼分析

sqlite3 where結(jié)構(gòu)中的查詢重寫(xiě),選取適合的索引

index 結(jié)構(gòu)體內(nèi)容

一個(gè)index結(jié)果里面,有好多統(tǒng)計(jì)數(shù)據(jù)
每個(gè)索引結(jié)構(gòu)中存放這key的統(tǒng)計(jì)信息,所謂統(tǒng)計(jì)直方圖,是放在index結(jié)果體中

struct Index {
  char *zName;     /* Name of this index */
  int *aiColumn;   /* Which columns are used by this index.  1st is 0 */
  tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
  Table *pTable;   /* The SQL table being indexed */
  char *zColAff;   /* String defining the affinity of each column */
  Index *pNext;    /* The next index associated with the same table */
  Schema *pSchema; /* Schema containing this index */
  u8 *aSortOrder;  /* Array of size Index.nColumn. True==DESC, False==ASC */
  char **azColl;   /* Array of collation sequence names for index */
  int nColumn;     /* Number of columns in the table used by this index */
  int tnum;        /* Page containing root of this index in database file */
  u8 onError;      /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
  u8 autoIndex;    /* True if is automatically created (ex: by UNIQUE) */
  u8 bUnordered;   /* Use this index for == or IN queries only */
#ifdef SQLITE_ENABLE_STAT3
  int nSample;             /* Number of elements in aSample[] 主要信息*/
  tRowcnt avgEq;           /* Average nEq value for key values not in aSample */
  IndexSample *aSample;    /* Samples of the left-most key */
#endif
};

/**/

在這個(gè)結(jié)構(gòu)體,indexsample中結(jié)構(gòu)體定義如下

struct IndexSample {
  union {
    char *z;        /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
    double r;       /* Value if eType is SQLITE_FLOAT */
    i64 i;          /* Value if eType is SQLITE_INTEGER */
  } u;
  u8 eType;         /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
  int nByte;        /* Size in byte of text or blob. */
  tRowcnt nEq;      /* Est. number of rows where the key equals this sample */
  tRowcnt nLt;      /* Est. number of rows where key is less than this sample */
  tRowcnt nDLt;     /* Est. number of distinct keys less than this sample */
};

存放著,最左邊的元素。其中統(tǒng)計(jì)信息有,跟這個(gè)值相等的個(gè)數(shù),比他小的值的個(gè)數(shù),比它小的不同的值的個(gè)數(shù)
其中,下進(jìn)行rang個(gè)數(shù)估算的時(shí)候,有用到index里面的值,跟他相等的,和比它小的,在where.c 的約第2734行,whereRangeScanEst 結(jié)構(gòu)里

whereEquascanEst 結(jié)構(gòu)體中任然存在這樣的結(jié)構(gòu)

whereInScanEst 結(jié)構(gòu)也應(yīng)用了這個(gè)結(jié)構(gòu),分別統(tǒng)計(jì)等于,再相加,所有的在where條件中的查詢優(yōu)化,最重要的就是訪問(wèn)index結(jié)構(gòu)中

代價(jià)評(píng)估,x = value value出現(xiàn)在數(shù)據(jù)直方圖中,

這統(tǒng)計(jì)直方圖的存放形式就是用結(jié)構(gòu)體給表示出來(lái)。
whereCost這個(gè)代碼,是存放查詢計(jì)劃的地方
WherePlan 是存放查詢計(jì)劃的地方,供外面的函數(shù)進(jìn)行調(diào)用。存放有策略,估計(jì)的行數(shù)使用的索引結(jié)構(gòu)等,方便下一步查詢的時(shí)候使用

where語(yǔ)句優(yōu)化

大量采用虛擬表達(dá)式(virtual term)這種形式,在一個(gè)真實(shí)的where條件后,再加一個(gè)等價(jià)的條件,然后在代價(jià)估算的時(shí)候,逐一判斷累計(jì)代價(jià),最后選擇出最合適的代價(jià)估算方案。

or語(yǔ)句優(yōu)化

優(yōu)化部位,在sql語(yǔ)句中where子句部分

WHERE  (a=5) AND (b=7 OR c=9 OR d=13) AND (d=13)
                 ^^^^^^^^^^^^^^^^^^^^

采取的優(yōu)化策略

  1. 把where中 多個(gè)or條件轉(zhuǎn)化為in條件。

    x = expr1 OR expr2 = x OR x = expr3------>
    x IN (expr1,expr2,expr3)

  2. 第二中情況。如果在子查詢條件中,有例如"=", "<", "<=", ">", ">=", "IS NULL", "IN".這樣的條件,就掃描索引,累加代價(jià)估算。否則就不優(yōu)化
    步驟

  3. or語(yǔ)句分解

  4. 找出可以滿足case 1,或者case2的項(xiàng)

  5. 做出相應(yīng)的標(biāo)記,方便后面進(jìn)行代價(jià)估算,查找出最佳的代價(jià)

改成左表達(dá)式

運(yùn)用左深樹(shù),更省內(nèi)存。左深樹(shù)的改寫(xiě),所有的類(lèi)似于 xxx = A的這種表達(dá)式,都會(huì)被轉(zhuǎn)化為A = xxx這種形式

多列索引使用規(guī)則

如果有一個(gè)類(lèi)似于這樣的多列索引創(chuàng)建

CREATE INDEX idx_ex1 ON ex1(a,b,c,d,e,...,y,z);

在多列索引中,有時(shí)候,這些索引會(huì)被用到,而有時(shí)候并不會(huì)被用到

WHERE a=5 AND b IN (1,2,3) AND c IS NULL AND d='hello'

這樣的子句,在 abcd列上的索引會(huì)被用到。

WHERE a=5 AND b IN (1,2,3) AND c>12 AND d='hello'

這樣的子句,只有在abc列上的索引會(huì)被用到,因?yàn)閏列是>號(hào)

WHERE a=5 AND b IN (1,2,3) AND d='hello'

如果這樣的where子句,之后ab列上的索引會(huì)被用到,因?yàn)橹虚g有c列被跳過(guò)

between規(guī)則改寫(xiě)

在范圍查詢中a BETWEEN b AND c會(huì)改寫(xiě)成 (a BETWEEN b AND c) AND (a>=b) AND (a<=c)這樣的,就可以通過(guò)利用索引結(jié)構(gòu)中 的一些統(tǒng)計(jì)數(shù)據(jù)來(lái)完成優(yōu)化,同事,由于后面的規(guī)則是附加上去的。如果可以優(yōu)化就采用優(yōu)化后的結(jié)構(gòu),那就直接跳過(guò)where子句中的between條件。

同樣的原理

x LIKE 'abc%'會(huì)被轉(zhuǎn)化為x>='abc' AND x<'abd' AND x LIKE 'abc%'X is not NULL 轉(zhuǎn)為 x>NULL通過(guò)這些邏輯重寫(xiě)。完成課堂上所講的代數(shù)優(yōu)化工作。

distinct 是否能被轉(zhuǎn)化

如果where 子句中,有col = x 在x 上的distinct,這種distinct就可以被取消。如果,該列,有unique,就把它distinct設(shè)置成冗余。這同樣也是一種代數(shù)優(yōu)化方式
找出可以利用的index信息。做上相應(yīng)的標(biāo)記。通過(guò)循環(huán)查找表中的index,進(jìn)行優(yōu)化匹配。

索引,能否在orderby 中優(yōu)化

如果,列不在,from子句中的最左邊的列,那么,這個(gè)索引,在orderby 中就沒(méi)有用,這是因?yàn)?,sqlite在使用多表連接的時(shí)候使用的是簡(jiǎn)單嵌套循環(huán)連接。如果索引列在from不是最左邊的列。在進(jìn)行連接的時(shí)候,就會(huì)順序會(huì)被打亂
如果,有== 約束,對(duì)應(yīng)條件的索引就設(shè)置成失效。在沒(méi)有索引能夠正常使用的情況下,就采用用主鍵的索引(沒(méi)有orderby 的項(xiàng)目用在表的連接上)

如果所有的orderby項(xiàng),列條件都能被index覆蓋,那么這個(gè)index就能被索引

為建立虛表計(jì)算出最合適的索引(where.c 2330)

在 兩個(gè)表,進(jìn)行join的時(shí)候,會(huì)執(zhí)行多次在虛表建立過(guò)程中,為了保證高效,可能會(huì)用到多列索引
在where中range掃描估計(jì),(在有索引的情況下)單側(cè)不等式,被估計(jì)為1/4 雙側(cè)不等式被估計(jì)為1/16 代碼在where.c 第2713行左右
在選擇最佳的執(zhí)行計(jì)劃的時(shí)候,會(huì)參考代價(jià)估算如果代價(jià)估算。其中如果估算代價(jià)小于掃描所有行數(shù)的時(shí)候才會(huì)可能被采納到。

總結(jié):

在整個(gè)sqlite系統(tǒng)中,我們子啊課堂上學(xué)習(xí)到的一些優(yōu)化算法,例如邏輯優(yōu)化,和物理優(yōu)化,選擇合適的索引等,在實(shí)際的程序?qū)崿F(xiàn)的時(shí)候都即依賴上一層sql解析器中傳過(guò)來(lái)來(lái)的具體的sql結(jié)構(gòu),也依賴下層數(shù)據(jù)存儲(chǔ)的信息,例如是否有索引,有哪些索引,在數(shù)據(jù)列中比當(dāng)前數(shù)據(jù)還要小的數(shù)據(jù)有多少?還有課堂上老師在講查詢估算的時(shí)候的估算方法,所有的信息都是用相應(yīng)的結(jié)構(gòu)體存放在特定的結(jié)構(gòu)中。很多時(shí)候,所有的結(jié)構(gòu)體都不會(huì)是單一存在的,結(jié)構(gòu)體會(huì)嵌套結(jié)構(gòu)體。每個(gè)結(jié)構(gòu)體中會(huì)存放相應(yīng)的信息。

在看sqlite源碼過(guò)程中,我體會(huì)到了一個(gè)課本上的優(yōu)化方法,是如何轉(zhuǎn)化為實(shí)際的數(shù)據(jù)庫(kù)產(chǎn)品。作者通過(guò)設(shè)計(jì)一些列巧妙的結(jié)構(gòu)體,精心讀寫(xiě)相應(yīng)的結(jié)構(gòu)體。比如統(tǒng)計(jì)直方圖的設(shè)計(jì)方式就很是精巧的結(jié)構(gòu)體來(lái)實(shí)現(xiàn)的。在index結(jié)構(gòu)體中,嵌套indexsample結(jié)構(gòu)體,在indexsample中存放一些統(tǒng)計(jì)信息,并精心維護(hù)。

另外sqlite是用c語(yǔ)言編寫(xiě)的一個(gè)數(shù)據(jù)庫(kù)。在c語(yǔ)言這個(gè)面向過(guò)程的語(yǔ)言中,進(jìn)行精巧的設(shè)計(jì)結(jié)構(gòu),和函數(shù)保證系統(tǒng)設(shè)計(jì)的運(yùn)行效率。

通過(guò)這門(mén)課程我最大的感受就是,每一個(gè)想法,點(diǎn)子,和鎖需要用到的信息,都需要用精巧的設(shè)計(jì)和定義才能完成。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,083評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,706評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,442評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,802評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,983評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,287評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,486評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,710評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,116評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,412評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,224評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,462評(píng)論 2 378

推薦閱讀更多精彩內(nèi)容

  • 一、基本數(shù)據(jù)類(lèi)型 注釋 單行注釋?zhuān)?/ 區(qū)域注釋?zhuān)?* */ 文檔注釋?zhuān)?** */ 數(shù)值 對(duì)于byte類(lèi)型而言...
    龍貓小爺閱讀 4,285評(píng)論 0 16
  • 1. 問(wèn)題的提出## 在應(yīng)用系統(tǒng)開(kāi)發(fā)初期,由于開(kāi)發(fā)數(shù)據(jù)庫(kù)數(shù)據(jù)比較少,對(duì)于查詢SQL語(yǔ)句,復(fù)雜視圖的的編寫(xiě)等體會(huì)不出...
    七寸知架構(gòu)閱讀 5,399評(píng)論 1 111
  • HashMap 是 Java 面試必考的知識(shí)點(diǎn),面試官?gòu)倪@個(gè)小知識(shí)點(diǎn)就可以了解我們對(duì) Java 基礎(chǔ)的掌握程度。網(wǎng)...
    野狗子嗷嗷嗷閱讀 6,684評(píng)論 9 107
  • 其實(shí)標(biāo)簽就那么點(diǎn)東西,在小碼哥的基礎(chǔ)課,最重要的是系統(tǒng)的學(xué)習(xí)了一邊常用CSS屬性。 期間真的學(xué)到了很多,話不多說(shuō),...
    單身狗的凝視閱讀 375評(píng)論 0 0
  • 清霖潤(rùn)色幾重紗,短笠長(zhǎng)風(fēng)細(xì)柳麻。 歸解藏枝栽屋角,去留濺雨掛檐花。 明知久怨隨流水,卻許多情喚客家。 終是煮春聽(tīng)淺...
    墨言之閱讀 1,044評(píng)論 16 27