当前位置: 首页 > news >正文

TDengine数据迁移之数据对比

数据完整性和一致性校验是迁移数据后的必要步骤,TDengine 数据迁移也是如此。但通常TDengine存储的都是海量数据,动辄几百亿条数据,如果像手工对比差异,工作量是非常巨大的。

以下脚本实现了对两个数据库记录数的对比。主要实现方式为:

  1. 读取超级表信息
  2. 读取时间段信息
  3. 通过 select count(*) from 超级表 group by tbname where ts>='' and ts<''; 查询子表记录数
  4. 对比源和目标库的记录数是否一致
  5. 输出对比结果。

为保证兼容2.x和3.x,数据库查询采用 Restful 方式。

脚本使用方法如下:

  1. 将要对比的超级表名称放入相同目录的stblist文件中(必须是同一个数据库)。
  2. 配置源和目标数据库信息(用户名、密码、URL、数据库名称)
  3. 运行脚本 python3 datacompare.py 2023-01-01T00:00:00Z 2023-10-01T00:00:00Z

注意:

  1. 时间格式必须是 ISO8601 格式
  2. 如果没有指定时间段,则默认为2000-01-01T00:00:00.000+00:002023-10-01T00:00:00.000+00:00
import requests
import sys
import datetime
import json
from requests.auth import HTTPBasicAuth
import configparserdef arg_j(sarg):"""Parse time string in ISO8601 format to timestamp."""try:dt = datetime.datetime.fromisoformat(sarg).strftime('%s')return dtexcept ValueError:sys.exit(f"{sarg}. Time only support ISO8601 format!")def request_post(url, sql, user, pwd):"""Post request to specific url."""try:sql = sql.encode("utf-8")headers = {'Connection': 'keep-alive','Accept-Encoding': 'gzip, deflate, br',}result = requests.post(url, data=sql, auth=HTTPBasicAuth(user,pwd),headers=headers)text = result.content.decode()return textexcept Exception as e:print(e)def check_return(result, tdversion):"""Check result of request."""if tdversion == 2:datart = json.loads(result).get("status")else:datart = json.loads(result).get("code")if str(datart) == 'succ' or str(datart) == '0':chkrt = 'succ'else:chkrt = 'error'return chkrtdef get_data(stbname, url, username, password, dbname, version, stime, etime):"""Get data from source database or destination database."""data = dict()if version == 2:sql = f"select count(*) from `{dbname}`.`{stbname}` where _c0>='{stime}' and _c0<='{etime}' group by tbname;"else:sql = f"select count(*),tbname from `{dbname}`.`{stbname}` where _c0>='{stime}' and _c0<='{etime}' group by tbname;"rt = request_post(url, sql, username, password)code = check_return(rt, version)if code != 'error':rdata = json.loads(rt).get("data")for ll in range(len(rdata)):data[rdata[ll][1]] = rdata[ll][0]else:print(rt)return datadef compare_data(source_info, destination_info, stime, etime):"""Compare data between source database and destination database."""tb_lost = set()tb_diff = set()with open('stblist', 'r') as sfile:for stbname in sfile:stbname = stbname.strip()source_data = get_data(stbname, **source_info, stime=stime, etime=etime)destination_data = get_data(stbname, **destination_info, stime=stime, etime=etime)for key, source_value in source_data.items():destination_value = destination_data.get(key)if destination_value is None:tb_lost.add(key)print(f'Table {key} not exist in destination DB {destination_info["dbname"]}')elif destination_value != source_value:tb_diff.add(key)print(f'Table {key} has different values between source and destination, source is {source_value}, destination is {destination_value}.')print("Lost tables: {}, Diff tables: {}.".format(len(tb_lost), len(tb_diff)))def main():config = configparser.ConfigParser()config.read('config.ini')source_info = {'url': config['source']['url'],'username': config['source']['username'],'password': config['source']['password'],'dbname': config['source']['dbname'],'version': int(config['source']['version']),}destination_info = {'url': config['destination']['url'],'username': config['destination']['username'],'password': config['destination']['password'],'dbname': config['destination']['dbname'],'version': int(config['destination']['version']),}if len(sys.argv) >= 3:stime = str(sys.argv[1])etime = str(sys.argv[2])else:stime = '2000-01-01T00:00:00.000+00:00'etime = '2023-10-01T00:00:00.000+00:00'arg_j(stime)arg_j(etime)compare_data(source_info, destination_info, stime, etime)if __name__ == "__main__":main()

以上代码是 AI 修改过的,不保证能够执行成功。

我将调试好的代码也上传了。点击下载

相关文章:

  • idea生成代码(一):实现java语言的增删改查功能(基于EasyCode插件)支持自定义模板【非常简单】
  • excel用RAND函数生成一个大于0小于1的随机数
  • 使用 HTTP Client 轻松进行 API 测试
  • clickhouse的安装和配置
  • 超详细!必看!!STM32--系统滴答SysTick
  • 鸿蒙系统扫盲(一):鸿蒙OS和开源鸿蒙什么关系?
  • 2023网络钓鱼状况报告:ChatGPT等工具致网络钓鱼电子邮件数量激增1265%
  • Excel表列序号
  • Mysql 和 Redis 数据如何保持一致
  • Antv/G2 折线图 DataSet 数据展开成指定格式
  • SQLite3 数据库学习(一):数据库和 SQLite 基础
  • 【入门Flink】- 09Flink水位线Watermark
  • ArcGIS进阶:栅格计算器里的Con函数使用方法
  • Vue项目的学习一
  • mac 安装使用svn教程
  • ES6指北【2】—— 箭头函数
  • (三)从jvm层面了解线程的启动和停止
  • [deviceone开发]-do_Webview的基本示例
  • Bytom交易说明(账户管理模式)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • echarts花样作死的坑
  • Git 使用集
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • Odoo domain写法及运用
  • Phpstorm怎样批量删除空行?
  • vue.js框架原理浅析
  • VuePress 静态网站生成
  • 百度小程序遇到的问题
  • 免费小说阅读小程序
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 入门到放弃node系列之Hello Word篇
  • 深度学习入门:10门免费线上课程推荐
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 算法---两个栈实现一个队列
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 我与Jetbrains的这些年
  • 一起参Ember.js讨论、问答社区。
  • Java性能优化之JVM GC(垃圾回收机制)
  • #Linux(Source Insight安装及工程建立)
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (四)汇编语言——简单程序
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .Net CF下精确的计时器
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NET下ASPX编程的几个小问题
  • @31省区市高考时间表来了,祝考试成功
  • @RequestBody与@ResponseBody的使用
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • @Validated和@Valid校验参数区别