我已经编写了一些经常持久化并从Redis中检索Joda-Time DateTime
个对象的Java软件。我只是对目前的对象进行序列化和反序列化。该软件读取对象的次数比写入次数多50倍。我没有对Joda-Time对象进行序列化/反序列化,但该软件在计算上有很好的扩展性,在负载下并且我对性能感到满意。
没有缩放的是内存使用情况。序列化的Joda-Time对象非常大,大小合适的Redis实例在我需要将其刷新到磁盘上的关系数据库之前,只需要大约3天的客户数据。第二个问题是Redis'自己的备份机制似乎更难管理数据集得到的更大......
撇开为这个问题投入更多内存的诱惑,到目前为止我已经想到了以下想法:
我会在决定之前尝试并分析这些,但我想知道是否有人能想到一种更有效的方法来减少持久化Joda对象的内存占用而不破坏计算库?
答案 0 :(得分:4)
虽然我对Redis一无所知......一般来说,序列化Joda-Time对象的最简单,最有效的方法是利用它们对明智,明确,标准ISO 8601字符串格式的内置支持。日期时间值。
对于分区日期时间值,标准提供YYYY-MM-DDTHH:MM:SS.SSS±HH:SS
格式,例如2014-10-24T21:17:30+02:00
或2014-10-24T19:17:30Z
(Z
Zulu
表示偏移量为DateTime
00:00来自UTC)。
各种Joda-Time 2.5类使用ISO 8601作为解析和生成日期时间值的String表示的默认值。
对于String output = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).toString();
,只需明确或隐式调用其toString
方法。
DateTime nowMontreal = DateTime.now( DateTimeZone.forID( "America/Montreal" ) );
DateTime nowUtc = nowMontreal.withZone( DateTimeZone.UTC );
String output = nowUtc.toString();
通常最好在存储日期时间值时使用UTC。 Joda-Time可让您轻松调整为UTC。
DateTime output = DateTime.now( DateTimeZone.UTC ).toString();
另一个例子。
DateTime dateTimeMontreal = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.forID( "America/Montreal" ) );
解析同样容易。唯一的问题是时区。如果省略时区,通常Joda-Time将分配JVM的当前默认时区。如果您明确指定所需的时区,通常会更好。
DateTime dateTimeUtc = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.UTC ) );
或者,对于UTC ...
Instant instant = Instant.parse( "2014-10-24T19:17:30Z" );
String outputInstant = instant.toString();
另一个替代方案是Java 8中内置的新java.time package。受Joda-Time的启发,java.time在很多方面都类似。但一个区别是java.time默认通过扩展ISO 8601标准来附加时区名称来生成字符串表示。虽然标准格式具有与UTC的偏移量,但您会丢失实际的时区信息。 (时区是偏移加上夏令时的规则以及当前,未来和过去的其他异常。)
另一方面,通常最好以UTC格式存储日期时间。如果您真的关心数据输入时的时区,除了UTC调整后的值之外,通常最好单独存储该信息。
在java.time中,Instant
类表示UTC时间轴上的一个时刻。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
String outputZdt = zdt.toString();
2014-10-24T19:17:30Z
要调整为时区,请指定ZoneId
以获得ZonedDateTime
。
{{1}}
2014-10-24T15:17:30-04:00 [美国/蒙特利尔]
答案 1 :(得分:1)
尝试分析日期时间对象的分布。如果碰巧他们彼此相对接近,那么你可以做一些“魔术”:
1)你可以引入一个特殊的“起始点日期”常量,然后将实际日期存储为从常量偏移的天数 - 这将是整数值(64位拱上的~8个字节。没有压缩)
2)你需要实际时间吗?如果没有 - 只是扔掉时间;如果是 - 您可以在一个int变量中存储小时+分钟+秒(另一个在64位拱上的8个字节。没有压缩)
3)分析结果 - 你有可能同时适应两个:日期(轮班)和单个int变量中的时间
4)引入一种缓存机制,可以大大提高序列化/反序列化对象的性能
答案 2 :(得分:0)
自纪元开始以来存储毫秒。它是一个单一的long值。如果您需要时区值,也将时区ID存储为字符串。序列化和解析字符串表示形式将始终占用更多资源,包括RAM,内部包含大量数据处理,一些正则表达式,分配更多内存的拆分调用。
使用此构造函数来还原值:public BaseDateTime(long instant, DateTimeZone zone)
如此轻巧,是因为它可以立即存储每个DateTime实例的内容:
public BaseDateTime(long instant, Chronology chronology) {
super();
iChronology = checkChronology(chronology);
iMillis = checkInstant(instant, iChronology);
adjustForMinMax();
}