逻辑与物理时钟以及乐观锁定相关知识 -Vlad Mihalcea


在本文中,我将解释逻辑和物理时钟版本控制策略如何工作,以及为什么您应该更喜欢使用逻辑时钟进行并发控制。
乐观锁定是一种可行的解决方案,用于防止在运行应用程序级事务时丢失更新。
乐观锁定要求版本列可以表示为:

  • 物理时钟(从系统时钟获取的时间戳值)
  • 逻辑时钟(递增的数值)

本文将说明为什么逻辑时钟更适合于乐观锁定机制。
 
系统时间
系统时间由当前操作系统的内部时钟提供。所述可编程间隔定时器周期性地发送一个中断信号(用1.193182 MHz的频率)。CPU接收时间中断,并增加一个滴答计数器。
Unix和Window都将时间记录为自预定义的绝对时间参考(纪元)以来的滴答数。操作系统时钟分辨率从1ms(Android)100ns(Windows)1ns(Unix)不等。
 
单调时间
要排序事件,版本必须单调前进。虽然增加本地计数器是单调函数,但是系统时间可能并不总是返回单调时间戳。系统时间并非总是单调递增。
Java有两种获取当前系统时间的方法。您可以使用:
此方法尝试使用当前的操作系统单调时钟实现,但是如果找不到单调时钟,它将回到挂钟时间
  1. System#currentTimeMillis(),它为您提供自Unix时代以来经过的毫秒数此方法不会给您单调的时间结果,因为它返回的墙上时钟时间容易进行正向和反向调整(如果NTP用于系统时间同步)。对于单调currentTimeMillis,您可以检查Peter Lawrey的解决方案Bitronix Transaction Manager单调时钟
  2. System#nanoTime(),返回自任意选择的时间参考以来经过的纳秒数

  
数据库时间戳精度
SQL-92标准将TIMESTAMP数据类型定义为YYYY-MM-DD hh:mm:ss。小数部分是可选的,每个数据库都实现特定的时间戳记数据类型:
  • 甲骨文TIMESTAMP(9) 最多可以使用9个小数位(纳秒精度)。
  • 微软SQLDATETIME2 具有100ns的精度。
  • MySQLMySQL的5.6.4增加了对微秒精度的支持TIME,DATETIME和TIMESTAMP类型。在5.6.4之前的版本中,MySQL丢弃所有时间类型的小数部分。
  • PostgreSQL这两个TIME和TIMESTAMP类型有微秒精度。
  • DB2TIMESTAMP(12) 最多可以使用12个小数位(皮秒精度)

当涉及到持久时间戳时,大多数数据库服务器至少提供6个小数位。MySQL用户长期以来一直在等待更精确的时间类型,而5.6.4版本最终增加了微秒精度。
在5.6.4之前的MySQL数据库服务器上,更新可能会在任何给定的每秒生命周期内丢失。这是因为所有更新同一数据库行的事务都将看到相同的版本时间戳(指向当前正在运行的秒的开始)。在5.6.4版本之前,MySQL仅支持秒精度时间戳。
 
处理时间不是那么容易
递增本地版本号始终更安全,因为此操作不依赖任何外部因素。如果数据库行已经包含更高的版本号,则您的数据已过时。就这么简单。
另一方面,时间是最复杂的维度之一。如果您不相信我,请查看的夏令时处理注意事项
Java用了8个版本终于有了成熟的Date / Time API。跨应用程序层处理时间(从JavaScript到Java中间件再到数据库日期/时间类型)会使情况变得更糟。
处理系统时间是一项艰巨的任务。您必须注意leap秒夏令时时区和各种时间标准
 
分布式计算的经验教训
乐观锁定与事件排序有关,因此自然而然地,我们只对事前发生的关系感兴趣。
在分布式计算中,逻辑时钟优于物理时钟(系统时钟),因为网络时间同步意味着可变的延迟。
序列号版本控制类似于Lamport时间戳算法,每个事件仅增加一个计数器。
虽然Lamport时间戳是为多个分布式节点事件同步定义的,但数据库乐观锁定要简单得多,因为只有一个节点(数据库服务器)可以同步所有事务(来自并发客户端连接)。
分布式计算相对于物理时钟更喜欢逻辑时钟,因为无论如何我们只对事件排序感兴趣。