时序数据库分区教程(一)介绍了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表的一个分区字段是日期,从而确保所有加载数据的作业不会产生有重叠的事务。