Java 持久化Joda Time`DateTime`对象最有效的方法是什么?

Java 持久化Joda Time`DateTime`对象最有效的方法是什么?,java,serialization,jodatime,Java,Serialization,Jodatime,我已经编写了一些Java软件,它们经常从Redis中持久化和检索对象。目前我只是对对象进行序列化和反序列化。软件读取对象的频率大约是写入对象的50倍。我没有分析序列化/反序列化Joda Time对象,但该软件在计算和负载下扩展良好,我对其性能感到满意 没有很好扩展的是内存使用。序列化的Joda Time对象非常大,在我需要将其刷新到磁盘上的关系数据库之前,一个相当大的Redis实例只需要大约3天的客户数据。第二个问题是,数据集越大,Redis自己的备份机制似乎越难管理 抛开在这个问题上投入更多内

我已经编写了一些Java软件,它们经常从Redis中持久化和检索对象。目前我只是对对象进行序列化和反序列化。软件读取对象的频率大约是写入对象的50倍。我没有分析序列化/反序列化Joda Time对象,但该软件在计算和负载下扩展良好,我对其性能感到满意

没有很好扩展的是内存使用。序列化的Joda Time对象非常大,在我需要将其刷新到磁盘上的关系数据库之前,一个相当大的Redis实例只需要大约3天的客户数据。第二个问题是,数据集越大,Redis自己的备份机制似乎越难管理

抛开在这个问题上投入更多内存的诱惑,到目前为止,我想到了以下想法:

  • 序列化然后在持久化之前压缩对象
  • 作为ISO日期格式字符串保留
  • 作为其他与Joda兼容的字符串格式保存
在决定之前,我将尝试并分析这些方法,但我想知道是否有人能想出一种更有效的方法来减少持久化Joda对象的内存占用,而不破坏计算库?

ISO 8601 虽然我对Redis一无所知……一般来说,序列化Joda Time对象最简单、最有效的方法是利用其内置的对日期时间值的合理、明确、标准字符串格式的支持

对于分区日期时间值,标准提供了
YYYY-MM-DDTHH:MM:SS.SSS±HH:SS
格式,例如
2014-10-24T21:17:30+02:00
2014-10-24T19:17:30Z
Zulu
Z
表示0:00的偏移)

各种Joda Time 2.5类使用ISO 8601作为默认值来解析和生成日期时间值的字符串表示形式

生成字符串 对于
DateTime
,只需显式或隐式调用其方法即可

String output = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).toString();
通常,在存储日期时间值时,最好使用UTC。Joda Time可让您轻松适应UTC

DateTime nowMontreal = DateTime.now( DateTimeZone.forID( "America/Montreal" ) );
DateTime nowUtc = nowMontreal.withZone( DateTimeZone.UTC );
String output = nowUtc.toString();
另一个例子

DateTime output = DateTime.now( DateTimeZone.UTC ).toString();
解析字符串 解析同样简单。唯一的问题是时区。如果省略时区,通常Joda time会分配JVM的当前默认时区。如果明确指定所需的时区,通常会更好

DateTime dateTimeMontreal = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.forID( "America/Montreal" ) );
或者,对于UTC

DateTime dateTimeUtc = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.UTC ) );
java.time 另一种选择是新的内置Java8。受Joda Time的启发,java.Time在许多方面都很相似。但一个区别是java.time默认情况下通过扩展ISO 8601标准以附加时区名称来生成字符串表示。虽然标准格式与UTC有偏移量,但会丢失实际的时区信息。(时区是一个偏移量加上夏令时规则以及当前、未来和过去的其他异常。)

另一方面,通常最好以UTC存储日期时间。如果您在输入数据时真正关心时区,通常最好将该信息与UTC调整值分开存储

在java.time中,该类表示UTC时间线上的一个时刻

Instant instant = Instant.parse( "2014-10-24T19:17:30Z" );
String outputInstant = instant.toString();
2014-10-24T19:17:30Z

若要调整为时区,请指定以获取

2014-10-24T15:17:30-04:00[美国/蒙特利尔]


尝试分析日期时间对象的分布。如果碰巧它们彼此比较接近,那么你可以做一些“魔术”:

1) 您可以引入一个特殊的“起始日期”常量,然后将实际日期存储为从该常量开始的天数移位-这将是整数值(64位arch.w/o压缩上约8个字节)

2) 你需要实际时间吗?如果没有,就浪费时间;如果是-您可以在一个int变量中存储小时+分钟+秒(在64位arch.w/o压缩上再存储8个字节)

3) 分析结果-有可能在单个int变量中同时拟合日期(shift)和时间


4) 引入一种缓存机制,这将大大提高序列化/反序列化对象的性能

从新纪元开始存储毫秒。它是单个长值。如果需要时区值,还可以将时区Id存储为字符串。序列化和解析字符串表示总是需要更多的资源,包括RAM、内存中的大量数据处理、一些正则表达式、分配更多内存的拆分调用

使用此构造函数还原值:
public BaseDateTime(长即时,DateTimeZone)
它是如此轻巧,因为它可以立即存储每个DateTime实例的引擎盖下的内容:

public BaseDateTime(long instant, Chronology chronology) {
        super();
        iChronology = checkChronology(chronology);
        iMillis = checkInstant(instant, iChronology);
        adjustForMinMax();
}

Basil,感谢您的详细回答和在Java8中使用JSR310类的想法。正如我所承诺的,我将分析所有这些想法,并根据我的测试选择正确答案。等等。。。去年去了哪里?很抱歉,接受这个答案花了这么长时间。终于有时间测试joda DateTime和java.time ZonedDateTime对象与Redis数据库之间的序列化和ISO字符串,ISO 8601获胜。当存储在Redis中时,ISO格式通常与对象的序列化速度相同(而且要小得多)。从Redis(我的应用程序非常重视)读取数据比序列化快50%左右。有趣的是,joda和java.time之间的差异似乎可以忽略不计,至少在我的测试中是这样。Ivan,谢谢你的回答和使用int偏移量的好主意。应用程序是分布式的,因此本地缓存可能是不可能的(redis就是缓存)。将测试此想法并相应地标记正确答案。
public BaseDateTime(long instant, Chronology chronology) {
        super();
        iChronology = checkChronology(chronology);
        iMillis = checkInstant(instant, iChronology);
        adjustForMinMax();
}