亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

作為一個(gè)天天都在CRUD的程序員,你有沒(méi)有想過(guò),數(shù)據(jù)庫(kù)是如何工作的?

我猜,你曾經(jīng)無(wú)數(shù)次的翻開(kāi)講數(shù)據(jù)庫(kù)的書籍和文章,但總是看著看著就被勸退,太多的專業(yè)術(shù)語(yǔ)把人頭都搞大了。

等等,看這畫風(fēng),今天是要賣課的節(jié)奏??!

NO!絕不賣課,請(qǐng)放心閱讀。

一直以來(lái),我都很想深入學(xué)習(xí)一下數(shù)據(jù)庫(kù)。我這個(gè)人學(xué)東西,如果只知其然而不知其所以然是非常難受的,啥都想去了解下背后的原理,學(xué)編程語(yǔ)言是這樣,學(xué)操作系統(tǒng)也是這樣。數(shù)據(jù)庫(kù)這個(gè)東西天天都在用,所以學(xué)習(xí)一下背后的原理也是非常實(shí)用和有必要的。

但無(wú)論是哪本書、哪個(gè)視頻教程,一打開(kāi)就被無(wú)數(shù)的專業(yè)術(shù)語(yǔ)淹沒(méi),太多概念淹沒(méi)了數(shù)據(jù)庫(kù)最本質(zhì)最核心的東西。

所以今天,讓我們從一個(gè)最最最簡(jiǎn)單的模型開(kāi)始,揭開(kāi)數(shù)據(jù)庫(kù)神秘的一角。

對(duì)我們使用者而言,數(shù)據(jù)庫(kù)就像是一個(gè)黑盒子,你可以往它里面寫入數(shù)據(jù),也可以從它里面讀出數(shù)據(jù)。

 

讓我們暫時(shí)拋卻SQL、網(wǎng)絡(luò)連接、數(shù)據(jù)庫(kù)等等概念,就來(lái)看這個(gè)最基本的需求:如果我們來(lái)實(shí)現(xiàn)一個(gè)可以對(duì)外提供讀寫數(shù)據(jù)的黑盒子,該怎么做?

假設(shè)我們的黑盒子很簡(jiǎn)單,里面只有一張表:user_info,用來(lái)存儲(chǔ)用戶信息。

表里面也很簡(jiǎn)單,只有三個(gè)字段,分別記錄用戶的ID、姓名和手機(jī)號(hào)。

user_id(uint32)
name(char[8])
phone(char[20])

我們可以用一個(gè)簡(jiǎn)單的結(jié)構(gòu)體(或者一個(gè)class)來(lái)表示一條數(shù)據(jù):

struct DataRecord{
  uint32 user_id;
  char name[8];
  char phone[20];
};

user_id是一個(gè)uint32類型,占4個(gè)字節(jié),name占8個(gè)字節(jié),phone占20個(gè)字節(jié),加起來(lái)一條數(shù)據(jù)總共占32個(gè)字節(jié)。

我們選擇用一個(gè)文件來(lái)存儲(chǔ)這些數(shù)據(jù),存儲(chǔ)非常簡(jiǎn)單,只需要一條一條的碼在一起就行了,就像這樣:

 

數(shù)據(jù)存儲(chǔ)方式有了,接下來(lái)就是如何來(lái)讀寫了,我們來(lái)提供兩個(gè)函數(shù),分別來(lái)插入(insert)和查詢(select)數(shù)據(jù):

// 偽代碼
void insert(Table* table, DataRecord record) {
  fp = open(table->file_name);
  fseek(fp, FILE_END);
  write(fp, sizeof(DataRecord));
  close(fp);
}

插入很簡(jiǎn)單,直接打開(kāi)表對(duì)應(yīng)的數(shù)據(jù)文件,然后把文件指針移動(dòng)到文件尾部,接著追加數(shù)據(jù),最后關(guān)閉文件,大功告成。這和把大象關(guān)進(jìn)冰箱的步驟是一樣一樣的。

接下來(lái)是查詢,我們提供一個(gè)可以通過(guò)id來(lái)查詢用戶的函數(shù):

// 偽代碼
DataRecord select(uint32 user_id) {

  DataRecord result;
  fp = open(table->file_name);
  
  while (1) {
    if (feof(fp)) 
      break;
      
    DataRecord record;
    read(fp, &record, sizeof(DataRecord));
    if (record.user_id == user_id) {
      result = record;
      break;
    }
  }
  
  close(fp);
  
  return result;
}

查詢也很簡(jiǎn)單,我們打開(kāi)文件,一條一條讀取數(shù)據(jù),然后比較用戶id和給定的參數(shù)是否相同,直到找到符合條件的數(shù)據(jù)返回。

好了,以上,我們就實(shí)現(xiàn)了一個(gè)最最最基礎(chǔ)的黑盒子:它里面有一張表,然后可以往里面寫數(shù)據(jù),從里面查數(shù)據(jù)。

現(xiàn)在來(lái)思考一下:

如果我們寫了很多數(shù)據(jù)進(jìn)去,比如幾十萬(wàn)條,我們要查一個(gè)指定id的數(shù)據(jù),要按照這樣一條條比對(duì),那不得等到猴年馬月去了?

為什么要一條條比對(duì)呢?是因?yàn)槲覀兊臄?shù)據(jù)全都是一條一條碼在一起的,完全沒(méi)有順序,所以要查詢,只能這樣一條條檢查——全表掃描。

如果我們的數(shù)據(jù)是有順序的呢?

假如我們插入數(shù)據(jù)的時(shí)候,是按照id從小到大排列著,這樣我們就能用二分法快速找到指定id的數(shù)據(jù)了。

 

看上面這張圖,假設(shè)我們要查找id為9的數(shù)據(jù),我們可以讀取第一條數(shù)據(jù)的id是1,就知道id為9的數(shù)據(jù)肯定在它后面。然后再讀取最后一條數(shù)據(jù)id是12,就知道id為9的數(shù)據(jù)肯定在它前面,然后選擇中間的數(shù)據(jù)讀取,如此二分查找,很快就能鎖定目標(biāo),不用每條數(shù)據(jù)都讀取了。

因?yàn)槊織l數(shù)據(jù)都是32個(gè)字節(jié),所以可以非常方便定位任意一條數(shù)據(jù)的位置:第n條數(shù)據(jù)的位置在32*(n-1)偏移處。

通過(guò)改變數(shù)據(jù)的存儲(chǔ)組織形式,我們可以把數(shù)據(jù)查找的時(shí)間復(fù)雜度從O(N)下降到O(LogN)。

但如此一來(lái),查找是變快了,但插入就麻煩了。以前的時(shí)候,我們直接把數(shù)據(jù)塞到文件最后就拍拍屁股走人了。但現(xiàn)在不行了,我們得把數(shù)據(jù)按照順序,插入到合適的位置。

最麻煩的是,我們的數(shù)據(jù)是一條一條挨個(gè)碼在一起的,如果中間某個(gè)位置要插入數(shù)據(jù),就得把那個(gè)位置及其以后的數(shù)據(jù)通通往后移動(dòng),這就涉及到大量的數(shù)據(jù)復(fù)制移動(dòng),開(kāi)銷非常大。

要是每來(lái)一條insert操作就數(shù)據(jù)大量遷徙,那不得累得半死?

(其實(shí)不止插入,刪除數(shù)據(jù)delete也同樣如此麻煩)

就沒(méi)有一種辦法,可以同時(shí)插入快,查詢也快嗎?


仔細(xì)思考一下,前面我們把數(shù)據(jù)按順序一條一條碼在一起,查起來(lái)為什么快?

是因?yàn)樽隽伺判蛞院?,?shù)據(jù)的存儲(chǔ)位置有一條隱含的信息:如果id比我們要找的小,那我們要找的肯定在它后面;反之,如果id比我們要找的大,那我們要找的肯定在它前面。

之所以有這個(gè)規(guī)律,是因?yàn)槲覀儼磇d的大小進(jìn)行了排序存儲(chǔ)。

那如果現(xiàn)在回到最開(kāi)始的那種方式,不排序了,還是一條一條順序來(lái)寫入,就沒(méi)有這個(gè)信息了。

但如果,我們?cè)诿織l數(shù)據(jù)記錄中增加一些額外的信息,用來(lái)指示id比它小的在哪里,id比它大的又在哪里,是不是就能順著這些額外的信息“順藤摸瓜”找到你要找的數(shù)據(jù)呢?

或許,聰明的你已經(jīng)看出來(lái)了,這是按照“二叉樹”的形式在記錄數(shù)據(jù)位置信息。

 

但實(shí)際上,二叉樹也才兩個(gè)分叉,如果數(shù)據(jù)量很大的話,這棵樹就會(huì)很高很瘦。

而每一次走入一個(gè)分支,就對(duì)應(yīng)著一次文件I/O,所以在實(shí)際使用中,不會(huì)使用二叉樹,而是使用開(kāi)了非常多個(gè)叉的樹——B樹或者B+樹。

 

如果用B樹或者B+樹來(lái)將文件中的數(shù)據(jù)在邏輯上組織起來(lái),要查找數(shù)據(jù)就會(huì)快得多。

用id來(lái)查找數(shù)據(jù)問(wèn)題解決了,但如果要用name來(lái)查找又該怎么辦呢?

想一想,如果另外有一個(gè)文件,記錄了每個(gè)name和這個(gè)name對(duì)應(yīng)的數(shù)據(jù)記錄在文件中的偏移位置,就像這樣:

user_id數(shù)據(jù)位置(偏移)xuanyuan0shuaidi31april63dibingfa95

有了這個(gè)東西,是不是就簡(jiǎn)單很多了?只需要在這里搜一遍,拿到數(shù)據(jù)的位置,然后打開(kāi)文件,定位到對(duì)應(yīng)的偏移,一下就讀出來(lái)了。

我們可以另外準(zhǔn)備一個(gè)文件,同樣使用B樹或者B+樹的形式來(lái)組織上面表格中的對(duì)應(yīng)關(guān)系。

聰明的你可能已經(jīng)看出來(lái)了,這玩意兒其實(shí)就是索引。當(dāng)然實(shí)際中的數(shù)據(jù)庫(kù)系統(tǒng)的索引實(shí)現(xiàn)或多或少有一些差別,但道理是通用的。

 

原文鏈接:
https://mp.weixin.qq.com/s/wUwJ4sPapE74AbOE2j5hMw

分享到:
標(biāo)簽:Github
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定