是否可以使用特定值插入PostgreSQL锁?

是否可以使用特定值插入PostgreSQL锁?,sql,postgresql,sql-insert,Sql,Postgresql,Sql Insert,是否可以在PostgreSQL的INSERT语句中为特定值创建锁 比如说,我有一张桌子: CREATE TABLE IF NOT EXISTS bookings ( bookingID SERIAL PRIMARY KEY, tableID integer REFERENCES tables ON DELETE CASCADE, startTime timestamp NOT NULL, endTime timestamp NOT NULL, ); 现在,如果启动了一个

是否可以在PostgreSQL的INSERT语句中为特定值创建锁

比如说,我有一张桌子:

CREATE TABLE IF NOT EXISTS bookings (
bookingID   SERIAL PRIMARY KEY,
tableID     integer REFERENCES tables ON DELETE CASCADE,
startTime   timestamp NOT NULL,
endTime     timestamp NOT NULL,
);
现在,如果启动了一个带有INSERT的事务块,我可以为事务期间具有相同tableID和相同日期的所有其他INSERT创建一个锁吗

因此,同一tableID的第二次预订必须等待第一次预订完成。然后可以再次检查,是否仍然可以插入。 因此,它基本上是插入中特定值的行锁

该程序是用Java编写的,但由于瓶颈,我不想使用同步块

谢谢你的帮助

同意。当然,同步块是更好的解决方案

但是如果您坚持使用数据库锁,那么()可以帮助您

例如:

begin; -- we will use transaction level lock but there is an option with session level locks
select pg_advisory_xact_lock(123); -- 123 is a tableID you need to lock
insert into bookings(tableID, ...) values(123, ...); -- or/and other queries
commit;
同意。当然,同步块是更好的解决方案

但是如果您坚持使用数据库锁,那么()可以帮助您

例如:

begin; -- we will use transaction level lock but there is an option with session level locks
select pg_advisory_xact_lock(123); -- 123 is a tableID you need to lock
insert into bookings(tableID, ...) values(123, ...); -- or/and other queries
commit;

问题是,为什么要阻止此表的其他插入

对我来说,看起来您希望确保同一个tableID没有相交间隔。可能您在Java代码中检查了这一点,并且不希望其他插入干扰检查

如果是这样,则根本不需要锁:使用约束

为此,您需要:

  • 将两个
    timestamp
    字段更改为一个
    tsrange
    字段
  • 安装扩展插件(它包含在contribs中)
  • 您的表将如下所示:

    CREATE TABLE IF NOT EXISTS bookings (
      bookingID   SERIAL PRIMARY KEY,
      tableID     integer REFERENCES tables ON DELETE CASCADE,
      during      tsrange NOT NULL,
      EXCLUDE using gist(during with &&, tableID with =)
    );
    
    将自动创建特殊的GIST索引,以确保相同的tableID(
    =
    运算符)没有相交间隔(
    &&&
    运算符)

    一些例子:

    -- interval for tableID=10
    test=# insert into bookings values (1, 10, '[2015-11-17 10:00, 2015-11-17 12:00)');
    INSERT 0 1
    
    -- interval for tableID=11
    test=# insert into bookings values (2, 11, '[2015-11-17 10:00, 2015-11-17 12:00)');
    INSERT 0 1
    
    -- can't create intersecting interval for tableID=10
    test=# insert into bookings values (3, 10, '[2015-11-17 11:00, 2015-11-17 13:00)');
    ERROR:  conflicting key value violates exclusion constraint "bookings_during_tableid_excl"
    DETAIL:  Key (during, tableid)=(["2015-11-17 11:00:00","2015-11-17 13:00:00"), 10) conflicts with existing key (during, tableid)=(["2015-11-17 10:00:00","2015-11-17 12:00:00"), 10).
    
    -- ok to create non-intersecting interval
    test=# insert into bookings values (4, 10, '[2015-11-17 12:00, 2015-11-17 13:00)');
    INSERT 0 1
    

    问题是,为什么要阻止此表的其他插入

    对我来说,看起来您希望确保同一个tableID没有相交间隔。可能您在Java代码中检查了这一点,并且不希望其他插入干扰检查

    如果是这样,则根本不需要锁:使用约束

    为此,您需要:

  • 将两个
    timestamp
    字段更改为一个
    tsrange
    字段
  • 安装扩展插件(它包含在contribs中)
  • 您的表将如下所示:

    CREATE TABLE IF NOT EXISTS bookings (
      bookingID   SERIAL PRIMARY KEY,
      tableID     integer REFERENCES tables ON DELETE CASCADE,
      during      tsrange NOT NULL,
      EXCLUDE using gist(during with &&, tableID with =)
    );
    
    将自动创建特殊的GIST索引,以确保相同的tableID(
    =
    运算符)没有相交间隔(
    &&&
    运算符)

    一些例子:

    -- interval for tableID=10
    test=# insert into bookings values (1, 10, '[2015-11-17 10:00, 2015-11-17 12:00)');
    INSERT 0 1
    
    -- interval for tableID=11
    test=# insert into bookings values (2, 11, '[2015-11-17 10:00, 2015-11-17 12:00)');
    INSERT 0 1
    
    -- can't create intersecting interval for tableID=10
    test=# insert into bookings values (3, 10, '[2015-11-17 11:00, 2015-11-17 13:00)');
    ERROR:  conflicting key value violates exclusion constraint "bookings_during_tableid_excl"
    DETAIL:  Key (during, tableid)=(["2015-11-17 11:00:00","2015-11-17 13:00:00"), 10) conflicts with existing key (during, tableid)=(["2015-11-17 10:00:00","2015-11-17 12:00:00"), 10).
    
    -- ok to create non-intersecting interval
    test=# insert into bookings values (4, 10, '[2015-11-17 12:00, 2015-11-17 13:00)');
    INSERT 0 1
    

    您需要的是谓词锁定。PostgreSQL不直接支持它,但@stas.yaranov描述的建议锁定是一种很好的解决方法。正如@EgorRogov所指出的,如果可能的话,您应该通过使用适当的约束来完全消除锁定的需要

    另一个无人提及的选项是在PostgreSQL 9.1或更新版本中使用
    SERIALIZABLE
    事务隔离。这使用了一种非常类似于乐观锁定的并发控制方法,其中每个事务都在不锁定的情况下进行,但如果其中一个事务与另一个事务冲突,则其中一个事务可能会中止。这意味着您的应用程序必须准备好捕获序列化失败错误并重试事务,但这通常是一种非常有效且非常简单的处理此类问题的方法,尤其是在排除约束对您没有帮助的情况下

    如果可以的话,我建议在这种特殊情况下使用排除约束,因为这正是它们的设计目的。但是您可以使用
    serializable
    隔离,而无需更改模式或添加新索引,这是一种更通用的解决方案


    (顺便说一句,在9.0或更高版本中,您不能使用
    SERIALIZABLE
    ,因为在这些版本中它不够智能)

    您需要的是谓词锁定。PostgreSQL不直接支持它,但@stas.yaranov描述的建议锁定是一种很好的解决方法。正如@EgorRogov所指出的,如果可能的话,您应该通过使用适当的约束来完全消除锁定的需要

    另一个无人提及的选项是在PostgreSQL 9.1或更新版本中使用
    SERIALIZABLE
    事务隔离。这使用了一种非常类似于乐观锁定的并发控制方法,其中每个事务都在不锁定的情况下进行,但如果其中一个事务与另一个事务冲突,则其中一个事务可能会中止。这意味着您的应用程序必须准备好捕获序列化失败错误并重试事务,但这通常是一种非常有效且非常简单的处理此类问题的方法,尤其是在排除约束对您没有帮助的情况下

    如果可以的话,我建议在这种特殊情况下使用排除约束,因为这正是它们的设计目的。但是您可以使用
    serializable
    隔离,而无需更改模式或添加新索引,这是一种更通用的解决方案


    (顺便说一句,在9.0或更高版本中,您不能使用
    可序列化的
    ,因为在这些版本中它不够智能)

    “我不想使用同步块”-为什么您认为在数据库资源上同步比在Java代码中同步更有效?据我所知,如果我使用
    已同步的
    ,整个表被“锁定”,因此其他事务必须等待。但如果可能的话,我只想锁定部分表以提高效率。另一个选项是
    可序列化
    隔离,但@EgorRogov建议使用排除约束是这种情况下更好的解决方案。“我不想使用同步块”-为什么您认为在数据库资源上进行同步比在Java代码中进行同步更有效?据我所知,如果使用
    synchronized
    ,整个表都被“锁定”,因此其他事务必须等待。但如果可能的话,我只想锁定部分表以提高效率。另一个选项是
    SERIALIZABLE
    isolation,但是@EgorRogov的sugge