Doris的建表规范小结(三)


前两篇文章带大家解读了Doris这个数据库的基本架构、集群部署,以及过程中遇到的一些值得注意的坑。


既然作为一个数据库,那么它的核心功能自然就是用来存储数据,而存储数据,自然就需要建各种各样的表,那么这篇文章咱就来聊聊Doris如何创建表的问题。



0. 瞧一眼数据目录


我们知道,任何存储软件,它最终的数据一定是要写到磁盘上的,那我们就先来一眼各个be节点(slaver节点)的数据目录情况,千万不要小瞧这个步骤,熟悉这里,对以后你深入理解doris的工作原理大有裨益。


我这个测试集群目前有6个节点,其中3个fe节点(master),3个be节点(slaver),目前为止,我的集群一张用户表都没有建,一条用户数据都没有,先来瞅瞅没有数据情况下的数据目录长啥样:


第1台be的数据存储目录



第2台be的数据存储目录



第3台be的数据存储目录


可以看到,虽然1条数据都没有,但是配置的这个存储数据的目录依然不为空,里面有一些be服务启动过程中的日志,当然这个日志跟配置的/var/log/doris用来记录be服务运行的日志肯定是不一样的,大致看了一眼,这里记录的应该是为数据写入前做的一些准备工作。


从MySQL的查询客户端也可以看到,当前数据库的数据存储情况:





1.  Doris的表引擎


说起一个数据库的建表,那就不得不先说说他的表引擎,之前聊过clickhouse我们知道,CK的表引擎可以根据场景和功能分为4大类,然后每个大类又分为多个子类,很容易让初次接触的人一脸懵逼。


但是Doris在这方面好像弱化了很多,或者说它的很多对数据处理的功能,并不是通过定义各种表引擎来实现的。


Doris的表引擎只有两大类:一个是Doris本身管理的OLAP引擎,另一个则是对接其他数据源的外部表引擎。而这块的描述,官网就只用了一句话:


因此,Doris对数据的所有强大处理能力,都是围绕这个OLAP引擎来展开,所以我们建表时,建表语句可以不像CK那样需要显示的指定当前的表引擎,这个表引擎的指定可以省略。



2. 表字段类型的划分


Doris有个有意思的设计就是,它会把我们创建表时候需要用到的字段,给分成两大类:维度列和指标列,其中维度列也叫这张表的key,而指标列也被叫做这张表的value。这个跟我们平时用的普通数据库建表方式是很不一样的。


所谓维度列:就是原始数据写进来之后,值不会被改变的列;


而所谓指标列:就是原始数据写进来之后,如果维度列相同,那么指标列的值就会被改变,比如相加,取最小值、最大值等等


既然维度列的值会一直保持不变,而指标列的值会随着维度列的相同而得到运算,那就可以形象的理解前者为key,后者为value,对吧。


为啥要这么设计呢?


要知道,Doris定位的是一个OLAP数据库,它最大的功能就是对数据做分析,根据业务要求对数据做各种聚合,传统数据库的玩法是原始数据写进去之后,再根据聚合条件,进行各种指标的聚合计算。


但是这样的话,我们知道,随着数据量的不断增加,聚合的代价就一定会越来越高,因为聚合这个“变态”的操作,它不像where筛选,可以通过索引的手段提高效率,它只能老老实实把你需要的结果给收集到一起,然后挨个运算,这个代价跟你的数据量呈指数级相关。


那照这么说,是不是只要涉及到聚合的场景,就只能接受这种越来越慢的局面呢?


肯定不是,我们完全可以把这个“查询时聚合”现象,给扭转为“写入时聚合”,这样一来,根据我们的业务目标,在原始数据写入到目标表的时候,所呈现的结果就是我们业务需要的。


这样一来,当我们只需要用普通的查询条件,就可以得到指标聚合的结果。


而这,就是Doris对表字段进行如此设计的核心原因(我理解的)。



3. 表模型


Doris除了提出上述对表字段的定义外,还提出了一个表模型的概念,官网目前给出了3种支持的表模型:


从这个名字我估计你应该能大概猜出来,Doris希望所有写进其表中的数据,都尽可能是根据业务聚合之后的(Aggregate是一种典型的聚合,而Unique则是一种特殊的聚合)。


但是,如果我就只想写入原始数据,不想做任何的“聚集”操作,Doris也提供了Duplicate的存储模型(也是默认的表模型),让你的数据回归原始状态。


这就让我想起了CK的几种表引擎:


这里的Aggregate表模型,类似CK的AggregatingMergeTree引擎(严格来说,更像SummeringMergeTree引擎);


Unique表模型,对应CK的ReplacingMergeTree引擎;


Duplicate表模型,则可以对应CK的普通MergeTree引擎。


只不过说法不一样而已。


但是,原本以为这个doris的Aggregate表模型可以跟CK的AggregatingMergeTree引擎PK一番,但是发现我错了,跟CK的一比,这个Aggregate表模型简直弱爆了


目前为止,它只支持如下几种聚合方式:


关键它这个replace聚合方式还跟后面的Unique模型重复了,所以说Doris的这个Aggregate模型跟CK的聚合引擎比起来,有点名不符实。


也就说,如果对于一些复杂的聚合场景,想指望用Doris的表模型来直接解决问题,目前来看是不可能的


在使用时,通过表模型关键字,然后紧接着需要的排序字段(key字段,也叫维度字段)来指定:



而对于Doris的Unique模型来说呢,就更简单一点,是用来根据维度字段(key字段)去重的种数据组织形式,这个跟CK的ReplacingMergeTree功能是一样的,关键在doris这里,这个模型的功能还可以被第一个Aggregate模型给代替(详见官网)。



而对于Duplicate表模型来说,则更加简单,它就是一张没有任何聚合功能,但是依然提供索引功能(排序)的普通表。




4. 建表语句的特点


Doris建表语句的语法也有其它的特点,跟CK很像,但是又有些区别,我们先来看一个典型的建表语句:


1. 这个建表语句在字段定义上,doris给分成了两大类,其中,key字段/维度字段,需要在确定的数据模型后面指定(也就是KEY关键字后面);


2. key字段/维度字段确定之后,剩余的就都是value字段/指标字段了,这类字段除了要指定其类型外,还需要比普通数据库多一个聚合类型的指定(sum、min、max、replace),当然,这个聚合类型只针对Aggregate表模型,其他的两个Unique和Duplicate是不需要的;


3. 表引擎:这里默认就是OLAP引擎,可以不用写,代表当前的表数据是明确交给Doris来全权管理的,即非外部数据源表;


4. 表模型的选择(必选,默认为DUPLICATE):不管是Aggregate还是Unique,或者是Duplicate,后面都必须要跟一个KEY关键字,一来确定当前表的哪些字段是属于key字段/维度字段,二来也是确定当前表的索引字段(排序字段);


5. 分区指定(可选):对于Doris来说,这个分区跟hive以及CK都不太一样的地方在于:官网给的例子好像只支持固定分区(静态分区)方式,而不支持动态分区。意思是这个分区的安排,哪些数据在哪个分区是需要你在建表时提前指定的,无法根据写入数据的值来自动判断。


PS:后来我通过找文档的其他地方发现,Doris在较高版本也支持了动态分区,但是好像只能支持基于时间(天、周、月、时)的分区方式,有待进一步验证。



6. 分桶指定(必选):跟hive一样,是为了把数据切的更细,分布的更加均匀,是在分区基础之上的又一次重新数据组织,只不过hive的分桶必须指定桶的数量,而doris的分桶,除了可以静态指定桶数量外,新版本还可以通过设定单个桶的最大容量,来动态指定:


而且这个分桶在建表时,必须指定,即便你指定了分区也不行,否则就会报如下的错误:



7. 表属性的指定(可选):用来指定当前表的一些其他属性,比如当前表数据的副本数量,这一点比CK方便(CK需要创建专门的副本引擎表),另外,如配置所示,Doris可以在建表语句层面,通过指定热数据的保存时间,来实现数据的冷热分离。



5.  写个数据试试


当我准备把手头的dns日志数据写入到一张doris表的时候,发现坑来了。


我根据我的数据源特点进行准备建一张最普通的表(Duplicate表模型),建表语句如下,但结果却报错了:


这个意思是说:不能将string类型的字段作为KEY字段


为啥呀?可是CK也有类似的建表方式,人家就可以用string类型的字段排序哦。


摸索一番之后发现,这个doris的第一个字段类型,不能是:double、float、string,或者array


即便是你的建表语句不指定key、不建索引也不行(因为doris会默认还是会给你创建),就是这么傲娇:



那为了满足这个个人认为不太合理的要求,只能把原本的string类型根据提示给改成了varchar,这才创建表成功:


另外值得一提的是,这个KEY指定的字段顺序,必须要跟上面罗列的字段顺序一致,否则也会报错。


然后我就根据官网提供的curl方式,把一个1.2G的数据文件给导入到这张表中:




6. 检查数据


大概1分钟左右,近9百万条数据就写入到刚才那张表中了,现在,咱来检查一下数据的写入情况。


可以看到,数据确实是已经全部写进来了,然后我们再看一眼数据量的大小:

这里面的ReplicaCount的数量为15,可以这么来解读:分桶数为5个,然后这张表默认的数据副本数量为3,因此5*3=15.


至于这个数据写入数据库之后占用的大小,这个1.036G需要再除以3(因为有3个副本),也就是大概300M+的样子,而原始数据大小为1.2G,所以Doris默认的数据压缩率大概为3/4


再来瞅一眼数据目录,doris的数据目录跟CK的不一样,CK的数据目录直接可以看到新建的库名,以及表名,但是doris则啥都没有,只能看到数据目录下,其data子目录有明显的大小变化:


这0,1,2,3,4目录代表了当前表的5个分桶,但是表名,库名之类的则啥都看不到,想搞点小破坏,从数据文件下手,去删除具体表的数据应该是没戏了,因为你根本就找不到。


来个简单的小聚合,看下效率怎么样:


对于近900万的数据量,这样一个聚合效率还行,但是这其实说明不了什么问题,等后续我把数据灌入到10亿级别再看看。



7. 最后


今天主要围绕了Doris的建表特点,聊了下它在建表时候需要注意的一些地方,以及跟CK的比较,有哪些不同之处。


虽然说官方文档其实已经写的很仔细了,但是我觉得还是有必要经过我的思考之后,给你再次解读一番,毕竟官方的内容还是过于“官方”,而我的解读则可能更符合你的理解,更接地气。


你觉得呢?



你可以添加我的私人微信,拉你入技术讨论群,跟一群热爱技术的小伙伴一起成长...


请使用浏览器的分享功能分享到微信等