关于dolphindb:干货丨时序数据库分区教程二

52次阅读

共计 5837 个字符,预计需要花费 15 分钟才能阅读完成。

时序数据库分区教程(一)介绍了 DolphinDB Database 的几种分区形式,本文将会具体解说 DolphinDB 的分区准则、非凡的分区计划,让用户对 DolphinDB 分区数据库有更深刻的理解。

1. 分区准则

分区的总准则是让数据管理更加高效,进步查问和计算的性能,达到低延时和高吞吐量。

1.1 抉择适合的分区字段

DolphinDB 分区字段的数据类型能够是整型、日期类型和 SYMBOL 类型。留神,STRING、FLOAT 和 DOUBLE 数据类型不能够作为分区字段。

尽管 DolphinDB 反对 TIME、SECOND、DATETIME 类型字段的分区,然而在理论应用中要审慎应用,防止采纳值分区,免得分区粒度过细,将大量工夫消耗在创立或查问几百上千万个只蕴含几条记录的文件目录。

分区字段该当是在业务中相当重要的。例如在证券交易畛域,许多工作都与股票交易日期或股票代码相干,因而应用这两个字段来分区比拟正当。

1.2 分区粒度不要过大

DolphinDB 单个分区反对最大记录条数是 20 亿条。但正当的记录条数应该远远小于这个数。一个分区内的多个列以文件模式独立存储在磁盘上,通常数据是通过压缩的。应用的时候,零碎从磁盘读取所须要的列,解压后加载到内存。如果分区粒度过大,可能会造成多个工作线程并行时内存不足,或者导致系统频繁地在磁盘和工作内存之间切换,影响性能。一个教训公式是,数据节点的可用内存是 S,工作线程(worker)的的数量是 W,则倡议每个分区解压后在内存中的大小不超过 S /8W。假如工作内存下限 32GB,8 工作线程,倡议单个分区解压后的大小不超过 512MB。

DolphinDB 的子工作以分区为单位。因而分区粒度过大会造成无奈无效利用多节点多分区的劣势,将原本能够并行计算的工作转化成了程序计算工作。

DolphinDB 是为 OLAP 的场景优化设计的,反对增加数据,不反对对个别行进行删除或更新。如果要批改数据,以分区为单位笼罩全副数据。如果分区过大,升高效率。DolphinDB 在节点之间复制正本数据时,同样以分区为单位,分区过大,不利于数据在节点之间的复制。

综上各种因素,倡议一个分区未压缩前的原始数据大小管制在 100M~1G 之间。当然这个数字可结合实际状况调整。譬如在大数据利用中,咱们常常看到宽表设计,一个表白到几百个字段,然而在单个利用中只会应用一部分字段。这种状况下,能够适当放大下限的范畴。

如果发现分区粒度过大,能够采纳几种办法,(1)采纳组合分区(COMPO),(2)减少分区个数,(3)将范畴分区改为值分区。

1.3 分区粒度不要过小

分区粒度过小,一个查问和计算作业往往会生成大量的子工作,这会减少数据节点和管制节点,以及管制节点之间的通信和调度老本。分区粒度过小,也会造成很多低效的磁盘拜访(小文件读写),造成零碎负荷过重。另外,所有的分区的元数据都会驻留在管制节点的内存中。分区粒度过小,分区数过多,可能会导致管制节点内存不足。咱们倡议每个分区未压缩前的数据量不要小于 100M。

譬如股票的高频交易数据若按交易日期和股票代码的值做组合分区,会导致许多极小的分区,因为许多交易不沉闷的股票的交易数据量太少。如果将股票代码的维度依照范畴分区的办法来切分数据,将多个交易不沉闷的股票组合在一个分区内,则能够无效解决分区粒度过小的问题,进步零碎的性能。

2. 如何把数据平均分区

当各个分区的数据量差别很大时,会造成零碎负荷不平衡,局部节点工作过重,而其余节点处于闲置期待状态。当一个工作有多个子工作时,只有最初一个子工作实现了,才会把后果返回给用户。因为一个子工作对应一个分区,如果数据分布不平均,可能会增大作业延时,影响用户体验。

为了不便依据数据的散布进行分区,DolphinDB 提供了一个十分有用的函数 cutPoints(X, N, [freq])。X 是一个数据,N 示意产生的分组数,freq 是与 X 等长的数组,每个元素对应 X 中元素呈现的频率。这个函数返回具备 (N+1) 个元素的数组,使得 X 中的数据平均地散布在 N 个组中。

上面的例子中,须要对股票的报价数据按日期和股票代码两个维度做数据分区。如果简略的按股票的首字母进行范畴分区,极易造成数据分布不均,因为极少量的股票代码以 U, V, X,Y,Z 等字母结尾。倡议应用 cutPoints 函数依据样本数据来划分分区。

1. //2007.08.01 这天数据导入
2. t = ploadText(WORK_DIR+"/TAQ20070801.csv")
4. // 抉择 2007.08.01 这天数据的股票代码的散布来计算分组规定
5. t=select count(*) as ct from t where date=2007.08.01 group by symbol
7. // 依照股票代码字母程序产生 128 个区间。每个区间外部的数据行数在 2007.08.01 这天是相当的。8. buckets = cutPoints(t.symbol, 128, t.ct)
10. // 最初一个区间的完结边界由 2007.08.01 的数据决定。为排除 2007.08.01 之后之后有新的将最初一个区间的完结边界替换成不会呈现的最大的股票代码。11. buckets[size(buckets)-1] = `ZZZZZ
13. //buckets 的后果如下:14. //["A",'ABA','ACEC','ADP','AFN','AII','ALTU','AMK',..., 'XEL','XLG','XLPRACL','XOMA','ZZZZZ']
16. dateDomain = database("", VALUE, 2017.07.01..2018.06.30)
17. symDomain = database("", RANGE, buckets)
18. stockDB = database("dfs://stockDBTest", COMPO, [dateDomain, symDomain])

除了应用范畴分区的办法,列表分区也是解决数据分布不平均的无效办法。

3. 时序类型分区

工夫是理论数据中最常见的一个维度。DolphinDB 提供了丰盛工夫类型以满足用户的需要。当咱们以工夫类型字段作为分区字段时,在工夫取值上须要预留足够的空间以包容未来的数据。上面的例子,咱们创立一个数据库,以天为单位,将 2000.01.01 到 2030.01.01 的日期分区。留神,只有当理论数据写入数据库时,数据库才会真正创立须要的分区。

dateDB = database("dfs://testDate", VALUE, 2000.01.01 .. 2030.01.01)

DolphinDB 应用工夫类型作为分区字段时,还有一个非凡的长处。数据库定义的分区字段类型和数据表理论采纳的工夫类型能够不统一,只有保障定义的分区字段数据类型精度小于等于理论数据类型即可。比如说,如果数据库是按月(month)分区,数据表的字段能够是 month, date, datetime, timestamp 和 nanotimestamp。零碎主动会作数据类型的转换。

4. 不同表雷同分区的数据寄存于同一节点

在分布式数据库中,如果多个分区的数据表要连贯(join)通常非常耗时,因为波及到的分区可能在不同的节点上,须要在不同节点之间复制数据。为解决这个问题,DolphinDB 推出了共存储地位的分区机制。DolphinDB 确保同一个分布式数据库里所有表在雷同分区的数据存储在雷同的节点上。这样的安顿,保障了这些表在连贯的时候十分高效。DolphinDB 以后版本对采纳不同分区机制的多个分区表不提供连接功能。

1. dateDomain = database("", VALUE, 2018.05.01..2018.07.01)
2. symDomain = database("", RANGE, string('A'..'Z') join `ZZZZZ)
3. stockDB = database("dfs://stockDB", COMPO, [dateDomain, symDomain])
5. quoteSchema = table(10:0, `sym`date`time`bid`bidSize`ask`askSize, [SYMBOL,DATE,TIME,DOUBLE,INT,DOUBLE,INT])
6. stockDB.createPartitionedTable(quoteSchema, "quotes", `date`sym)
8. tradeSchema = table(10:0, `sym`date`time`price`vol, [SYMBOL,DATE,TIME,DOUBLE,INT])
9. stockDB.createPartitionedTable(tradeSchema, "trades", `date`sym)

下面的例子中,quotes 和 trades 两个分区表采纳同一个分区机制。

DolphinDB 是为 OLAP 设计的零碎,次要是解决海量结构化数据的疾速存储和计算,以及通过内存数据库和流数据实现高性能的数据处理。DolphinDB 不适宜数据频繁更改的 OLTP 业务零碎。DolphinDB 的数据写入与 Hadoop HDFS 相似,疾速在每个分区或文件的开端批量插入数据。插入的数据会压缩存储到磁盘,个别压缩比例在 20%~25%。数据一旦追加到基于磁盘的数据表后,不能疾速更新或删除某些符合条件的记录,必须以分区为单位对数据表进行批改。这也是分区准则中提到单个分区不宜过大的起因之一。

5. 多正本机制

DolphinDB 容许为每一个分区保留多个正本,默认的正本个数是 2,能够批改管制节点的参数 dfsReplicationFactor 来设置正本数量。

设置冗余数据的目标有两个:(1)当某个数据节点生效或者或磁盘数据损坏时,零碎提供容错性能持续提供服务;(2)当大量并发用户拜访时,多正本提供负载平衡的性能,进步零碎吞吐量,升高拜访延时。

DolphinDB 通过两阶段事务提交机制,确保数据写入时,同一正本在多节点之间的数据强一致性。

在管制节点的参数文件 controller.cfg 中,还有一个十分重要的参数 dfsReplicaReliabilityLevel。该参数决定是否容许多个正本驻留在同一台物理服务器的多个数据节点上。在 development 阶段,容许在一个机器上配置多个节点,同时容许多个正本驻留在同一台物理服务器(dfsReplicaReliabilityLevel=0),然而 production 阶段须要设置成为 1,否则起不到容错备份的作用。

1. // 每个表分区或文件块的正本数量。默认值是 2。2. dfsReplicationFactor=2
4. // 多个正本是否能够驻留在同一台物理服务器上。Level 0:容许; Level 1:不运行。默认值是 0。5. dfsReplicaReliabilityLevel=0

6. 事务机制

DolphinDB 对基于磁盘(分布式文件系统)的数据库表的读写反对事务,也就是说确保事务的原子性,一致性,隔离性和长久化。DolphinDB 采纳多版本机制实现快照级别的隔离。在这种隔离机制下,数据的读操作和写操作相互不阻塞,能够最大水平优化数据仓库读的性能。

为了最大程序优化数据仓库查问、剖析、计算的性能,DolphinDB 对事务作了一些限度:

首先,一个事务只能蕴含写或者读,不能同时进行写和读。

其次,一个写事务能够逾越多个分区,然而同一个分区不能被多个 writer 并发写入。也就是说当一个分区被某一个事务 A 锁定了,另一个事务 B 试图再次去锁定这个分区时,零碎立即会抛出异样导致事务 B 失败回滚。

7. 多 Writer 并行写入

DolphinDB 提供了弱小的分区机制,单个数据表能够反对几百万的分区数量,这为高性能的并行数据加载发明了条件。特地是当将海量的数据从别的零碎导入到 DolphinDB 时,或者须要将实时数据以准实时的形式写入到数据仓库时,并行加载显得尤为重要。

上面的例子将股票报价数据(quotes)并行加载到数据库 stockDB。stockDB 以日期和股票代码做复合分区。数据存储在 csv 文件中,每个文件保留一天的 quotes 数据。

1. // 创立数据库和数据表
2. dateDomain = database("", VALUE, 2018.05.01..2018.07.01)
3. symDomain = database("", RANGE, string('A'..'Z') join `ZZZZZ)
4. stockDB = database("dfs://stockDB", COMPO, [dateDomain, symDomain])
5. quoteSchema = table(10:0, `sym`date`time`bid`bidSize`ask`askSize, [SYMBOL,DATE,TIME,DOUBLE,INT,DOUBLE,INT])
6. stockDB.createPartitionedTable(quoteSchema, "quotes", `date`sym)
8. def loadJob(){
9. fileDir='/stockData'
11. // 到门路下取出数据文件名
12. filenames = exec filename from files(fileDir)
14. // 加载数据库
15. db = database("dfs://stockDB")
17. // 对每个文件,通过文件名产生 jobId 前缀。18. // 通过函数 submitJob 提交后台程序调用 loadTextEx 将数据加载到 stockDB 数据库中。19. for(fname in filenames){20. jobId = fname.strReplace(".txt", "")
21. submitJob(jobId,, loadTextEx{db, "quotes", `date`sym, fileDir+'/'+fname})
22. }
23. }
25. // 通过 pnodeRun 将 loadJob 这个工作发送到集群的每个数据节点进行并行加载。26. pnodeRun(loadJob)

当多个 writer 并行加载数据时,要确保这些 writer 不会同时往同一个分区写入数据,否则会导致事务失败。在下面的例子中,每一个文件存储了一天的数据,而 quotes 表的一个分区字段是日期,从而确保所有加载数据的作业不会产生有重叠的事务。

正文完
 0