在业务开发前选择合适的表类型以提高开发效率和查询性能
数据库的存储模型直接影响数据处理的性能与效率,本文对当前DWS支持的表类型进行介绍,从存储格式进行分类,对各种类型的表的适用场景进行介绍,并提供了一些简单的使用示例,方便业务进行表选型和开发。
表的存储格式
从数据的存储结构上看,DWS支持两种存储格式:行存,列存。
需要注意的是,行列混存表本质上也是列存格式。
- 行存表:以数据行(Row)为最小物理存储单元,同一行的所有字段在物理上相邻连续存储(包括空值占位)。可以通过行标识符tid定位到一整行的数据。
行存表的数据物理存储示例:
1 2 3 4
| ID | Name | Age | Salary | Department | |-----|-------|-----|--------|------------| | 001 | 张三 | 30 | 15000 | 技术部 | ← 完整行数据连续存储 | 002 | 李四 | 28 | 12000 | 市场部 |
- 列存表:以数据列(Column)为最小物理存储单元,每列的数据连续存储,同列数据连续排列,不同列独立存储为不同的存储单元。
列存表的数据物理存储示例:
1 2 3
Column: ID → | 001 | 002 | 003 |... Column: Name → | 张三 | 李四 | 王五 |... Column: Age → | 30 | 28 | 35 |...
DWS支持的表类型
目前DWS支持的表类型有:行存表,列存2.0表,Hstore_opt表,存算分离表,行列混存表。
- DWS的系统表以及业务默认创建的表都是行存表。
- 列存2.0表,Hstore_opt表,存算分离表和行列混存表都属于列存表,这几种列存表都是以数据列存储为基础演化出来的,分别适用于不同的业务场景。
行存表介绍
行存表的一行数据可以通过一个位置指针(索引)一次找到,并且在读取时一次读取至少一整行数据。
基于以上特点,行存储的优势主要有两个方面:
- 点查性能好:在点查场景下可以直接索引到某行数据的元组位置;
- 更新效率高:行存储在实时并发入库,并发更新方面都比较有优势。
但是也有一些缺点:
- 存储时空间占用大:当前DWS采用多版本保存更新前后的数据,造成脏数据也占用存储空间;
- 读放大:读取时需要读取一整个页面和元组,如果是大宽表,但是又只读取一两列数据的情况下,读放大的问题比较严重。
对于传统的OLTP业务,数据需要进行频繁的增删改查的场景,比较适合使用行存表。
举例:
1 2 3 4 5 6 |
-- 默认类型,指定 ORIENTATION = ROW 或者默认即可。 CREATE TABLE table_row ( c1 INT, c2 TEXT ) WITH (ORIENTATION = ROW); |
列存表介绍
行形式存储的表如果涉及到分析查询场景,特别是在数据量大且复杂的查询时,就会遇到性能瓶颈,这时可以选择列存储。
列存储的优势主要有两方面:
- 批量查询性能好:当分析查询只涉及某列或者某几列,不需要访问无关列,特别是在表的宽度比较大时(如一千列),优势更加明显。
- 空间占用小:列存储支持压缩,而且由于单列数据类型相同,压缩性能更高。
在OLAP场景下,优选列存表,以下按不同使用场景分别介绍列存2.0表、Hstore_opt表、存算分离表(3.0列存表)、行列混存表。
列存2.0表
列存2.0表是最初的列存表,各列数据按一定数量组成一个存取单元(CU),压缩后写入一个物理文件中,通过辅助表控制以CU为单位的事务可见性。
列存2.0表目前在已上线的各种业务中广泛使用。
举例:
1 2 3 4 5 6 |
-- 指定 ORIENTATION = COLUMN 选项 CREATE TABLE table_col ( c1 INT, c2 TEXT ) WITH (ORIENTATION = COLUMN); |
Hstore_opt表
列存2.0表虽然在海量复杂查询方面相比行存表有优势,但是也存在比较明显的短板:由于事务可见性基于CU,所以基本无法支持并发更新入库。
随着业务复杂程度的提升和实时入库+查询诉求越来越多,Hstore_opt表应运而生。Hstore_opt表融合行存和列存的优势,在列存2.0的基础上业务入库并发进行了优化:使用一个行存表存储实时的增删改操作信息,然后通过后台线程将行存表数据merge到CU中。
Hstore_opt表解决了列存2.0表的以CU为单位进行并发的问题,支持上游增删改操作实时并发入库,数据merge到CU后又保证了数据的压缩能力和分析能力。
由于近些年实时业务的需求越来越多,Hstore_opt表已逐步取代列存2.0表,成为首选的列存表类型。
举例:
1 2 3 4 5 6 |
-- 在列存的基础上指定 ENABLE_HSTORE_OPT = TRUE 选项 CREATE TABLE table_hstore ( c1 INT, c2 TEXT ) WITH (ORIENTATION = COLUMN, ENABLE_HSTORE_OPT = TRUE); |
存算分离表
Hstore_opt表解决了实时业务的问题,但是由于列存表的AppendOnly属性,实时业务中大量的并发插入或更新会导致CU文件快速膨胀,而EVS存储价格不低,所以用户成本很高。
存算分离表在Hstore_opt表的基础上将CU文件存储到OBS(华为分布式对象存储,一种高可靠、低成本的海量持久化存储产品)中,搭配本地EVS盘的diskcache缓存能力,既可以尽可能地保证读取和写入的性能,又能有效降低用户成本。同时依托OBS的分布式存储能力,可以做到存储无线扩展,计算能力快速扩缩。
对于比较重视存储成本以及弹性能力的业务,可以选择存算分离表。
举例:
1 2 3 4 5 6 7 |
-- 需要选择存算分离集群 -- 在hstore表的基础上增加 COLVERSION = 3.0 选项 CREATE TABLE table_v3 ( c1 INT, c2 TEXT ) WITH (ORIENTATION = COLUMN, ENABLE_HSTORE_OPT = TRUE, COLVERSION = 3.0); |
行列混存表
列存2.0、Hstore_opt表、存算分离表虽然在批量查询、实时入库、空间管理方面都做了各种优化,但还是无法做到行存表的点查性能。行列混存表则用空间换时间,将行列格式混合存储,通过占用更多的空间来换取点查性能的提升。
行列混存表在一张表中同时存储行格式数据和列格式数据,两种格式各自独立维护、同步更新,由查询优化器根据实际查询路径选择最优访问方式,通过列存固有的过滤优化能力实现更智能的I/O路径控制,自动选择访问行/列格式数据。确保提供列存储高压缩、高吞吐优势的同时,保留行存储对局部数据的灵活访问能力。
行列混存表适用于混合查询场景下的高效数据调度。行列混存表可以显著降低整体I/O开销,同时提升数据命中率与查询性能。
举例:
1 2 3 4 5 6 |
-- 在hstore表的基础上增加 STORAGE_MODE = MIX 选项 CREATE TABLE table_mix ( c1 INT, c2 TEXT ) WITH (ORIENTATION = COLUMN, ENABLE_HSTORE_OPT = TRUE, STORAGE_MODE = MIX); |
表类型选择建议
行存储和列存储各有优势,没有绝对的优劣之分。行存储在事务处理方面表现出色,而列存储在大数据分析领域优势明显。随着技术的发展,二者的界限正逐渐模糊,现代数据库系统越来越多地采用混合策略来兼顾不同场景的需求。数据库选型时,应根据具体的应用场景、查询模式和性能要求来选择合适的存储模型。
|
表类型 |
行存表 |
列存2.0 |
Hstore表 |
存算分离表 |
行列混存表 |
|---|---|---|---|---|---|
|
数据存储方式 |
以元组为单位,将每一条数据的所有属性值存储到临近的空间里。 |
以CU(Compress Unit)为单位,将单个属性的所有值存储到临近的空间里。 |
数据主要以CU形式存储在列存主表上,对于被更新的列、小批量插入的数据将被序列化后存储到新设计的Delta表上。 |
类似hstore表,CU文件写入到OBS中。 |
存储行格式数据和列格式数据。 |
|
数据写入 |
没有压缩能力,数据按原始状态存储,磁盘空间占用较大。 |
支持压缩,同一列属性值类型相同,压缩效果好。数据写入时能极大节省IO资源与磁盘空间占用。 |
批量插入的数据直接写入CU,具有与列存一致的压缩优势。被更新的列、小批量插入的数据会序列化后压缩。同时定期merge到主表CU。 |
类似hstore表,CU数据异步写入OBS。 |
类似hstore,数据按行/列格式同时存入CU,占用空间变大,但得益于压缩能力,空间占用比行存表小。 |
|
数据更新 |
数据按行更新,没有CU锁问题,并发更新(update/upsert/delete等)性能好。 |
即使更新单条数据,也要获取整个CU的锁,基本无法支持并发更新(update/upsert/delete等)。 |
彻底解决列存更新的CU锁问题,并发更新(update/upsert/delete等)的性能达到行存的60%以上。 |
OBS相较本地盘读写速度较慢,通过异步写方式加快写入速度。 |
并发更新能力与hstore表相似。 |
|
数据读取 |
按行读取,即使只需访问某一列的数据,也需要将一整行的数据取出。非点查性能较差。 |
按列读取时只需访问该列的CU,再加上CU的压缩优势导致需要占用的IO资源更少,单列读取性能很好。 |
对于列存主表的数据按列读取,对于被更新的列、小批量插入的数据会反序列化后取出,数据merge到主表后具有与列存一致的数据读取优势。 |
通过本地盘diskcache能力加速读取。 |
由查询优化器根据实际查询路径选择最优访问方式,通过列存固有的过滤优化能力实现更智能的I/O路径控制,自动选择访问行/列格式数据 |
|
优点 |
并发更新性能好,点查能力强。 |
查询性能好,磁盘占用空间少。 |
并发更新性能好,数据MERGE后具有与列存一致的查询性能优势与压缩优势。 |
扩缩能力强,存储成本低。 |
兼顾点查性能和空间压缩能力,比行存表磁盘占用小。 |
|
缺点 |
占用磁盘空间多,查询性能差。 |
基本无法支持并发更新。 |
需要后台常驻线程对HStore表进行merge清理操作。先merge到CU主表再进行清理,与SQL语法中的Merge无关。 |
OBS相较本地盘读写速度较慢。 |
相比列存表和hstore表,磁盘占用较高。 |
|
适用场景 |
更新删除操作频繁的TP事务场景。点查询(基于索引的、返回数据量小的简单查询)。 |
查询分析为主的AP场景。数据量大,存入后的更新删除操作少。 |
实时并发入库场景。需要支持高并发的更新入库操作以及高性能的查询效率。 |
对实时扩展性要求较高或者存储成本敏感的场景。 |
即要求实时入库和大数据查询分析,又要兼顾点查性能的业务。 |