具体(Java)示例的乐观锁定 [英] Optimistic Locking by concrete (Java) example

查看:19
本文介绍了具体(Java)示例的乐观锁定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了一上午的时间阅读了所有关于 Google 在乐观锁定方面大展身手的热门文章,并且对于我的生活,我还是不太明白.

I have spent my morning reading all the top articles that Google churns up on optimistic locking, and for the life of me, I still don't really get it.

理解乐观锁定涉及添加用于跟踪记录版本"的列,并且该列可以是时间戳、计数器或任何其他版本跟踪构造.但是我仍然不明白这是如何确保 WRITE 完整性的(这意味着如果多个进程同时更新同一个实体,那么该实体之后会正确反映它应该处于的真实状态).

I understand that optimistic locking involves the addition of a column for tracking the record's "version", and that this column can be a timestamp, a counter, or any other version-tracking construct. But I'm still not understanding how that ensures WRITE integrity (meaning that if multiple process are updating the same entity at the same time, that afterwards, the entity correctly reflects the true state it should be in).

有人可以提供一个具体的、易于理解的示例,说明如何在 Java 中使用乐观锁(可能针对 MySQL 数据库).假设我们有一个 Person 实体:

Can someone provide a concrete, easy-to-understand example of how optimistic locking could be used in Java (against, perhaps, a MySQL DB). Let's say we have a Person entity:

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Color favoriteColor;
}

并且 Person 实例被持久化到 people MySQL 表中:

And that Person instances get persisted to a people MySQL table:

CREATE TABLE people (
    person_id PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,        # } I realize these column defs are not valid but this is just pseudo-code
    age INT NOT NULL,
    color_id FOREIGN KEY (colors) NOT NULL  # Say we also have a colors table and people has a 1:1 relationship with it
);

现在假设有 2 个软件系统,或者 1 个系统上有 2 个线程,它们试图同时更新同一个 Person 实体:

Now let's say there are 2 software systems, or 1 system with 2 threads on it, that are trying to update the same Person entity at the same time:

  • 软件/线程 #1 试图保留姓氏更改(从John Smith"到John Doe")
  • 软件/线程 #2 试图保持最喜欢的颜色的变化(从 REDGREEN)
  • Software/Thread #1 is trying to persist a surname change (from "John Smith" to "John Doe")
  • Software/Thread #2 is trying to persist a change in the favorite color (from RED to GREEN)
  1. 如何在 people 和/或 colors 表上实现乐观锁定?(寻找具体的 DDL 示例)
  2. 然后您如何在应用程序/Java 层使用这种乐观锁定?(寻找具体的代码示例)
  3. 有人可以帮我完成一个场景,其中 DDL/代码更改(来自上面的 #1 和 #2)将在我的场景(或任何其他场景)中发挥作用,并且会乐观地锁定"people/colors 表格正确吗?基本上,我希望看到乐观锁定的作用,并对其工作原理进行简单易懂的解释.
  1. How could optimistic locking be implemented on the people and/or colors tables? (Looking for specific DDL example)
  2. How could you then utilize this optimistic locking at the application/Java layer? (Looking for specific code example)
  3. Can someone run me through a scenario where the DDL/code changes (from #1 and #2 above) would come into play in my scenario (or any other scenario) and would "optimistically lock" the people/colors tables correctly? Basically, I'm looking to see optimistic locking in action, with an easy-to-follow explanation of why it works.

推荐答案

通常,当您研究乐观锁时,您也会使用像 休眠 或其他带有 @Version 支持.

Normally when you look into optimistic locking you also use a library like Hibernate or an other JPA-Implementation with @Version support.

示例可以是这样的:

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    private Color favoriteColor;
    @Version
    private Long version;
}

虽然如果您不使用支持此注释的框架,显然没有必要添加 @Version 注释.

while obviously there is no point of adding a @Version annotation if you are not using a framework which supports this.

DDL 可以是

CREATE TABLE people (
    person_id PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(100) NOT NULL,
    last_name VARCHAR(100) NOT NULL,        # } I realize these column defs are not valid but this is just pseudo-code
    age INT NOT NULL,
    color_id FOREIGN KEY (colors) NOT NULL,  # Say we also have a colors table and people has a 1:1 relationship with it
    version BIGINT NOT NULL
);

版本会怎样?

  1. 每次在存储实体之前,您都会检查存储在数据库中的版本是否仍然是您知道的版本.
  2. 如果是,则以版本递增一存储您的数据

为了在两个步骤之间不冒其他进程更改数据的风险完成这两个步骤,通常通过像

To get both steps done without risking an other process changing data between both steps it is normally handled through a statement like

UPDATE Person SET lastName = 'married', version=2 WHERE person_id = 42 AND version = 1;

执行语句后,检查是否更新了一行.如果您这样做了,那么自从您阅读数据以来,没有其他人更改过数据,否则其他人更改了数据.如果其他人更改了数据,您通常会收到 OptimisticLockException 由您正在使用的库.

After executing the statement you check if you updated a row or not. If you did, nobody else changed the data since you've read it, otherwise somebody else changed the data. If somebody else changed the data you will normally receive an OptimisticLockException by the library you are using.

此异常应导致所有更改被撤销,并且更改值的过程将重新启动,因为更新实体的条件可能不再适用.

This exception should cause all changes to be revoked and the process of changing the value to be restarted as the condition upon which the entity was to be updated may no longer be applicable.

所以没有碰撞:

  1. 进程 A 读取人
  2. 进程 A 写入 Person 从而增加版本
  3. 进程 B 读取 Person
  4. 进程 B 写入 Person 从而增加版本

碰撞:

  1. 进程 A 读取人
  2. 进程 B 读取 Person
  3. 进程 A 写入 Person 从而增加版本
  4. 进程 B 在尝试保存时收到异常,因为自读取 Person 以来版本已更改

如果颜色是另一个对象,您应该按照相同的方案在那里放置一个版本.

If Colour is another object you should put a version there by the same scheme.

  • 乐观锁定并不是合并冲突更改的魔法.乐观锁定只会防止进程意外覆盖另一个进程的更改.
  • 乐观锁实际上并不是真正的 DB-Lock.它只是通过比较 version 列的值来工作.你不会阻止其他进程访问任何数据,所以期望你得到 OptimisticLockExceptions

如果许多不同的应用程序访问您的数据,您最好使用由数据库自动更新的列.例如用于 MySQL

If many different applications access your data you may be best off using a column automatically updated by the database. e.g. for MySQL

version TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

这样,实现乐观锁的应用程序就会注意到愚蠢应用程序的变化.

this way the applications implementing optimistic locking will notice changes by dumb applications.

如果您更新实体的频率高于 TIMESTAMP 的分辨率或其 Java 解释,则该方法可能无法检测到某些更改.此外,如果您让 Java 生成新的 TIMESTAMP,您需要确保所有运行您的应用程序的机器都处于完美的时间同步.

If you update entities more often than the resolution of TIMESTAMP or the Java-interpretation of it, ths approach can fail to detect certain changes. Also if you let Java generate the new TIMESTAMP you need to ensure that all machines running your applications are in perfect time-sync.

如果您的所有应用程序都可以更改整数、长整数、......版本通常是一个很好的解决方案,因为它永远不会受到不同设置时钟的影响;-)

If all of your applications can be altered an integer, long, ... version is normally a good solution as it will never suffer from differently set clocks ;-)

还有其他场景.你可以例如每次要更改一行时,使用哈希甚至随机生成一个 String.重要的是,当任何进程为本地处理或缓存保存数据时,您不要重复值,因为该进程将无法通过查看版本列来检测更改.

There are other scenarios. You could e.g. use a hash or even randomly generate a String every time a row is to be changed. Important is, that you don't repeat values while any process is holding data for local processing or inside a cache as that process will not be able to detect change by looking at the version-column.

作为最后的手段,您可以使用所有字段的值作为版本.虽然在大多数情况下这将是最昂贵的方法,但它是一种无需更改表结构即可获得类似结果的方法.如果您使用 Hibernate,则有 @OptimisticLocking - 强制执行此行为的注释.在实体类上使用 @OptimisticLocking(type = OptimisticLockType.ALL) 如果自您读取实体或 @OptimisticLocking(type = OptimisticLockType.DIRTY) 后任何行发生更改,则失败> 在另一个进程也更改了您更改的字段时失败.

As a last resort you may use the value of all fields as version. While this will be the most expensive approach in most cases it is a way to get similar results without changing the table structure. If you use Hibernate there is the @OptimisticLocking-annotation to enforce this behavior. Use @OptimisticLocking(type = OptimisticLockType.ALL) on the entity-class to fail if any row changed since you have read the entity or @OptimisticLocking(type = OptimisticLockType.DIRTY) to just fail when another process changed the fields you changed, too.

这篇关于具体(Java)示例的乐观锁定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆