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

大表数据如何在OceanBase中进行表分区管理的实践

背景

将Zabbix的数据库迁移至OceanBase后,以其中的几个大表作为案例,本文将分享如何利用ODC(OceanBase 开发者工具),来进行自动管理OB数据库中的表分区的方案。

因为原始表里已经有大量的数据,如果贸然对原始表去创建表分区的话,可能会造成长时间的缩影,所以,当前的整体思路是:

  1. 为每个要操作的表创建一个 [tb_name]---->临时表[tb_name_tmp],表结构语原始表保持一致;
  2. 通过代码为每个临时表 [tb_name_tmp]生成表分区创建语句;
  3. 为每个临时表 [tb_name_tmp]创建表分区;
  4. 将操作日前一天的数据通过 insert into select 写入到临时表 [tb_name_tmp]中,并校验
  5. 停止应用,并将剩余数据写入到临时表 [tb_name_tmp]中
  6. 将原始表[tb_name]重名为[tb_name_bak],并将临时表 [tb_name_tmp]重命名为[tb_name]
  7. 启动服务,并检查服务是否正常,如果服务异常,检查数据是否正确;
  8. 如果服务正常,在odc中为相关表创建表分区维护计划;
  9. 一段时间后,将原始表[tb_name_bak]进行归档或者移除;

操作对象

首先,明确需要操作的表对象是哪些:

表名作用保留时长清理间隔数据类型
history存储原始的历史数据90天每天数字(浮点数)
history_uint存储原始的历史数据90天每天数字(无符号)
history_str存储原始的短字符串数据90天每天字符型
history_text存储原始的长字符串数据90天每天文本
history_log存储原始的日志字符串数据90天每天日志
trends存储每小时统计数据(趋势)12个月每月数字(浮点数)
trends_uint保持每小时统计数据(趋势)12个月每月数字(无符号)
auditlog审计日志表

准备操作

查找各个表中的最早记录

如果记录最早时间早于保留时间,那就从保留日开始导入,如果晚于保留开始日期,则用最早记录的日期开始建立分区

SELECT FROM_UNIXTIME(MIN(clock)) FROM history;SELECT FROM_UNIXTIME(MIN(clock)) FROM history_uint;SELECT FROM_UNIXTIME(MIN(clock)) FROM history_str;SELECT FROM_UNIXTIME(MIN(clock)) FROM history_text;SELECT FROM_UNIXTIME(MIN(clock)) FROM history_log;SELECT FROM_UNIXTIME(MIN(clock)) FROM trends;SELECT FROM_UNIXTIME(MIN(clock)) FROM trends_uint;

创建分区表结构

SHOW CREATE TABLE history;
SHOW CREATE TABLE history_uint;
SHOW CREATE TABLE history_str;
SHOW CREATE TABLE history_text;
SHOW CREATE TABLE history_log;
SHOW CREATE TABLE trends;
SHOW CREATE TABLE trends_uint;

使用以下脚本来生成表分区

使用python生成创建表分区的SQL语句

by day

# -*- coding: utf-8 -*-            
# @Time     : 2023/6/8 10:17
# @Author   : YoKing Ma
# @FileName : create_tp_day.py
# @Software : PyCharmimport datetimetb_name = input("Please input data table name:")print(f"""
Please get the first day, use like this command:
SELECT FROM_UNIXTIME(MIN(clock)) FROM {tb_name}
""")
start_day = input("Please input the first day (2023-03-03): ")# tb_name="history"
# start_day="2023-03-03"try:first_date = datetime.datetime.strptime(start_day, "%Y-%m-%d")print(f"Your input date is [{first_date}]")
except ValueError:print("date format is error!")pt_create=[]period = 0
while period <= 90:curr_date = first_date + datetime.timedelta(days=period)curr_date_title = curr_date.strftime("%Y_%m_%d")tommow_date = curr_date + datetime.timedelta(days=1)period += 1sql_str=f'PARTITION p{curr_date_title} VALUES LESS THAN (UNIX_TIMESTAMP("{tommow_date}"))'pt_create.append(sql_str)pt_sql_str = f"""ALTER TABLE {tb_name} PARTITION BY RANGE (clock)
("""
for sql in pt_create:pt_sql_str += '\n'pt_sql_str += sqlpt_sql_str += ','pt_sql_str += "\b\n);"print(pt_sql_str)

by mouth

# -*- coding: utf-8 -*-            
# @Time     : 2023/6/8 11:27
# @Author   : YoKing Ma
# @FileName : create_tp_month.py
# @Software : PyCharm## pip install python-dateutilimport datetime
from dateutil.relativedelta import relativedeltatb_name = input("Please input data table name:")print(f"""
Please get the first day, use like this command:
SELECT FROM_UNIXTIME(MIN(clock)) FROM {tb_name}
""")
start_month = input("Please input the first day (2023-03): ")# tb_name="history"
# start_month="2022-02"try:tmp_date = start_month.split('-')fyear = int(tmp_date[0])fmonth = int(tmp_date[1])fdate = datetime.datetime(fyear, fmonth, 1)
except ValueError:print("date format is error!")pt_create=[]period = 12
cdate = fdate
for i in range(0,13):title = f"{cdate.year}_{cdate.month}"ndate = cdate + relativedelta(months=+1)sql = f'PARTITION p{title} VALUES LESS THAN (UNIX_TIMESTAMP("{ndate}"))'pt_create.append(sql)cdate = ndatept_sql_str = f"""ALTER TABLE {tb_name} PARTITION BY RANGE (clock)
("""
for sql in pt_create:pt_sql_str += '\n'pt_sql_str += sqlpt_sql_str += ','pt_sql_str += "\b\n);"print(pt_sql_str)

案例演示

如生成了 `history` 表的临时表 `history_tmp`

CREATE TABLE `history_tmp` (`itemid` bigint(20) unsigned NOT NULL,`clock` int(11) NOT NULL DEFAULT '0',`value` double NOT NULL DEFAULT '0',`ns` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`itemid`, `clock`, `ns`)
) DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_bin;

用上面的脚本,为`history_tmp`表创建表分区语句

ALTER TABLE history_tmp PARTITION BY RANGE ( clock)
(
PARTITION p2024_04_04 VALUES LESS THAN (UNIX_TIMESTAMP("2024-04-05 00:00:00")) ENGINE = InnoDB,
PARTITION p2024_04_05 VALUES LESS THAN (UNIX_TIMESTAMP("2024-04-06 00:00:00")) ENGINE = InnoDB,
……
PARTITION p2024_07_01 VALUES LESS THAN (UNIX_TIMESTAMP("2024-07-02 00:00:00")) ENGINE = InnoDB,
PARTITION p2024_07_02 VALUES LESS THAN (UNIX_TIMESTAMP("2024-07-03 00:00:00")) ENGINE = InnoDB,
PARTITION p2024_07_03 VALUES LESS THAN (UNIX_TIMESTAMP("2024-07-04 00:00:00")) ENGINE = InnoDB
);

迁移数据

分2次迁移数据,这样可以减少业务停机的时长

数据导入

第一次迁移可以把变更日前的数据全部导入,比如变更日为2024年的7月3日,那将2024年7月3日 00:00:00之前90天的数据全部导入到分区表中

INSERT /*+ append enable_parallel_dml parallel(8) */ INTO `history_tmp`(`itemid`, `clock`, `value`, `ns`)
SELECT `itemid`, `clock`, `value`, `ns`
FROM `history`
WHERE `clock` >= 1712160000 and `clock` < 1719936000;

数据校验

对比临时表中的最早和最晚的记录是否和原表一致,并检查行数是否一致。

因为zabbix有housekeeper服务,这个服务会不定时的删除表中过期的数据,所以,在导入数据的过程中会造成数据不一致的情况。可以先把zabbix的管家服务停止;
SELECT FROM_UNIXTIME(MIN(clock)) FROM history_tmp;
SELECT FROM_UNIXTIME(MAX(clock)) FROM history_tmp;
SELECT COUNT(1) FROM history WHERE `clock` >= 1712160000 and `clock` < 1719936000;      -- 402789803
SELECT COUNT(1) FROM history_tmp;   -- 402789803

校验通过后,可以停止zabbix服务,并进行剩余数据的导入。操作与上文类似就不再赘述。

修改表名

rename table history to history_bak;
rename table history_tmp to history;

修改完表名后,就启动zabbix服务,看服务日志是否有报错,报错的话需要解决报错,直至服务正常。

在ODC中创建表分区管理计划

为已经分区表创建分区计划(在odc中每个数据库只允许有1个分区计划,新的分区计划生效后,旧的就失效了)

1720061551

我这边history相关表是每日一个表分区,trends相关表是1个月做一次表分区的维护。

所以【分区策略】我就以2中类型展示:【日】和【月】

分区策略--日

1720061781

这里产生的SQL语句类似于:

ALTER TABLE `zabbix`.`history` DROP PARTITION (`p2024_04_04`);ALTER TABLE `zabbix`.`history` ADD PARTITION (PARTITION `p2024_07_04` VALUES LESS THAN (1720108800));

策略中用到的表达式

分区创建表达式:unix_timestamp(DATE(NOW() - INTERVAL DAY(NOW())
表分区命名表达式:DATE_FORMAT(DATE(NOW() - INTERVAL DAY(NOW()) DAY + INTERVAL 1 DAY + INTERVAL 1 MONTH), 'p%Y_%m')

分区策略-月

1720061955

ALTER TABLE `zabbix`.`trends` DROP PARTITION (`p2023_7`);ALTER TABLE `zabbix`.`trends` ADD PARTITION (PARTITION `p2024_08` VALUES LESS THAN (1725120000));

策略中用到的表达式

分区创建表达式:unix_timestamp(DATE(NOW() - INTERVAL DAY(NOW())
表分区命名表达式:DATE_FORMAT(DATE(NOW() - INTERVAL DAY(NOW()) DAY + INTERVAL 1 DAY + INTERVAL 1 MONTH), 'p%Y_%m')

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Shiro-550—漏洞分析(CVE-2016-4437)
  • 【笔记】第二节 轧制、热处理和焊接工艺
  • 同一时刻最多有多少会议进行
  • EP25 调试接口渲染分类页面
  • Android 内核开发之—— repo 使用教程
  • 电线覆盖物检测数据集 气球风筝鸟巢 1300张 voc yol
  • 【Windows】快速帮你解决如何找到 Windows 上的 .condarc 文件
  • set的相关函数(3)
  • 关于加强银行业保险业移动互联网应用程序管理的通知
  • python爬虫初体验(一)
  • 网络编程问题解答
  • C++教程(五):C++高手养成之代码规范,如何写出规范优雅的程序
  • C# Action和delegate区别及示例代码
  • tb的数数问题(牛客小白月赛)
  • Vue3新组件transition(动画过渡)
  • 【Linux系统编程】快速查找errno错误码信息
  • conda常用的命令
  • fetch 从初识到应用
  • JavaScript设计模式与开发实践系列之策略模式
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • SpriteKit 技巧之添加背景图片
  • unity如何实现一个固定宽度的orthagraphic相机
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 事件委托的小应用
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 一道面试题引发的“血案”
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 阿里云重庆大学大数据训练营落地分享
  • 浅谈sql中的in与not in,exists与not exists的区别
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • # Kafka_深入探秘者(2):kafka 生产者
  • ## 基础知识
  • #LLM入门|Prompt#3.3_存储_Memory
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (创新)基于VMD-CNN-BiLSTM的电力负荷预测—代码+数据
  • (七)Activiti-modeler中文支持
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (转)http协议
  • .net framework 4.0中如何 输出 form 的name属性。
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net framework4与其client profile版本的区别
  • .NET 材料检测系统崩溃分析
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .NET下的多线程编程—1-线程机制概述
  • @vue-office/excel 解决移动端预览excel文件触发软键盘
  • [20170705]lsnrctl status LISTENER_SCAN1
  • [20170713] 无法访问SQL Server
  • [8-27]正则表达式、扩展表达式以及相关实战
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务
  • [C#]winform部署PaddleOCRV3推理模型
  • [C++] 轻熟类和对象