IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Mysql 由于 Java 日期 LocalDateTime 数据精度引发的线上问题 -> 正文阅读

[大数据]Mysql 由于 Java 日期 LocalDateTime 数据精度引发的线上问题

最近在公司开发的项目是对账系统,其中就包括每日(T+1)对账。每日对账就是对前一日期产生的数据对于核对。举个具体的例子就是:在第三方支付公司,里面会产生支付里面的支付订单与下载渠道(微信、支付或者其它第三方支付)的每日交易账户需要进行对比。关于微信支付的 下载交易账单 地址。支付宝或者其它的下载交易单内容类似,只是文件格式不一样。

我们来看一下对账的数据流向:

在这里插入图片描述
今天要说的是在查询我们保存的第三方支付支付订单与微信支付的交易账单进行对比的时候,在查询我们 T 日数据的时候多查询出来了数据导致数据对不平。下面我把数据库的对账表的简化模型简单列举一下:

对账数据表

create table trade_record (
	id BIGINT(20) AUTO_INCREMENT COMMENT '主键',
	source_no varchar(32) not null DEFAULT '' COMMENT '数据来源(微信、交易系统、支付宝等)',
	order_no VARCHAR(64) not null DEFAULT '' COMMENT '订单号',
	compare_no VARCHAR(64) not null DEFAULT '' COMMENT '对比号',
	amount DECIMAL(20,4) not null default '0' COMMENT '金额',
	trade_at DATETIME not null COMMENT '交易成功时间',
	create_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
	update_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
	PRIMARY KEY (`id`),
	index idx_trade_at(trade_at),
	index idx_create_at(create_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '交易记录表';

我们先插入几条数据用于查询测试:

待对账数据

-- 支付系统订单
insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('payment', '111111', '111111', '100', '2022-05-10 02:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('payment', '222222', '222222', '200', '2022-05-10 05:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('payment', '333333', '333333', '300', '2022-05-10 12:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('payment', '444444', '444444', '400', '2022-05-10 23:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('payment', '555555', '555555', '500', '2022-05-11 00:00:10');

-- 渠道返回订单
insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('wechat_pay', '111111', '111111', '100', '2022-05-10 02:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('wechat_pay', '222222', '222222', '200', '2022-05-10 05:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('wechat_pay', '333333', '333333', '300', '2022-05-10 12:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('wechat_pay', '444444', '444444', '400', '2022-05-10 23:23:43');

insert into trade_record(source_no, order_no, compare_no, amount, trade_at) 
values ('wechat_pay', '555555', '555555', '500', '2022-05-11 00:00:00');

插入完成后,总共 10 条数据:

在这里插入图片描述
比如我们需要比较 2022-05-10 的数据的时候,那些我们只需要查询出时间范围为:2022-05-10 00:00:00 ~ 2022-05-10 23:59:59 的微信支付以及支付平台的数据然后按照 compare_no 进行逐条对比就行了。

支付系统数据:
在这里插入图片描述
渠道返回数据:
在这里插入图片描述
查询出来两方数据,我们就可以根据 compare_no 进行逐一比对,可以得到两方的对账是可以对平的。虽然我们是这样设计的,然后我们验证也没有问题。但是在线上出现了对账不匹配的情况。

DateUtils.java

public abstract class DateUtils {

    /**
     * 昨天的开始时间
     * @return
     */
    public static LocalDateTime yesterdayDateBegin() {
        LocalDate currentTime = LocalDate.now();
        LocalDate yesterday = currentTime.plusDays(-1);
        return LocalDateTime.of(yesterday, LocalTime.MIN);
    }

    /**
     * 昨天的结束时间
     * @return
     */
    public static LocalDateTime yesterdayDateEnd() {
        LocalDate currentTime = LocalDate.now();
        LocalDate yesterday = currentTime.plusDays(-1);
        return LocalDateTime.of(yesterday, LocalTime.MAX);
    }
    
}

以上是日期工具类,在单元测试的时候没有问题。但是上面的工具类得到的开始结束时间以今日是: 2022-05-10 为例:

  • 昨日开始时间:2022-05-09T00:00
  • 昨日结束时间:2022-05-09T23:59:59.999999999

然后以上面的时间进行查询的得到的结果是:
在这里插入图片描述
结果查询出来了一条交易成功时间为:2022-05-11 00:00:00 的数据,与我们的期望不符。我们可以查看 Mysql 官网对于 Datetime 这个日期类型的描述。可以看到 Datetime 这个日期类型的精度为小数点后面 6 位。而我们在查询的时候使用的是 9 位。于是我就使用不同的精度的结束时间进行查询:

  • 使用 6 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 69 进行查询,查询总条数为 4 条,符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 79 进行查询,查询总条数为 5 条,不符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 9999991 进行查询,查询总条数为 4条,符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 9999992 进行查询,查询总条数为 4条,符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 9999993 进行查询,查询总条数为 4条,符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 9999994 进行查询,查询总条数为 4条,符合期望。
  • 使用 7 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 9999995 进行查询,查询总条数为 5条,不符合期望。
  • 使用 8 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 89 进行查询,查询总条数为 5 条,不符合期望。
  • 使用 9 位精度的结束时间查询:也就是 2022-05-10 23.59.59. 添加 99 进行查询,查询总条数为 5 条,不符合期望。

所以我们可以得出结论:在使用 Mysql datetime 进行数据查询的时候只能使用正确的精度才能得出符合期望的数据。并且当使用 datetime 大于精度的时候是进行四舍五入的。

建议大家在查询一天内的数据的时候使用 左关右开 模式也就是:

时间 >= ‘2022-05-10 00:00:00’ 并且 时间 < ‘2022-05-11 00:00:00’

参考文章:

  • https://dev.mysql.com/doc/refman/5.7/en/fixed-point-types.html
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:30:44  更:2022-05-11 16:32:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:58:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码