生成纳秒级别的时间戳,高性能

43 篇文章 0 订阅

问题

同步influxdb有些数据没有,不知道啥原因,后来百度发现时间需要唯一,毫秒还会重复,只能采用纳秒处理了

java实现

TimeStampUtils.java

package com.wujialiang;

/**
 * 获取纳秒值的工具类
 */
public class TimeStampUtils {

    /**
     * 递增数初始值
     */
    private static long randomNum = 1L;

    /**
     * 递增数最大值,可根据并发情况适当调整
     * 比如1秒钟生成1万条数据,这个值尽量接近或者大于1万
     */
    private static final long RANDOM_NUM_MAX = 999L;

    public static long getTimeStamp() {
        long curRandomNum;
        synchronized (TimeStampUtils.class) {
            if (randomNum > RANDOM_NUM_MAX) {
                randomNum = 1;
            }
            curRandomNum = randomNum++;
        }
        return System.currentTimeMillis() * 1000000L + System.nanoTime() % 1000000L + curRandomNum;
    }
}

测试

public static void main( String[] args )
{
    for (int i = 0; i < 100; i++) {
        System.out.println(TimeStampUtils.getTimeStamp());
    }
}

在这里插入图片描述

C#实现

NanoHelper.cs

using System.Diagnostics;

namespace InfluxdbStu01;

public class NanoHelper
{
    private static readonly object lockObj = new object();

    /// <summary>
    /// 递增数初始值
    /// </summary>
    private static long randomNum = 1L;

    /// <summary>
    /// 递增数最大值,可根据并发情况适当调整
    /// 比如1秒钟生成1万条数据,这个值尽量接近或者大于1万
    /// </summary>
    private const long RANDOM_NUM_MAX = 999L;

    /// <summary>
    /// 返回纳秒
    /// </summary>
    /// <returns>返回的结果是纳秒</returns>
    public static long getTimeStamp()
    {
        long curRandomNum;
        lock (lockObj)
        {
            if (randomNum > RANDOM_NUM_MAX)
            {
                randomNum = 1;
            }

            curRandomNum = randomNum++;
        }

        long nanoPart = Stopwatch.GetTimestamp(); // 计时器刻度(不是纳秒)
        long nowTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        /*
        皮秒 纳秒 微秒 毫秒 秒 ps、ns、us、ms、s 时间单位之间的换算
        1,000,000,000,000皮秒=1秒 (12个0)ps -> s
        1,000,000,000纳秒=1秒         (9个0) ns -> s
        1,000,000微秒=1秒                 (6个0)us -> s
        1,000毫秒=1秒                        (3个0) ms -> s
        */
        return  nowTimestamp* 1000000L
                + nanoPart % 1000000L + curRandomNum;
    }
}

测试

NanoHelper.getTimeStamp()

优化

上面代码效率比较低,优化一下,这个不加锁,效率比较高但是会有重复,要求不高可以使用

using System.Diagnostics;

namespace InfluxdbStu01;

public class TimeStampHelper
{
    private static readonly Stopwatch StopwatchInstance = new Stopwatch();
    
    // 静态构造函数,确保Stopwatch在类首次被访问时开始  
    static TimeStampHelper()
    {
        StopwatchInstance.Start();
    }

    /// <summary>  
    /// 返回一个组合了Unix时间毫秒和Stopwatch刻度的“时间戳”  
    /// 注意:这不是真正的纳秒精度,但提供了比毫秒更高的精度
    /// 会重复
    /// </summary>  
    /// <returns>返回一个long类型的时间戳</returns>  
    public static long GetTimeStamp()
    {
        long nowTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        long stopwatchTicks = StopwatchInstance.ElapsedTicks; // 获取Stopwatch的刻度  

        // 这里假设Stopwatch的刻度频率足够高,以提供亚毫秒级别的精度  
        // 但注意,这仍然不是纳秒级别的精度  
        // 如果需要纳秒级别的精度,您可能需要考虑使用硬件计时器或其他高精度时间源  

        // 根据Stopwatch的Frequency将刻度转换为更小的单位(例如微秒或纳秒的近似值)  
        // 但由于Frequency的单位是TicksPerSecond,我们需要先将其转换为纳秒/刻度,然后再乘以刻度数  
        double nanosecondsPerTick = 1_000_000_000.0 / Stopwatch.Frequency; // 纳秒/刻度  
        long subMillisecondNanoseconds = (long)(stopwatchTicks * nanosecondsPerTick);

        // 注意:由于double到long的转换,这里可能会有精度损失  
        // 如果需要更高的精度,可以保留为double或更大的整数类型,但这样会增加存储和处理的复杂性  

        // 将毫秒时间戳和亚毫秒纳秒部分组合起来  
        // 这里假设subMillisecondNanoseconds不会大于999,999,999(即小于1毫秒),这通常是合理的  
        return nowTimestamp * 1_000_000_000L + subMillisecondNanoseconds;
    }
}

100万数据54毫秒,但是重复数据有一半,优化重复的问题

namespace InfluxdbStu01;

public class TimeStampHelper2
{
    private static readonly object counterLock = new object(); // 用于同步的锁对象
    private static long counter; // 简单的递增计数器  

    /// <summary>  
    /// 返回一个组合了Unix时间毫秒和递增计数器的“时间戳”  
    /// 注意:这不是真正的纳秒精度,但在毫秒级别上增加了计数器以防止短时间内重复  
    /// </summary>  
    /// <returns>返回一个long类型的时间戳</returns>  
    public static long GetTimeStamp2()
    {
        long nowTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

        // 使用锁来确保计数器的线程安全递增  
        lock (counterLock)
        {
            if (counter >= int.MaxValue) // 防止计数器溢出(可选)  
            {
                counter = 0; // 重置计数器(可选)  
            }

            counter++;
        }

        // 将毫秒时间戳和计数器组合起来(这里简单地将计数器作为额外的精度添加)  
        // 注意:这种方法仍然可能在极短的时间内产生重复,但比仅使用时间戳的可能性小  
        return (nowTimestamp * 1000000L) + (int)counter; // 假设counter不会超过int范围  
    }
}

100万数据,53毫秒,没有重复,如果数据量够大的情况可能会出现,但是目前满足业务需求

参考

https://blog.csdn.net/hahawangzi520/article/details/134846374

  • 27
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

假装我不帅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值