MySQL索引底層實現(xiàn)原理
MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結構眯亦。提取句子主干,就可以得到索引的本質:索引是數(shù)據(jù)結構般码。
我們知道妻率,數(shù)據(jù)庫查詢是數(shù)據(jù)庫的最主要功能之一。我們都希望查詢數(shù)據(jù)的速度能盡可能的快板祝,因此數(shù)據(jù)庫系統(tǒng)的設計者會從查詢算法的角度進行優(yōu)化宫静。最基本的查詢算法當然是順序查找(linear search),這種復雜度為O(n)的算法在數(shù)據(jù)量很大時顯然是糟糕的扔字,好在計算機科學的發(fā)展提供了很多更優(yōu)秀的查找算法囊嘉,例如二分查找(binary search)、二叉樹查找(binary tree search)等革为。如果稍微分析一下會發(fā)現(xiàn)扭粱,每種查找算法都只能應用于特定的數(shù)據(jù)結構之上,例如二分查找要求被檢索數(shù)據(jù)有序震檩,而二叉樹查找只能應用于二叉查找樹上琢蛤,但是數(shù)據(jù)本身的組織結構不可能完全滿足各種數(shù)據(jù)結構(例如蜓堕,理論上不可能同時將兩列都按順序進行組織),所以博其,在數(shù)據(jù)之外套才,數(shù)據(jù)庫系統(tǒng)還維護著滿足特定查找算法的數(shù)據(jù)結構,這些數(shù)據(jù)結構以某種方式引用(指向)數(shù)據(jù)慕淡,這樣就可以在這些數(shù)據(jù)結構上實現(xiàn)高級查找算法背伴。這種數(shù)據(jù)結構,就是索引峰髓。
看一個例子:
上圖展示了一種可能的索引方式傻寂。左邊是數(shù)據(jù)表,一共有兩列七條記錄携兵,最左邊的是數(shù)據(jù)記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)疾掰。為了加快Col2的查找,可以維護一個右邊所示的二叉查找樹徐紧,每個節(jié)點分別包含索引鍵值和一個指向對應數(shù)據(jù)記錄物理地址的指針静檬,這樣就可以運用二叉查找在 ??(????????2) 的復雜度內獲取到相應數(shù)據(jù)。
雖然這是一個貨真價實的索引并级,但是實際的數(shù)據(jù)庫系統(tǒng)幾乎沒有使用二叉查找樹或其進化品種紅黑樹(red-black tree)實現(xiàn)的拂檩,原因會在下文介紹。
二叉排序樹
在介紹B樹之前嘲碧,先來看另一棵神奇的樹——二叉排序樹(Binary Sort Tree)广恢,首先它是一棵樹,“二叉”這個描述已經很明顯了呀潭,就是樹上的一根樹枝開兩個叉,于是遞歸下來就是二叉樹了(下圖所示)至非,而這棵樹上的節(jié)點是已經排好序的钠署,具體的排序規(guī)則如下:
- 若左子樹不空,則左子樹上所有節(jié)點的值均小于它的根節(jié)點的值
- 若右子樹不空荒椭,則右字數(shù)上所有節(jié)點的值均大于它的根節(jié)點的值
- 它的左谐鼎、右子樹也分別為二叉排序數(shù)(遞歸定義)
從圖中可以看出,二叉排序樹組織數(shù)據(jù)時趣惠,用于查找是比較方便的狸棍,因為每次經過一次節(jié)點時,最多可以減少一半的可能味悄,不過極端情況會出現(xiàn)所有節(jié)點都位于同一側草戈,直觀上看就是一條直線,那么這種查詢的效率就比較低了侍瑟,因此需要對二叉樹左右子樹的高度進行平衡化處理唐片,于是就有了平衡二叉樹(Balenced Binary Tree)丙猬。
所謂“平衡”,說的是這棵樹的各個分支的高度是均勻的费韭,它的左子樹和右子樹的高度之差絕對值小于1茧球,這樣就不會出現(xiàn)一條支路特別長的情況。于是星持,在這樣的平衡樹中進行查找時抢埋,總共比較節(jié)點的次數(shù)不超過樹的高度,這就確保了查詢的效率(時間復雜度為O(logn))
B樹
還是直接看圖比較清楚督暂,圖中所示揪垄,B樹事實上是一種平衡的多叉查找樹,也就是說最多可以開m個叉(m>=2)损痰,我們稱之為m階b樹福侈,為了體現(xiàn)本博客的良心之處,不同于其他地方都能看到2階B樹卢未,這里特意畫了一棵5階B樹 肪凛。
總的來說,m階B樹滿足以下條件:
- 每個節(jié)點至多可以擁有m棵子樹辽社。
- 根節(jié)點伟墙,只有至少有2個節(jié)點(要么極端情況,就是一棵樹就一個根節(jié)點滴铅,單細胞生物戳葵,即是根,也是葉汉匙,也是樹)拱烁。
- 非根非葉的節(jié)點至少有的Ceil(m/2)個子樹(Ceil表示向上取整,圖中5階B樹噩翠,每個節(jié)點至少有3個子樹戏自,也就是至少有3個叉)。
- 非葉節(jié)點中的信息包括[n,A0,K1,A1,K2,A2,…,Kn,An]伤锚,擅笔,其中n表示該節(jié)點中保存的關鍵字個數(shù),K為關鍵字且Ki<Ki+1屯援,A為指向子樹根節(jié)點的指針猛们。
- 從根到葉子的每一條路徑都有相同的長度,也就是說狞洋,葉子節(jié)在相同的層弯淘,并且這些節(jié)點不帶信息,實際上這些節(jié)點就表示找不到指定的值吉懊,也就是指向這些節(jié)點的指針為空耳胎。
B樹的查詢過程和二叉排序樹比較類似惯吕,從根節(jié)點依次比較每個結點,因為每個節(jié)點中的關鍵字和左右子樹都是有序的怕午,所以只要比較節(jié)點中的關鍵字废登,或者沿著指針就能很快地找到指定的關鍵字,如果查找失敗郁惜,則會返回葉子節(jié)點堡距,即空指針。
例如查詢圖中字母表中的K:
- 從根節(jié)點P開始兆蕉,K的位置在P之前羽戒,進入左側指針。
- 左子樹中虎韵,依次比較C易稠、F、J包蓝、M驶社,發(fā)現(xiàn)K在J和M之間。
- 沿著J和M之間的指針测萎,繼續(xù)訪問子樹亡电,并依次進行比較,發(fā)現(xiàn)第一個關鍵字K即為指定查找的值硅瞧。
B樹搜索的簡單偽算法如下:
BTree_Search(node, key) {
if(node == null) return null;
foreach(node.key)
{
if(node.key[i] == key) return node.data[i];
if(node.key[i] > key) return BTree_Search(point[i]->node);
}
return BTree_Search(point[i+1]->node);
}
data = BTree_Search(root, my_key);
B樹的特點可以總結為如下:
- 關鍵字集合分布在整顆樹中份乒。
- 任何一個關鍵字出現(xiàn)且只出現(xiàn)在一個節(jié)點中。
- 搜索有可能在非葉子節(jié)點結束腕唧。
- 其搜索性能等價于在關鍵字集合內做一次二分查找或辖。
- B樹在插入刪除新的數(shù)據(jù)記錄會破壞B-Tree的性質,因為在插入刪除時枣接,需要對樹進行一個分裂孝凌、合并、轉移等操作以保持B-Tree性質月腋。
Plus版 — B+樹
作為B樹的加強版,B+樹與B樹的差異在于
- 有n棵子樹的節(jié)點含有n個關鍵字(也有認為是n-1個關鍵字)瓣赂。
- 所有的關鍵字全部存儲在葉子節(jié)點上榆骚,且葉子節(jié)點本身根據(jù)關鍵字自小而大順序連接。
- 非葉子節(jié)點可以看成索引部分煌集,節(jié)點中僅含有其子樹(根節(jié)點)中的最大(或最屑酥)關鍵字。
B+樹的查找過程苫纤,與B樹類似碉钠,只不過查找時纲缓,如果在非葉子節(jié)點上的關鍵字等于給定值,并不終止喊废,而是繼續(xù)沿著指針直到葉子節(jié)點位置祝高。因此在B+樹,不管查找成功與否污筷,每次查找都是走了一條從根到葉子節(jié)點的路徑工闺。
B+樹的特性如下:
- 所有關鍵字都存儲在葉子節(jié)上,且鏈表中的關鍵字恰好是有序的瓣蛀。
- 不可能非葉子節(jié)點命中返回陆蟆。
- 非葉子節(jié)點相當于葉子節(jié)點的索引,葉子節(jié)點相當于是存儲(關鍵字)數(shù)據(jù)的數(shù)據(jù)層惋增。
- 更適合文件索引系統(tǒng)叠殷。
帶有順序訪問指針的B+Tree
一般在數(shù)據(jù)庫系統(tǒng)或文件系統(tǒng)中使用的B+Tree結構都在經典B+Tree的基礎上進行了優(yōu)化,增加了順序訪問指針诈皿。
如上圖所示林束,在B+Tree的每個葉子節(jié)點增加一個指向相鄰葉子節(jié)點的指針,就形成了帶有順序訪問指針的B+Tree纫塌。做這個優(yōu)化的目的是為了提高區(qū)間訪問的性能诊县,例如圖4中如果要查詢key為從18到49的所有數(shù)據(jù)記錄,當找到18后措左,只需順著節(jié)點和指針順序遍歷就可以一次性訪問到所有數(shù)據(jù)節(jié)點依痊,極大提到了區(qū)間查詢效率。
二叉樹與紅黑樹的比較
從上面我們發(fā)現(xiàn)怎披,紅黑樹相比較于二叉樹又進步了一些胸嘁,但紅黑樹還是有些問題:那就是數(shù)據(jù)量大的話,紅黑樹的深度會很深凉逛,也就是說深度不可控性宏,這樣一來查找數(shù)據(jù)還是會很耗時
BTree
從上面看,我們發(fā)現(xiàn)BTree又進步了一些状飞,查詢速度提高毫胜,存儲容量也沒影響到。當然有人可能會這樣想诬辈,那我們?yōu)槭裁床话褦?shù)據(jù)全部都存在一個節(jié)點酵使,這樣深度不就是1了嗎?
當然不行了!java拿取數(shù)據(jù)一般是這樣的:java程序-->CPU--->內存---->硬盤焙糟,而內存與硬盤的交互是有大小限制的口渔,是一頁數(shù)據(jù)4k左右,所以不能把所有數(shù)據(jù)都放在一個節(jié)點來獲取穿撮,一般來說節(jié)點會盡量預存4K容量缺脉。
看到這里痪欲,我們知道(4K=節(jié)點;節(jié)點=小節(jié)點*小節(jié)點的容量)一個節(jié)點是4K攻礼,而節(jié)點內有幾個小節(jié)點业踢,那么也就是說,只要我們每個的小節(jié)點的data容量越小秘蛔,那么可以存的節(jié)點也就可以更多陨亡。
B+Tree
B+Tree通過把data不放在非葉子節(jié)點來增加度(小節(jié)點),一般會一百個以上使得深度是3~5深员,從而減少查詢次數(shù)负蠕。并且,葉子節(jié)點之間會有指針倦畅,數(shù)據(jù)又是遞增的遮糖,這使得我們范圍查找可以通過指針連接查找,而不再從上面節(jié)點往下一個個找叠赐。
結論:B+Tree 既減少查詢次數(shù)又提供了很好的范圍查詢
參考:https://blog.csdn.net/caijunsen/article/details/83045985
MySQL為什么使用B樹(B+樹)
紅黑樹等數(shù)據(jù)結構也可以用來實現(xiàn)索引欲账,但是文件系統(tǒng)以及數(shù)據(jù)庫系統(tǒng)普遍采用B樹或者B+樹,這一節(jié)將結合計算機組成原理相關知識討論B-/+Tree作為索引的理論基礎芭概。
一般來說赛不,索引本身也很大,不可能全部存儲在內存中罢洲,因此索引往往以索引文件的形式存儲在磁盤上踢故。這樣的話,索引查找過程中就要產生磁盤I/O消耗惹苗,相對于內存存取殿较,I/O存取的消耗要高幾個數(shù)量級,所以評價一個數(shù)據(jù)結構作為索引的優(yōu)劣最重要的指標就是在查找過程中磁盤I/O操作次數(shù)的漸進復雜度桩蓉。換句話說淋纲,索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數(shù)。下面先介紹內存和磁盤存取原理院究,然后再結合這些原理分析B-/+Tree作為索引的效率洽瞬。
主存存取原理
目前計算機使用的主存基本都是隨機讀寫存儲器(RAM),現(xiàn)代RAM的結構和存取原理比較復雜业汰,這里本文拋卻具體差別伙窃,抽象出一個十分簡單的存取模型來說明RAM的工作原理。
從抽象角度看蔬胯,主存是一系列的存儲單元組成的矩陣,每個存儲單元存儲固定大小的數(shù)據(jù)位他。每個存儲單元有唯一的地址氛濒,現(xiàn)代主存的編址規(guī)則比較復雜产场,這里將其簡化成一個二維地址:通過一個行地址和一個列地址可以唯一定位到一個存儲單元。上圖展示了一個4 x 4的主存模型舞竿。
主存的存取過程如下:
當系統(tǒng)需要讀取主存時京景,則將地址信號放到地址總線上傳給主存,主存讀到地址信號后骗奖,解析信號并定位到指定存儲單元确徙,然后將此存儲單元數(shù)據(jù)放到數(shù)據(jù)總線上,供其它部件讀取执桌。
寫主存的過程類似鄙皇,系統(tǒng)將要寫入單元地址和數(shù)據(jù)分別放在地址總線和數(shù)據(jù)總線上,主存讀取兩個總線的內容仰挣,做相應的寫操作伴逸。
這里可以看出,主存存取的時間僅與存取次數(shù)呈線性關系膘壶,因為不存在機械操作错蝴,兩次存取的數(shù)據(jù)的“距離”不會對時間有任何影響,例如颓芭,先取A0再取A1和先取A0再取D3的時間消耗是一樣的顷锰。
磁盤存取原理
上文說過,索引一般以文件形式存儲在磁盤上亡问,索引檢索需要磁盤I/O操作官紫。與主存不同,磁盤I/O存在機械運動耗費玛界,因此磁盤I/O的時間消耗是巨大的万矾。
下圖是磁盤的整體結構示意圖:
一個磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)慎框。在磁盤的一側有磁頭支架良狈,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容笨枯。磁頭不能轉動薪丁,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的馅精,即從正上方向下看严嗜,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)洲敢。
下圖是磁盤結構的示意圖:
盤片被劃分成一系列同心環(huán)漫玄,圓心是盤片中心,每個同心環(huán)叫做一個磁道,所有半徑相同的磁道組成一個柱面睦优。磁道被沿半徑線劃分成一個個小的段渗常,每個段叫做一個扇區(qū),每個扇區(qū)是磁盤的最小存儲單元汗盘。為了簡單起見皱碘,我們下面假設磁盤只有一個盤片和一個磁頭。
當需要從磁盤讀取數(shù)據(jù)時隐孽,系統(tǒng)會將數(shù)據(jù)邏輯地址傳給磁盤癌椿,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數(shù)據(jù)在哪個磁道菱阵,哪個扇區(qū)踢俄。為了讀取這個扇區(qū)的數(shù)據(jù),需要將磁頭放到這個扇區(qū)上方送粱,為了實現(xiàn)這一點褪贵,磁頭需要移動對準相應磁道,這個過程叫做尋道抗俄,所耗費時間叫做尋道時間脆丁,然后磁盤旋轉將目標扇區(qū)旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間动雹。
局部性原理與磁盤預讀
由于存儲介質的特性槽卫,磁盤本身存取就比主存慢很多,再加上機械運動耗費胰蝠,磁盤的存取速度往往是主存的幾百分分之一歼培,因此為了提高效率,要盡量減少磁盤I/O茸塞。為了達到這個目的躲庄,磁盤往往不是嚴格按需讀取,而是每次都會預讀钾虐,即使只需要一個字節(jié)噪窘,磁盤也會從這個位置開始,順序向后讀取一定長度的數(shù)據(jù)放入內存效扫。這樣做的理論依據(jù)是計算機科學中著名的局部性原理:
當一個數(shù)據(jù)被用到時倔监,其附近的數(shù)據(jù)也通常會馬上被使用。
所以菌仁,程序運行期間所需要的數(shù)據(jù)通常應當比較集中浩习。
由于磁盤順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間)济丘,因此對于具有局部性的程序來說谱秽,預讀可以提高I/O效率。
預讀的長度一般為頁(page)的整倍數(shù)。頁是計算機管理存儲器的邏輯塊疟赊,硬件及操作系統(tǒng)往往將主存和磁盤存儲區(qū)分割為連續(xù)的大小相等的塊辱士,每個存儲塊稱為一頁(在許多操作系統(tǒng)中,頁得大小通常為4k)听绳,主存和磁盤以頁為單位交換數(shù)據(jù)。當程序要讀取的數(shù)據(jù)不在主存中時异赫,會觸發(fā)一個缺頁異常椅挣,此時系統(tǒng)會向磁盤發(fā)出讀盤信號,磁盤會找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內存中塔拳,然后異常返回鼠证,程序繼續(xù)運行。
B-/+Tree索引的性能分析
到這里終于可以分析B-/+Tree索引的性能了靠抑。
上文說過一般使用磁盤I/O次數(shù)評價索引結構的優(yōu)劣量九。先從B-Tree分析,根據(jù)B-Tree的定義颂碧,可知檢索一次最多需要訪問h個節(jié)點荠列。數(shù)據(jù)庫系統(tǒng)的設計者巧妙利用了磁盤預讀原理,將一個節(jié)點的大小設為等于一個頁载城,這樣每個節(jié)點只需要一次I/O就可以完全載入肌似。為了達到這個目的,在實際實現(xiàn)B-Tree還需要使用如下技巧:
每次新建節(jié)點時诉瓦,直接申請一個頁的空間川队,這樣就保證一個節(jié)點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的睬澡,就實現(xiàn)了一個node只需一次I/O固额。
B-Tree中一次檢索最多需要h-1次I/O(根節(jié)點常駐內存),漸進復雜度為 ??(?)=??(??????????)煞聪。
一般實際應用中斗躏,出度d是非常大的數(shù)字,通常超過100米绕,因此h非常猩贰(通常不超過3)。(h表示樹的高度 & 出度d表示的是樹的度栅干,即樹中各個節(jié)點的度的最大值)
綜上所述迈套,用B-Tree作為索引結構效率是非常高的。
而紅黑樹這種結構碱鳞,h明顯要深的多桑李。由于邏輯上很近的節(jié)點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進復雜度也為O(h)贵白,效率明顯比B-Tree差很多率拒。
上文還說過,B+Tree更適合外存索引禁荒,原因和內節(jié)點出度d有關猬膨。從上面分析可以看到,d越大索引的性能越好呛伴,而出度的上限取決于節(jié)點內key和data的大胁铡:
????????=??????????(????????????????/(??????????????+????????????????+??????????????????))
floor表示向下取整。由于B+Tree內節(jié)點去掉了data域热康,因此可以擁有更大的出度沛申,擁有更好的性能。
在MySQL中姐军,索引屬于存儲引擎級別的概念铁材,不同存儲引擎對索引的實現(xiàn)方式是不同的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現(xiàn)方式奕锌。
MyISAM 非聚簇索引
MyISAM引擎使用B+Tree作為索引結構著觉,葉節(jié)點的data域存放的是數(shù)據(jù)記錄的地址。下圖是MyISAM索引的原理圖:
這里設表一共有三列惊暴,假設我們以Col1為主鍵固惯,則上圖是一個MyISAM表的主索引(Primary key)示意〗墒兀可以看出MyISAM的索引文件僅僅保存數(shù)據(jù)記錄的地址葬毫。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區(qū)別屡穗,只是主索引要求key是唯一的贴捡,而輔助索引的key可以重復。如果我們在Col2上建立一個輔助索引村砂,則此索引的結構如下圖所示:
同樣也是一棵B+樹烂斋,data域保存數(shù)據(jù)記錄的地址。因此础废,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引汛骂,如果指定的Key存在,則取出其data域的值评腺,然后以data域的值為地址帘瞭,讀取相應數(shù)據(jù)記錄。
MyISAM的索引方式也叫做“非聚集”的蒿讥,之所以這么稱呼是為了與InnoDB的聚集索引區(qū)分蝶念。
InnoDB索引實現(xiàn)
雖然InnoDB也使用B+Tree作為索引結構抛腕,但具體實現(xiàn)方式卻與MyISAM截然不同。
第一個重大區(qū)別是InnoDB的數(shù)據(jù)文件本身就是索引文件媒殉。從上文知道担敌,MyISAM索引文件和數(shù)據(jù)文件是分離的,索引文件僅保存數(shù)據(jù)記錄的地址廷蓉。而在InnoDB中全封,表數(shù)據(jù)文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節(jié)點data域保存了完整的數(shù)據(jù)記錄桃犬。這個索引的key是數(shù)據(jù)表的主鍵售貌,因此InnoDB表數(shù)據(jù)文件本身就是主索引。
主索引 (Primary Key)
上圖是InnoDB主索引(同時也是數(shù)據(jù)文件)的示意圖疫萤,可以看到葉節(jié)點包含了完整的數(shù)據(jù)記錄。這種索引叫做聚集索引敢伸。因為InnoDB的數(shù)據(jù)文件本身要按主鍵聚集扯饶,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定池颈,則MySQL系統(tǒng)會自動選擇一個可以唯一標識數(shù)據(jù)記錄的列作為主鍵尾序,如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含字段作為主鍵躯砰,這個字段長度為6個字節(jié)每币,類型為長整型。
輔助索引(Secondary Key)
第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址琢歇。換句話說兰怠,InnoDB的所有輔助索引都引用主鍵作為data域。例如李茫,上圖為定義在Col3上的一個輔助索引:
這里以英文字符的ASCII碼作為比較準則揭保。聚集索引這種實現(xiàn)方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵魄宏,然后用主鍵到主索引中檢索獲得記錄秸侣。
了解不同存儲引擎的索引實現(xiàn)方式對于正確使用和優(yōu)化索引都非常有幫助,例如知道了InnoDB的索引實現(xiàn)后宠互,就很容易明白為什么不建議使用過長的字段作為主鍵味榛,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大予跌。再例如搏色,用非單調的字段作為主鍵在InnoDB中不是個好主意,因為InnoDB數(shù)據(jù)文件本身是一棵B+Tree券册,非單調的主鍵會造成在插入新記錄時數(shù)據(jù)文件為了維持B+Tree的特性而頻繁的分裂調整继榆,十分低效巾表,而使用自增字段作為主鍵則是一個很好的選擇。
參考資料
- MySQL索引背后的數(shù)據(jù)結構及算法原理
- B樹略吨、B-樹集币、B+樹、B*樹【轉】,mysql索引
- MySQL 和 B 樹的那些事
- https://www.cnblogs.com/boothsun/p/8970952.html
Kotlin 開發(fā)者社區(qū)
越是喧囂的世界,越需要寧靜的思考河质。
合抱之木冀惭,生于毫末;
九層之臺掀鹅,起于壘土散休;
千里之行,始于足下乐尊。
積土成山戚丸,風雨興焉;
積水成淵扔嵌,蛟龍生焉限府;
積善成德,而神明自得痢缎,圣心備焉谣殊。
故不積跬步,無以至千里牺弄;
不積小流姻几,無以成江海。
騏驥一躍势告,不能十步蛇捌;
駑馬十駕,功在不舍咱台。
鍥而舍之络拌,朽木不折;
鍥而不舍回溺,金石可鏤春贸。
蚓無爪牙之利混萝,筋骨之強,上食埃土萍恕,下飲黃泉逸嘀,用心一也。
蟹六跪而二螯允粤,非蛇鱔之穴無可寄托者崭倘,用心躁也。