源碼分析

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

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)化嫩海,最重要的就是訪問index結(jié)構(gòu)中

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

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

where語句優(yōu)化

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

or語句優(yōu)化

優(yōu)化部位栖榨,在sql語句中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語句分解

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

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

改成左表達(dá)式

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

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

如果有一個(gè)類似于這樣的多列索引創(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)閏列是>號

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

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

between規(guī)則改寫

在范圍查詢中a BETWEEN b AND c會(huì)改寫成 (a BETWEEN b AND c) AND (a>=b) AND (a<=c)這樣的购桑,就可以通過利用索引結(jié)構(gòu)中 的一些統(tǒng)計(jì)數(shù)據(jù)來完成優(yōu)化畅铭,同事,由于后面的規(guī)則是附加上去的勃蜘。如果可以優(yōu)化就采用優(yōu)化后的結(jié)構(gòu)硕噩,那就直接跳過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通過這些邏輯重寫炉擅。完成課堂上所講的代數(shù)優(yōu)化工作。

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

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

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

如果,列不在兢孝,from子句中的最左邊的列窿凤,那么,這個(gè)索引跨蟹,在orderby 中就沒有用雳殊,這是因?yàn)椋瑂qlite在使用多表連接的時(shí)候使用的是簡單嵌套循環(huán)連接窗轩。如果索引列在from不是最左邊的列夯秃。在進(jìn)行連接的時(shí)候,就會(huì)順序會(huì)被打亂
如果痢艺,有== 約束仓洼,對應(yīng)條件的索引就設(shè)置成失效。在沒有索引能夠正常使用的情況下堤舒,就采用用主鍵的索引(沒有orderby 的項(xiàng)目用在表的連接上)

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

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

在 兩個(gè)表舌缤,進(jìn)行join的時(shí)候箕戳,會(huì)執(zhí)行多次在虛表建立過程中,為了保證高效国撵,可能會(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解析器中傳過來來的具體的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源碼過程中揭措,我體會(huì)到了一個(gè)課本上的優(yōu)化方法,是如何轉(zhuǎn)化為實(shí)際的數(shù)據(jù)庫產(chǎn)品刻蚯。作者通過設(shè)計(jì)一些列巧妙的結(jié)構(gòu)體绊含,精心讀寫相應(yīng)的結(jié)構(gòu)體。比如統(tǒng)計(jì)直方圖的設(shè)計(jì)方式就很是精巧的結(jié)構(gòu)體來實(shí)現(xiàn)的炊汹。在index結(jié)構(gòu)體中躬充,嵌套indexsample結(jié)構(gòu)體,在indexsample中存放一些統(tǒng)計(jì)信息讨便,并精心維護(hù)充甚。

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梳凛,隨后出現(xiàn)的幾起案子耿币,更是在濱河造成了極大的恐慌,老刑警劉巖韧拒,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淹接,死亡現(xiàn)場離奇詭異,居然都是意外死亡叛溢,警方通過查閱死者的電腦和手機(jī)塑悼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楷掉,“玉大人厢蒜,你說我怎么就攤上這事。” “怎么了斑鸦?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵愕贡,是天一觀的道長。 經(jīng)常有香客問我巷屿,道長固以,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任嘱巾,我火速辦了婚禮憨琳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旬昭。我一直安慰自己栽渴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布稳懒。 她就那樣靜靜地躺著闲擦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪场梆。 梳的紋絲不亂的頭發(fā)上墅冷,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音或油,去河邊找鬼寞忿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛顶岸,可吹牛的內(nèi)容都是我干的腔彰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辖佣,長吁一口氣:“原來是場噩夢啊……” “哼霹抛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起卷谈,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤杯拐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后世蔗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體端逼,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年污淋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了顶滩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸爆,死狀恐怖礁鲁,靈堂內(nèi)的尸體忽然破棺而出浊吏,到底是詐尸還是另有隱情,我是刑警寧澤惠昔,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布陪踩,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏那先。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一剪况、第九天 我趴在偏房一處隱蔽的房頂上張望钥星。 院中可真熱鬧,春花似錦心铃、人聲如沸准谚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柱衔。三九已至,卻和暖如春愉棱,著一層夾襖步出監(jiān)牢的瞬間唆铐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工奔滑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艾岂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓朋其,卻偏偏與公主長得像王浴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子梅猿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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