介绍
关于数据库的使用场景,常用的分为OLTP和OLAP 2种,OLTP型数据库以行存为主,OLAP型数据库大多以列存为主,那么如何从数据库方面验证具体的存储方式,本文利用Oracle与 Clickhouse数据库,通过几个小方法进行验证,纯属娱乐。
最近项目需要,了解了一下Clickhouse,其实早就听说过此数据库,印象也是停留在这是一款分析型列存数据库,适合OLAP业务场景,看官方文档第一节,更加清晰的认识了一下。
行存和列存存储方式
所谓行存,在数据库底层以行为单位存储,一行的各个字段大概率在一个数据块,形式如下:
| Row | WatchID | JavaEnable | Title | GoodEvent | EventTime |
|---|---|---|---|---|---|
| #0 | 89354350662 | 1 | Investor Relations | 1 | 2016-05-18 05:19:20 |
| #1 | 90329509958 | 0 | Contact us | 1 | 2016-05-18 08:10:20 |
| #2 | 89953706054 | 1 | Mission | 1 | 2016-05-18 07:38:00 |
| #N | … | … | … | … | … |
处于同一行中的数据总是被物理的存储在一起。
常见的行式数据库系统有:Oracle 、 Mysql 、pg等。
所谓列存,在数据库底层以列为单位存储,同一字段的不同行大概率在一个数据块,形式如下:
| Row: | #0 | #1 | #2 | #N |
|---|---|---|---|---|
| WatchID: | 89354350662 | 90329509958 | 89953706054 | … |
| JavaEnable: | 1 | 0 | 1 | … |
| Title: | Investor Relations | Contact us | Mission | … |
| GoodEvent: | 1 | 1 | 1 | … |
| EventTime: | 2016-05-18 05:19:20 | 2016-05-18 08:10:20 | 2016-05-18 07:38:00 | … |
行存和列存读取方式
既然行存和列存 存储方式不同,进行查询分析时,方式自然也不同。
行式,假如需要读取表中的3个字段,形式如下:
在没有索引加持的情况下,会读取整行数据,然后筛选所需的3个字段记录。

列式,假如需要读取表中的3个字段,形式如下:
按列读取数据,直接读取3个字段,读取的数据块大大降低。

存储验证方式
方式一 数据库元数据信息
Oracle 元数据表 dba_segments 可以方便的查到整个表的大小,但没有元数据表可以查到具体字段的大小。
SQL> select sum(bytes)/1024/1024/1024 from dba_segments where segment_name='T_PART_GLOBAL_PSDDU';
SUM(BYTES)/1024/1024/1024
-------------------------
.390625
Clickhouse 元数据表system.parts查询整个表,system.parts_columns查询表每个字段大小。


小结:从Oracle 和 Clickhouse 元数据表可以看出,Oracle 无法统计每个字段占用的大小,而Clickhouse 由于列存的原因,可以详细的统计每个字段大小。
方式二 查询时间
Oracle 通过查询不同字段的方式,进行验证,通过查看表T_OBJ的OBJECT_ID和OBJECT_NAME 字段,时间分别为1.17s和1.21s,相差不多,个人认为都是在通过全表扫描统计数据吧。
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
System altered.
Elapsed: 00:00:00.06
SQL> select count(OBJECT_ID) from (select OBJECT_ID from T_OBJ);
COUNT(OBJECT_ID)
----------------
11187328
Elapsed: 00:00:01.17
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
System altered.
Elapsed: 00:00:00.07
SQL> select count(OBJECT_NAME) from (select OBJECT_NAME from T_OBJ);
COUNT(OBJECT_NAME)
------------------
11187584
Elapsed: 00:00:01.21
Clickhouse 通过查询不同字段的方式,进行验证,查看表srv_log的message和business字段,时间分别为1.69s和0.3s差异较大。CK不仅显示了查询时间,更显示了查询数据量。

小结:通过进行实际表的不同字段查询,在Oracle和Ck的表现来看,Oracle 查询不同字段时间差别不大,CK查询不同字段查询时间相差较大,也从侧面验证了CK 是进行列存的。
方式三 查询数据量
Oracle 通过查询不同字段的方式,进行验证,查询表T_OBJ的字段OBJECT_ID和OBJECT_NAME,显示查询数据块数量一致,而且执行计划也显示在进行全表扫描。
SQL> set autot on
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
System altered.
Elapsed: 00:00:00.05
SQL> select count(OBJECT_ID) from (select OBJECT_ID from T_OBJ);
COUNT(OBJECT_ID)
----------------
11187328
Elapsed: 00:00:01.31
Execution Plan
----------------------------------------------------------
Plan hash value: 3371328957
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 | 1375 (1)| 00:00:17 |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
| 2 | TABLE ACCESS FULL| T_OBJ | 349K| 1707K| 1375 (1)| 00:00:17 |
----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
22 recursive calls
18 db block gets
159427 consistent gets
159405 physical reads <============== 物理读,数据块数
908 redo size
537 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> ALTER SYSTEM FLUSH BUFFER_CACHE;
System altered.
Elapsed: 00:00:00.05
SQL> select count(OBJECT_NAME) from (select OBJECT_NAME from T_OBJ);
COUNT(OBJECT_NAME)
------------------
11187584
Elapsed: 00:00:01.32
Execution Plan
----------------------------------------------------------
Plan hash value: 3371328957
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 25 | 1375 (1)| 00:00:17 |
| 1 | SORT AGGREGATE | | 1 | 25 | | |
| 2 | TABLE ACCESS FULL| T_OBJ | 349K| 8535K| 1375 (1)| 00:00:17 |
----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
22 recursive calls
15 db block gets
159426 consistent gets
159403 physical reads <============== 物理读,数据块数
876 redo size
539 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Clickhouse 通过查询不同字段的方式,进行验证,查询表SRV_LOG的字段message和business,显示查询数据量为26.G和8.9G,差异较大,而且都没有达到表的最大数据量70.4G,可以说明是按列查询。
注:user 条件基本可以查到表的所有数据。


小结:通过进行实际表的不同字段查询,在Oracle和Ck的表现来看,Oracle 查询数据量一样,CK查询相差较大,且都为达到表的数据量,也从侧面验证了CK 是进行列存的。
方式四 查询具体数据存储
Oracle 通过BBED或者dump的方式可以看到具体块存储的内容,下面通过dump方式查看,可以看到block_row_dump中,每行数据都在一起存储。
SQL> col OBJECT_NAME for a30
SQL> select object_name,dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid) from T_OBJ where rownum<10;
OBJECT_NAME DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
------------------------------ ------------------------------------ ------------------------------------
ICOL$ 4 3635
I_USER1 4 3635
CON$ 4 3635
UNDO$ 4 3635
C_COBJ# 4 3635
I_OBJ# 4 3635
PROXY_ROLE_DATA$ 4 3635
I_IND1 4 3635
I_CDEF2 4 3635
9 rows selected.
Elapsed: 00:00:00.00
SQL> alter system dump datafile 4 block 3635;
System altered.
--- first_ora_32571.trc
.......
block_row_dump:
tab 0, row 0, @0x1f33
tl: 77 fb: --H-FL-- lb: 0x0 cc: 14
col 0: [ 3] 53 59 53
col 1: [ 5] 49 43 4f 4c 24
col 2: *NULL*
col 3: [ 2] c1 15
col 4: [ 2] c1 03
col 5: [ 5] 54 41 42 4c 45
col 6: [ 7] 78 71 08 18 0c 26 24
col 7: [ 7] 78 71 08 18 0c 30 26
col 8: [19] 32 30 31 33 2d 30 38 2d 32 34 3a 31 31 3a 33 37 3a 33 35
col 9: [ 5] 56 41 4c 49 44
col 10: [ 1] 4e
col 11: [ 1] 4e
col 12: [ 1] 4e
col 13: [ 2] c1 02
tab 0, row 1, @0x1ee4
tl: 79 fb: --H-FL-- lb: 0x0 cc: 14
col 0: [ 3] 53 59 53
col 1: [ 7] 49 5f 55 53 45 52 31
col 2: *NULL*
col 3: [ 2] c1 2f
col 4: [ 2] c1 2f
col 5: [ 5] 49 4e 44 45 58
col 6: [ 7] 78 71 08 18 0c 26 24
col 7: [ 7] 78 71 08 18 0c 26 24
col 8: [19] 32 30 31 33 2d 30 38 2d 32 34 3a 31 31 3a 33 37 3a 33 35
col 9: [ 5] 56 41 4c 49 44
col 10: [ 1] 4e
col 11: [ 1] 4e
col 12: [ 1] 4e
col 13: [ 2] c1 05
tab 0, row 2, @0x1e98
tl: 76 fb: --H-FL-- lb: 0x0 cc: 14
Clickhouse 通过查看其存储文件的方式可以看到,每一列数据都有一个xxx.bin文件,也证明确实按列存储。
xxx.bin内容暂未学习到有何方法可以具体查看到。

小结:通过查看表数据的实际存储,在Oracle 按行存储,在数据块中可以看到每行数据紧密排列,Clickhouse 在存储文件的方式上已经把每个字段隔离开来。
总结
通过以上4种方式,对OLTP和OLAP 2种数据库系统的存储方式上进行验证学习。当然,2种类型的数据库发展这么长久,行存、列存的方式肯定是事实存在,验证并不是代表质疑,一种学习方式,更好的了解一款数据库。




