簡(jiǎn)單說(shuō)說(shuō)列式存儲(chǔ)數(shù)據(jù)庫(kù)

隨著業(yè)務(wù)越來(lái)越看重?cái)?shù)據(jù)的重要性,相信大家也做了很多多維分析的需求,在調(diào)研技術(shù)選型時(shí)候,會(huì)發(fā)現(xiàn)很多olap,如druid、clickhouse、starRocks都是列式存儲(chǔ)數(shù)據(jù)庫(kù),今天我們來(lái)通過(guò)對(duì)比行存儲(chǔ)簡(jiǎn)單說(shuō)下列存儲(chǔ)

舉個(gè)栗子來(lái)說(shuō)下列存儲(chǔ)和行存儲(chǔ)

要過(guò)年回家了,今天做了核酸檢測(cè),我們就以存儲(chǔ)核酸檢測(cè)為業(yè)務(wù)場(chǎng)景

栗子

核酸檢測(cè)表
  • 就以應(yīng)用頁(yè)面為主,存儲(chǔ)核酸記錄需要存儲(chǔ)以下幾個(gè)字段
  • 姓名身份證號(hào)、檢測(cè)機(jī)構(gòu)檢測(cè)時(shí)間、結(jié)果價(jià)格

行存儲(chǔ)

  • 在行存儲(chǔ)下(如mysql等),大家都比較熟悉,就不多說(shuō)了,每條記錄都是表中的每一行,表存儲(chǔ)如下
    | 姓名 | 身份證號(hào) | 檢測(cè)機(jī)構(gòu) | 檢測(cè)時(shí)間 | 結(jié)果 | 價(jià)格 |
    |-----|-----|--|----------|-----|-----|
    | 彥祖 | 123512387 | 北京 | 2021-12-24 12:12:45 | 陰性 |35|
    | 德華 | 213124157 | 上海 | 2021-12-22 12:12:45 | 陰性 |20|
    | 路人甲 | 213123145 | 河南 | 2021-12-21 12:12:45 | 陰性 | 8 |
    | 德華 | 213124157 | 廣州 | 2021-12-29 12:12:45 | 陰性 | 23|
    | 彥祖 | 123512387 | 上海 | 2021-12-30 12:12:45 | 陰性 | 20|

  • 因?yàn)閿?shù)據(jù)都是按行方式存儲(chǔ),所以在物理存儲(chǔ)中,會(huì)以連續(xù)空間來(lái)存儲(chǔ)數(shù)據(jù),物理存儲(chǔ)中,這些數(shù)據(jù)存儲(chǔ)方式如下:


    行存儲(chǔ)物理結(jié)構(gòu)
行存儲(chǔ)優(yōu)點(diǎn)分析
  • 在這樣的物理結(jié)構(gòu)下,因?yàn)槭沁B續(xù)空間,所以插入一條數(shù)據(jù)只需要追加到當(dāng)前數(shù)據(jù)之后即可,很方便
  • 對(duì)于按記錄查詢也很方便,例如:我們要查詢彥祖的所有核酸記錄,頁(yè)面應(yīng)用的話應(yīng)該是通過(guò)彥祖的身份證號(hào)
  • 對(duì)應(yīng)的sql如下:
select * from 核酸記錄表 where 身份證號(hào)='彥祖的身份證號(hào)'
  • 這個(gè)sql的執(zhí)行流程比較清晰
1.先從索引查詢出來(lái)彥祖的記錄存儲(chǔ)的物理地址
2.在通過(guò)物理地址去表的物理存儲(chǔ)中查詢對(duì)應(yīng)地址中的數(shù)據(jù)
  • 這樣就可以快速得到彥祖的核酸記錄
行存儲(chǔ)缺點(diǎn)分析
  • 這時(shí)候,業(yè)務(wù)方提了一個(gè)需求,他要統(tǒng)計(jì)彥祖做核酸總共花了多少錢
  • 對(duì)于這個(gè)需求,sql實(shí)現(xiàn)也很簡(jiǎn)單,通過(guò)對(duì)價(jià)格sum就可以實(shí)現(xiàn),sql如下:
select sum(價(jià)格) from 核酸記錄表 where 身份證號(hào)='彥祖的身份證號(hào)'
  • 這個(gè)sql的執(zhí)行流程也比較清晰
1.先從索引查詢出來(lái)彥祖的記錄存儲(chǔ)的物理地址
2.在通過(guò)物理地址去表的物理存儲(chǔ)中查詢對(duì)應(yīng)地址中的數(shù)據(jù)
3.拿到所有數(shù)據(jù)時(shí)候,在通過(guò)對(duì)于價(jià)格列sun聚合得到結(jié)果
  • 分析下,因?yàn)樾写鎯?chǔ)使用的是連續(xù)空間,即使需求里面只需要select sum(價(jià)格),但是讀取物理存儲(chǔ)時(shí)候,還是讀取出來(lái)了所有的字段
行存儲(chǔ)優(yōu)缺點(diǎn)總結(jié)
  • 通過(guò)上面的分析,總結(jié)一下行存儲(chǔ)的優(yōu)缺點(diǎn)
  • 優(yōu)點(diǎn)
1.連續(xù)空間對(duì)于插入/更新很方便
2.對(duì)于記錄查詢很方便
  • 缺點(diǎn)
1.會(huì)查詢出來(lái)很多不需要的列

列存儲(chǔ)

姓名 身份證號(hào) 檢測(cè)機(jī)構(gòu) 檢測(cè)時(shí)間 結(jié)果 價(jià)格
彥祖 123512387 北京 2021-12-24 12:12:45 陰性 35
德華 213124157 上海 2021-12-22 12:12:45 陰性 20
路人甲 213123145 河南 2021-12-21 12:12:45 陰性 8
德華 213124157 廣州 2021-12-29 12:12:45 陰性 23
彥祖 123512387 上海 2021-12-30 12:12:45 陰性 20
  • 在列存儲(chǔ)中,對(duì)于同樣的核酸記錄表,存儲(chǔ)的物理結(jié)構(gòu)如下:
列物理結(jié)構(gòu)
  • 在列式存儲(chǔ)中,會(huì)把每一列存儲(chǔ)到一起,如姓名列,是把所有記錄中的姓名這列的值使用連續(xù)空間存放到一起
  • 而對(duì)于各個(gè)列之間,是沒有必要使用連續(xù)空間存放到一起的,所以很多列式數(shù)據(jù)庫(kù)都使用了分布式存儲(chǔ)的方式,存儲(chǔ)各個(gè)列
  • 下面我們來(lái)分析下列存儲(chǔ)的數(shù)據(jù)壓縮查詢執(zhí)行流程
列存儲(chǔ)的數(shù)據(jù)壓縮
  • 很多列式數(shù)據(jù)庫(kù)都是通過(guò)字典表的方式進(jìn)行數(shù)據(jù)壓縮
  • 因?yàn)槭前衙恳涣写娣诺揭黄鸬?,所以很容易通過(guò)對(duì)于每一列進(jìn)行去重,來(lái)構(gòu)建一個(gè)字典表,例如:
  • 對(duì)于姓名列,這列的所有數(shù)據(jù)如下:
彥祖|德華|路人甲|德華|彥祖
  • 對(duì)這列值去重以后,構(gòu)建一張姓名字典表,構(gòu)建算法忽略,就使用自增id的方式,如下:
    | id | 姓名列 |
    |-----|-----|
    |1| 彥祖 |
    |2| 德華 |
    |3| 路人甲 |
  • 這樣構(gòu)建字典表,對(duì)于列存儲(chǔ)的物理存儲(chǔ)結(jié)構(gòu),就可以執(zhí)行存儲(chǔ)字典表中的id,而不用存儲(chǔ)具體的值,有了字典表以后姓名列存儲(chǔ)如下:
1|2|3|2|1
  • 同樣對(duì)于價(jià)格列,這列的所有數(shù)據(jù)如下:
35|20|8|23|20
  • 對(duì)這列值去重以后,構(gòu)建一張價(jià)格字典表,構(gòu)建算法忽略,就使用自增id的方式,如下:
    | id | 價(jià)格列 |
    |-----|-----|
    |1| 35 |
    |2| 20 |
    |3| 8 |
    |4|23|

  • 有了字典表以后價(jià)格列存儲(chǔ)如下:

1|2|3|4|2
  • 這樣通過(guò)一些數(shù)據(jù)壓縮算法等,可以對(duì)數(shù)據(jù)存儲(chǔ)進(jìn)行壓縮
列存儲(chǔ)的查詢執(zhí)行過(guò)程
  • 有字典表以后,我們來(lái)看下,列存儲(chǔ)一般是如何進(jìn)行查詢的
  • 業(yè)務(wù)需求查詢彥祖,20塊錢做的核酸記錄:
select * from 核酸記錄表 where 姓名=彥祖 and 價(jià)格=20
  • 對(duì)于該sql,執(zhí)行過(guò)程如下:
1.對(duì)于where 姓名=彥祖
首先查詢姓名字典表,查詢到彥祖的id=1
id 姓名列
1 彥祖
2 德華
3 路人甲
2.通過(guò)查詢到彥祖的id,對(duì)于性名列進(jìn)行對(duì)比,構(gòu)建一個(gè)bitmap,把匹配的要的列的索引位設(shè)置為1,否則為0
姓名列
3.對(duì)于where 價(jià)格=20
和上面一樣的操作,先查詢價(jià)格字段表,20的id=2
id 價(jià)格列
1 35
2 20
3 8
4 23
4.通過(guò)查詢到價(jià)格20的id,對(duì)于價(jià)格列進(jìn)行對(duì)比,構(gòu)建一個(gè)bitmap,把匹配的要的列的索引位設(shè)置為1,否則為0
價(jià)格列
5.對(duì)于兩個(gè)where條件的結(jié)果bitmap做與運(yùn)算,bitmap中,位為1的索引就是要查詢數(shù)據(jù)的所有列的索引,如該栗子中,兩個(gè)結(jié)果bitmap與運(yùn)算后的結(jié)果是00001,所以所有列的第5個(gè)值,拼接起來(lái)就是我們要查詢的數(shù)據(jù)
與運(yùn)算
6.所以我們把所有列的第五個(gè)值拿出來(lái)組裝后就是我們需要的數(shù)據(jù)
列存儲(chǔ)優(yōu)點(diǎn)分析
  • 上面講了列存儲(chǔ)的數(shù)據(jù)壓縮,在數(shù)據(jù)壓縮上列存儲(chǔ)有一定的優(yōu)勢(shì)
  • 每一列都可以天然做索引,不需要額外的數(shù)據(jù)結(jié)構(gòu)來(lái)對(duì)各個(gè)列構(gòu)建索引,所以不用在意每一列的數(shù)據(jù)類型,都可以做索引
  • 對(duì)于統(tǒng)計(jì)彥祖做核酸總共花了多少錢這種需求
select sum(價(jià)格) from 核酸記錄表 where 身份證號(hào)='彥祖的身份證號(hào)'
  • 因?yàn)榱惺?code>分開存儲(chǔ)的,按照上面講的查詢流程,其實(shí)最后我們得到的結(jié)果bitmap,拿到位=1的索引后,我們不需要查詢所有的列,只需要拿著索引去價(jià)格列中獲取相應(yīng)位置的值,然后在進(jìn)行sum聚合
列存儲(chǔ)缺點(diǎn)分析
  • 因?yàn)楦鱾€(gè)列是分開存儲(chǔ)的,所以在插入、更新時(shí),需要對(duì)于每一個(gè)列進(jìn)行操作,沒有行存儲(chǔ)連續(xù)空間那么方便
  • 還是看上面說(shuō)的查詢過(guò)程,每次查詢過(guò)后,都需要對(duì)查詢到的需要的列進(jìn)行一個(gè)數(shù)據(jù)組裝
列存儲(chǔ)優(yōu)缺點(diǎn)總結(jié)
  • 通過(guò)上面的分析,總結(jié)一下列存儲(chǔ)的優(yōu)缺點(diǎn)
  • 優(yōu)點(diǎn)
1.數(shù)據(jù)壓縮比較有優(yōu)勢(shì)
2.任何列都可以做索引
3.查詢時(shí)只有涉及到的列會(huì)被讀取
  • 缺點(diǎn)
1.每次查詢時(shí),都需要對(duì)查詢到的列進(jìn)行數(shù)據(jù)重新組裝
2.插入/更新操作比較困難

列式存儲(chǔ)數(shù)據(jù)庫(kù)就說(shuō)到這里,歡迎大家來(lái)交流,指出文中一些說(shuō)錯(cuò)的地方,讓我加深認(rèn)識(shí),愿大家沒有bug,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容