时序数据库 .NET 操作 QuestDb

缺点

用户使用下来都夸性能强大,虽然性能强大,但是使用下来有几个小问题

1、不支持删操作 (只能truncatetable 或者删除分区)

2、程序里面尽量不要修改表结构和truncate drop操作这样容易锁表(右边菜单有解锁方案)

表建好了就尽量不要去改他了,CodeFirst只执行一次就行了  

修改表结构后最好重启服务,不然会出现LOCK TABLE问题,更多解决方案 看右边菜单【常见错误】

QuestDb 数据库

QuestDb 性能最强的时序库,适合物联网、金融大数据,数据日志等 ,也是2021年Github增速最快的数据库

image.png


适合历史数据和数据聚合统计 比如金融软件,支持Window和Linux

在线测试

测试网址 https://demo.questdb.io/

//测试用例1: LIKE 1亿多取一条 用了1秒
select * from pos where id ~ 'OQHFDQTJTJJQEEEZ' //Like

//测试用例2:按天分表 所有数据进行汇总 接近2亿数据汇总 用了9秒左右
select  sum(price),side from 'trades' where price>100 group by side

//测试用例3:按天分表 所有数据进行汇总 接近2亿进行Count 用了2秒 (关系型数据库要20分钟以上)
select count(1) from 'trades'

一、安装SqlSugar

SqlSugarCore

二、连接字符串

host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;
//数据库固定的

三、代码

 DbType = DbType.QuestDB

四、源码DEMO

image.png

五、Symbol类型

注意:symbol只能存储重复率很高值 ,不能是那种重复率不高的值,重复率不高的不能用该类型,插入会很慢

symbol类型去重后要小于6万

   //索引特性可以多个字段
   [SugarIndex(null, nameof(IndexClass.Name), OrderByType.Asc)]
    public class IndexClassTest
    {
        public int Id { get; set; }

        //只能是string类型并且 datatype=symbol 其它类型不需要索引
        //symbol只能存储重复率很高值
        [SugarColumn(ColumnDataType = "symbol")]
        public string Name { get; set; }
    }
   //创建表
   db.CodeFirst.InitTables<IndexClassTest>();

六、GroupBy

和正常的GroupBy用法一样,在SqlSugar中用法没有区别

 var list= db.Queryable<Order>()
                .GroupBy(x => x.Name)
                .Select(i => new
                {
                    name = i.Name,
                    count = SqlFunc.AggregateCount(i.Name)
                })
                .ToList();

七、独有:创建分表

数据不超过30亿不需要分表30亿内分表意义不大,分表已知问题存在下面几个

1. 插入后不能立马查询出来随机性的,不分表正常,不清楚是否有什么配置

2. 出现过1个客户测试电脑环境经常断电出现过一次数据丢失,不清楚系统问题还是数据库问题(其他时序库也有这种情况)

3. 1个客户出现分表并且表的字段超级多 出现了性能问题 ,时间比较久了也可能是官方修复了

如果数据超过十亿或者更多,我们需分表存储性能更佳

//无需自动分表特性 只需要在分表字段上加上 TimeDbSplitField
public class SplitTableEntity
 {
        public string Id { get; set; }
        public string name{get;set;}
        [TimeDbSplitField(DateType.Day)]//按天分表 
        public DateTime Ts { get; set; }//只能是时间类型一个字段
}
    
//创建表
 db.CodeFirst.InitTables<IndexClassTest>();

创建完带有Day标识的表说明你创建成功了和正常表一样使用,存储时是按时间分开存储的全自动的

image.png

八、独有:高级时间统计 SampleBy

请升级到:5.1.4.86-preview07

//用例1:根据1天进行分组统计,比报表查询要好用很多
var list=db.Queryable<UnitSiafayyy>()
               .SampleBy(1, SampleByUnit.Day)//有字符串重载
               .Select(it=>new { 
                  Id=SqlFunc.AggregateMin(it.Id),
                  Count=SqlFunc.AggregateCount(it.Id)
                })
               .ToList();
//用例2:统计后过滤
var list=db.Queryable<UnitSiafayyy>()
               .SampleBy(1, SampleByUnit.Day)//有字符串重载
               .Select(it=>new { 
                  Id=SqlFunc.AggregateMin(it.Id),
                  Count=SqlFunc.AggregateCount(it.Id)
                })
                .MergeTable().Where(it=>it.Count>1)
               .ToList(); 
 
//ORM建表情况下,创建时间一定要加 分表标记,并且忽略更新
public class UnitSiafayyy
{
   [SugarColumn(IsPrimaryKey = true)]
   public long Id { get; set; }
   [TimeDbSplitField(DateType.Year)]//重点2个标签
   [SugarColumn(IsOnlyIgnoreUpdate =true)]
   public DateTime DateTime { get; set; }
}

九、独有:Last On (开窗函数)

请升级到:5.1.4.86-preview10

分组取最后一条可以多个字段,相当于其他数据库的开窗口函数要弱一些

var list db.Queryable<Users>()
 .PartitionBy(it=>new {时间戳字段,分组字段1,分组字段1}).ToList();//只能根据时间戳字段取最后一条
 
  //生成的Sql
  //SELECT * RFOM USERS Lastest on  时间戳字段 Partition By 分组字段1,分组字段1
 
  //ORM建表要注意
  [TimeDbSplitField(DateType.Year)]//时间戳字段需标记一下
  [SugarColumn(IsOnlyIgnoreUpdate =true)]
  public DateTime DateTime { get; set; }

十、大数据插入

注意:symbol 类型如果使用不当会影响插入速度,看一下symbol 类型文档中介绍

symbol 可以理解为字典分类 去重后要小于6万,也就不能唯一,要重复使用的字段类型才能用symbol 

 //新功能:如果存在时间插入不进去改一下系统短时间格式 
 //Nuget安装:SqlSugar.QuestDb.RestAPI
 db.RestApi().BulkCopy(list)//是RestApi不是Fastest
 db.RestApi().PageSize(50000).BulkCopy(list)//一次太多可以分页
 
  //程序启动时加上, 可以不配置取默认
 QuestDbRestAPI.HttpPort=9000;//默认9000
 QuestDbRestAPI.UserName;//默认和连接字符串一样
 QuestDbRestAPI.Password;//默认和连接字符串一样
 
 
 //传统写法 兼容性好些,并发差些
 db.Insertable(updateObjs).UseParameter().ExecuteCommand()//100万16秒

十一、大数据更新

时序库不建议大数据库更新,普通更新数量大了会比较慢,所以要换个思路

没有唯一索引情况下是可以插入重复数据的

更新操作都换成插入,通过LastOn查询最新记录实现

LastOn用法:看标题11

十二、并发插入[table busy]

常见错误:00000: table busy [reason=insert]

解决方案:https://github.com/questdb/questdb/issues/1461

使用WAL表https://questdb.io/docs/concept/write-ahead-log/#comparison

在高性能并发情况下并发建议使用异步处理

//新写法 如果存在时间插入不进去改一下系统短时间格式 
//Nuget安装:SqlSugar.QuestDb.RestAPI
await db.RestApi().BulkCopyAsync(list)//是RestApi不是Fastest
await db.RestApi().BulkCopyAsync(data)//支持单条
await  db.RestApi().ExecuteCommandAsync("sql");

 //程序启动时加上, 可以不配置取默认
 QuestDbRestAPI.HttpPort=9000;//默认9000
 QuestDbRestAPI.UserName;//默认和连接字符串一样
 QuestDbRestAPI.Password;//默认和连接字符串一样

//老写法
await db.CopyNew().Insertable().UseParameter().ExecuteCommandAsync()

十三、绿色数据库下载

加群 :995692596 群文件可以下载,后缀改成Zip

十四、常见错误 

文件被锁 

错误:could not open read-only [file=H:\BuleDb\QuestDb\bin\qdbroot\db\booltest511\default\a.d]”

1、重启服务器 

2、禁止使用tuncatetable语法和修改表操作,这些表初始化后不要动了

3、上面2个都不行在用C#程序插入一记录 锁就解除了

db.Insertable(new BoolTest511() { Id = 1, dateTime = DateTime.Now, A = true }).ExecuteCommand();

错误: Exception while reading from stream

解决方案:先检查SQL是否正确,在不行就重启服务

十五、官网

https://questdb.io/docs/get-started/docker

关闭
果糖网