我正在做一个Grails 2.5版本的项目。我遇到了一个关于Gorm / Hibernate的问题。该项目部署在2台服务器上,每台服务器都将以循环方式获得调用。当我们点击我们的一个API时,它试图创建一个新的实体并在DB中持久化。
问题是,当我们调用这个API时,几乎所有其他请求都失败了,并显示错误消息:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '493' for key 'PRIMARY'
我尝试设置id值,但它不接受该值,而是尝试自己的值。我还尝试在子类中Mapid generator 'assigned'
(不能接触基类,因为有其他子类,基类的生成器设置为identity)
我的问题是,我们如何清除hibernate认为是主键的下一个值的内容?我试过禁用二级缓存和查询缓存,但这不起作用。我们有没有一种方法可以编程地清除hibernate缓存,以便它进入DB查看那里的最大id值?不仅如此,如果你知道任何其他方法来解决它,那么请张贴他们太。
编辑:
下面是基类和子类的Map:
基类:
abstract class BaseEntity implements Serializable {
static mapping = {
tablePerHierarchy false // avoid creating the base_domain table
tablePerConcreteClass true
id generator: 'identity'
createdDate index: true // index this column in each subclass table
inUse index: true // index this column in each subclass table
}
}
子类:
class ActualEntity1 extends BaseEntity {
static mapping = {
id generator: 'assigned'
}
}
这些是gorm域类。我故意删除了类的其他字段。
1条答案
按热度按时间9wbgstp71#
如果调用相同的API,则可能存在争用条件。所以,现在,我想做的第一件事就是不要像现在这样频繁地出现这个问题。
之后,如果发生这种情况,我们可以考虑在Redis中使用一个公共密钥,我们可以继续递增。
我正在尝试所有不涉及更改DB模式的解决方案
确实,以循环方式使用多个服务器可能会导致主键的下一个值出现竞争条件。当您使用
identity
生成器时,Hibernate依赖于底层数据库来生成ID,通常是通过自动递增列。在多服务器环境中处理这种情况的最直接的方法是使用序列生成器,数据库将始终生成一个唯一的标识符,确保没有重复的键。
但是,这涉及到修改数据库模式。
您可以开始并尝试使用UUIDs (Universally Unique Identifiers) as primary keys。UUID是以这样一种方式生成的,即它们实际上保证是唯一的。
这不需要更改数据库模式,但需要修改域对象以支持UUID作为主键。
它确实具有几乎消除碰撞可能性的优点。
但是:UUID比常规的整数ID大,在某些情况下可能更难管理。
由于您已经尝试在
ActualEntity1
中使用assigned
生成器,因此可以通过查询数据库以获得最大当前ID,然后向其添加一个ID,以编程方式确定下一个ID。然而,这有潜在的竞争风险。您必须实现一些锁定机制来确保ID不重复。
您也可以使用外部服务:
有关插图“Generating Distributed UUID's using Zookeeper",请参见Phani Kumar Yadavilli,2019年5月。
问题是,将DB中的这个键更改为UUID不仅是对该特定表的更改,而且对其他保留FK的表也是如此。*
所以,我想避免大量的数据迁移,这将到来。此外,我尝试了生成器分配,但只有当它的工作作为记录,我不会粘贴这个问题。
如果有一种方法可以在grails中实现,而不需要设置zookeeper / redis / DB更改,我将非常感激。
好的,考虑到这些限制,另一种方法可能是结合使用编程分配和同步。
这将涉及使用专用数据库表作为分布式锁机制。这将有助于确保在给定时间只有一台服务器可以分配ID,从而有效地消除竞争条件。
您需要:
DistributedLock
的表,其中只有一行。SELECT ... FOR UPDATE
语句,它试图在事务期间锁定行。这种方法确实增加了一点锁定的开销,但可以确保在不更改主键类型或引入外部系统的情况下处理争用条件。
伪代码可能如下所示:
但是,如果系统的负载非常重,需要频繁创建实体,那么这可能不是最具可伸缩性的解决方案,您可能需要探索其他途径。