建库建表

创建数据库

创建数据库可以通过两种方式:

  • 通过 SQL 语句:
    CREATE DATABASE "dfs://test" PARTITIONED BY VALUE(1..10),ENGINE="TSDB",ATOMIC="TRANS",CHUNKGRANULARITY="TABLE"
    其中关键字不区分大小写。更多关于使用 SQL 语句创建数据库和表的内容,参考:create
  • 通过 database 函数:
    database("dfs://test", VALUE, 1..10,, "TSDB", "TRANS", "TABLE") 

上述两条语句都创建了一个名为 dfs://test 的分布式数据库,前缀 dfs:// 是分布式数据库的标识。

  • 指定了分区类型为值分区,并设置了 1 到 10 共 10 个初始分区。
  • 指定了存储引擎的类型为 TSDB。
  • 指定了写入事务的原子性层级为 TRANS,即不允许并发写入同一个分区。
  • 指定了分区粒度为 TABLE,即支持同时写入同一分区的不同表。

下面以 database 函数为例,对创建数据库进行详细介绍

database(directory, [partitionType], [partitionScheme], [locations], [engine=’OLAP’], [atomic=’TRANS’], [chunkGranularity=’TABLE’])

  • directory 用于指示数据库的存储目录。

    • 创建内存数据库,只需将 directory 设为空,可通过以下脚本实现:
      db = database("", VALUE, 1..10)
    • 创建分布式数据库,此参数需要以 dfs:// 开头,并指定一个唯一的库名,可通过以下脚本实现:
      db = database("dfs://uniquedb", VALUE, 1..10)
    注:
    • 采用单节点模式部署的 DolphinDB 也支持创建分布式数据库。
    • 数据库的库名一经确定,不可更改。
    • 数据库的库名必须具有唯一性,不允许与现有数据库同名。可通过 existsDatabasegetClusterDFSDatabases 在创建前查看是否存在同名数据库,也可在创建后验证是否创建成功。
  • partitionType 用于指示数据库的分区类型,partitionScheme 用于指示创建的分区结构,DolphinDB 提供了五种分区方式:

    • 值分区(VALUE),partitionScheme 指定一个向量,每个元素定义一个分区,例如常见的按天分区可以通过以下脚本实现:
      db = database("dfs://valuedb", VALUE, 2023.01.01..2023.12.31)
    • 范围分区(RANGE),partitionScheme 指定一个向量,相邻两个元素构成一个左闭右开的区间,每个区间定义一个分区,例如常用的按年分区可通过以下脚本实现:
      db = database("dfs://rangedb", RANGE, date(2020.01M+12*1..10)) 
    • 哈希分区(HASH),partitionScheme 指定一个长度为 2 的向量,第一个元素指定分区的数据类型,第二个元素指定分区数,例如常见的按照股票代码进行哈希分区可通过以下脚本实现:
      db = database("dfs://hashdb", HASH, [SYMBOL,25])
    • 列表分区(LIST),partitionScheme 指定一个元组,元组的每个元素是一个向量,定义一个分区,例如:
      db = database("dfs://listdb", LIST, [`A`B`C, `D`E])
    • 组合分区(COMPO),partitionScheme 指定一个向量,向量的每个元素是一个内存数据库的句柄,用于指示各级分区的分区类型,每级分区可以独立采用上述四种分区方式,最多为 3 级分区。组合分区在逻辑上是并列的,并不存在从属或优先级关系,例如常见的对按天分区和按照股票代码哈希分区进行组合分区,可以通过以下脚本实现:
      db1 = database("", VALUE, 2020.01.01..2021.01.01)
      db2 = database("", HASH, [SYMBOL,25])
      db = database("dfs://compodb", COMPO, [db1,db2])
    注:
    • 数据库的分区方式一经确定,不可更改。
    • 数据库的分区的概念是针对库的,即同一库下的所有表都是采用相同的分区方案。
    • 分布式表的值分区(VALUE)后续允许增加分区;分布式表的范围分区(RANGE)允许向后增加分区;其他方式的分区结构,即 partitionScheme 一经确定,不可更改。所以用户在设置分区结构时应充分考量,可参考:数据分区
  • engine 用于指定数据库采用的存储引擎,有 OLAP 存储引擎TSDB 存储引擎 可供选择,默认采用 OLAP。其中,TSDB 存储引擎仅在 200 及以上系列 server 中可用。

    OLAP 引擎查询和计算整列或多列的性能优越,数据压缩率高,但不适于存储列数较多的表(建议列数不超过 100)。TSDB 的点查性能更优,且兼具优秀的整列计算性能,综合考虑,推荐用户使用 TSDB 存储引擎。

    // 采用OLAP引擎
    db = database("dfs//olap", VALUE, 1..10,, "OLAP")
    
    // 采用TSDB引擎
    db = database("dfs://tsdb", VALUE, 1..10,, "TSDB)
    注:
    • 使用 TSDB 引擎创建数据库时,应检查配置文件,确保配置项 dataSync=1TSDBCacheEngineSize>0 。单节点的配置文件为 dolphindb.cfg,普通集群的配置文件为 cluster.cfg,高可用集群的配置项须通过 Web 界面修改。
    • 使用 TSDB 引擎创建数据库时,需要连接数据节点或计算节点创建,不能在控制节点创建。
    • TSDB 引擎只可应用于分布式数据库,不可用于内存数据库和本地磁盘数据库。
    • 一个数据库的存储引擎一经确定,后续不可修改。
  • atomic 用于设置写入事务的原子性层级,即,是否允许并发写入同一分区。可选值为 "TRANS" 和 "CHUNK",默认值为 "TRANS"。

    • 设置为 "TRANS" 时,写入事务的原子性层级为事务,即一个事务写入多个分区时,若某个分区被其他写入事务锁定而出现写入冲突,则该事务的写入全部失败。因此,该设置下,不允许并发写入同一个分区。
    • 设置为 "CHUNK" 时,写入事务的原子性层级为分区。若一个事务写入多个分区时,某分区被其它写入事务锁定而出现冲突,系统会完成其他分区的写入,同时对之前发生冲突的分区不断尝试写入,尝试数分钟后仍冲突才放弃。此设置下,允许并发写入同一个分区,但由于不能完全保证事务的原子性,可能出现部分分区写入成功而部分分区写入失败的情况。同时由于采用了重试机制,写入速度可能较慢。
  • chunkGranularity 用于指定分区的粒度。可选值为 "TABLE" 和 "DATABASE",默认值为 "TABLE"。

    • "TABLE":表级分区,设置后支持同时写入同一分区的不同表。
    • "DATABASE":数据库级分区,设置后只支持同时写入不同分区。
注:
  • 创建数据库前请确保用户已经登录。可通过函数 getCurrentSessionAndUser 查看,如果返回结果的第二个元素不是 guest,表示用户已经登录。默认的管理员 admin 的初始密码为 123456,用户可通过以下脚本登录:

    login(`admin,`123456)
  • 创建数据库只有具备 DB_OWNER 权限的用户才能执行。可通过 getUserAccess 查看当前用户是否具有该权限,如果没有,请联系管理员赋权。

创建表

创建内存表

常用的内存表有 tablekeyedTable, indexedTable, streamTable, mvccTable 和分区内存表 (createPartitionedTable)等,这里以 table 为例进行介绍。

table 有两种用法:

  • table(X, [X1], [X2], ...),把多个向量、元组、矩阵或表组合成一个表,向量名、元组名即为列名,集成表中的各列,

    • 每列的长度必须一致

    • 注意参数的类型只能是上述几种,尤其是在数据只有一行的情况,例如

      u = [2]
      v = [3]
      t = table(u, v)
    • 变量名是大小写敏感的,但是表的列名大小写不敏感,请注意建表时不能出现重复列名

    • 如果需要指定列名,可以在列后增加 as <alias> , 例如

      t = table([1,2,3] as id, ["x","y","z"] as sym)
    • 如果要创建存在空列的表,需要为其指定数据类型,如下例当中的 data 列

      t = table(`A as tag, now() as time, int(NULL) as data)
  • table(capacity:size, colNames, colTypes),其中 capacity 为建表时分配的内存(以记录数为单位),如果该参数设置很大,即使创建空表,也会占用大量内存。

    table(100:0, `col1`col2`col3`col4, [INT,INT,DOUBLE,STRING])
提示: 当内存表数据量较大时,可以创建分区内存表,它由多个子表构成,锁由每个子表独立管理,这样可以大大增加读写并发能力。

目前支持对内存表进行值分区、范围分区、哈希分区和列表分区,可使用 createPartitionedTable 创建分区内存表。

创建分区内存表与创建分布式分区表的方式相同,如下例所示:

db = database("",VALUE,1..5)
schemaTb = table(1:0, `id`sym`val, [INT,SYMBOL,DOUBLE])
t = db.createPartitionedTable(schemaTb, `mpt, `id)

有关更多参数细节,参考:分区表

创建分布式表

分布式表包括维度表和分区表两种类型。首先介绍一下维度表和分区表创建时的一些共有特点:

  • 创建分布式表时,需要为其指定一个表名。同一个数据库下,表名具有唯一性,不允许创建同名分布式表。可使用函数 existsTable 查看是否存在同名表,以及确认创建是否成功。

  • 创建分布式表时,需要一个表作为模板,创建一个跟该表结构一致的分布式表。该表可以是空表,也可以包含数据,使用非空表也不会将其中的数据写入分布式表,仅用于提供表结构。

  • 提供表结构的表不可是流数据表。

  • 如果是现有的分布式表或流数据表,想要复用其结构,可以通过 schema 函数查询其表的列名和列的数据类型,快速创建,也可查询少部分数据赋值给一个新的表变量作为模板。以流数据表为例:

    // st 为一流数据表,有以下两种方式快速复用其结构
    t1 = select top 3 * from st
    
    t2 = table(1:0,st.schema().colDefs.name,st.schema().colDefs.typeString)
    

维度表

维度表是分布式数据库中没有分区的表,一般用于存储不频繁更新的小数据集。可以使用 createTable 函数来创建维度表。

db = database("dfs://db1", VALUE, 1..5)
schemaTb = table(1:0,`id`sym`qty`price,[INT,SYMBOL,INT,DOUBLE])
t = db.createTable(schemaTb,`dt)

分区表

注: 只有具备 DBOBJ_CREATE 权限的用户或当前数据库创建者能够创建分区表。可通过 getUserAccess 查看当前用户是否具有创建分区表的权限,如果没有,请联系管理员赋权。

创建分区表的 createPartitionedTable 函数语法如下:

createPartitionedTable(dbHandle, table, tableName, partitionColumns, compressMethods, sortColumns, keepDuplicates=ALL, sortKeyMappingFunction)

  • partitionColumns 同分区内存表类似,创建分布式分区表时,需要指定分区列,分区列的数据类型必须与分区结构一致。分区列一经确定,无法修改。

    下例中,数据库根据日期值进行分区,建表时应指定一列数据类型为 DATE 或可以转化为 DATE 的列(DATETIME 类型数据可经 date 函数转换为 DATE 类型)

    db = database("dfs://valuedb", VALUE, 2023.01.01..2023.12.31)
    schemaTb = table(1:0,`date`time`sym`price,[DATE,TIME,SYMBOL,DOUBLE])
    t = db.createPartitionedTable(schemaTb,`pt,`date)

    下例中,数据库使用了由值分区和哈希分区组成的组合分区,分区列应指定两列,分别对应两级分区,且数据类型匹配:

    db1 = database("", VALUE, 2020.01.01..2021.01.01)
    db2 = database("", HASH, [SYMBOL,25])
    db = database("dfs://compodb", COMPO, [db1,db2])
    schemaTb = table(1:0,`date`time`sym`price,[DATE,TIME,SYMBOL,DOUBLE])
    t = db.createPartitionedTable(schemaTb,`pt,`date`sym)
  • sortColumns,当前数据库使用 TSDB 存储引擎时,则必须指定此参数。

  • sortColumns 如果只有一列,则数据会根据此列,在分区内进行分组;如果有n(n>1)列,则数据会根据前 n-1 列进行分组,这时在考虑是否使用参数 sortKeyMappingFunction 进行降维时,无需考虑最后一列。
  • keepDuplicates,如果指定当前去重策略为 LAST 和 FIRST,sortColumns 的数量不得小于 2。

  • TSDB 引擎可以根据 sortColumnskeepDuplicates 对数据进行去重,但 sortColumns 的设置不可过于随意,因为数据存储时会根据 sortColumns 创建索引,查询也可通过索引提升效率,如果索引设置不合理,不仅会因存储大量索引而造成磁盘占用过多,也会导致查询缓慢。