繁体   English   中英

PostgreSQL - 在函数中启动一个事务块

[英]PostgreSQL - Start A Transaction block IN Function

我正在尝试在函数内部创建一个事务块,所以我的目标是一次使用这个函数,所以如果有人使用这个函数而另一个想要使用它,他就不能直到第一个完成我创建这个功能:

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
  max INT;
BEGIN
   BEGIN;
       SELECT MAX(max_value) INTO max FROM sch_lock.table_concurente;
       INSERT INTO sch_lock.table_concurente(max_value, date_insertion) VALUES(max + 1, now());
       -- Sleep a wail
       PERFORM pg_sleep(time_to_wait);
       RETURN max;
   COMMIT; 
END;
$$ 
LANGUAGE plpgsql;

但它接缝不起作用,我有一个错误语法错误BEGIN;

没有BEGIN; COMMIT我得到一个正确的结果,我使用此查询来检查:

-- First user should to wait 10 second
SELECT my_job(10) as max_value;

-- First user should to wait 3 second
SELECT my_job(3) as max_value;

结果是:

+-----+----------------------------+------------+
|  id |              date          | max_value  |
+-----+----------------------------+------------+
|  1  | 2017-02-13 13:03:58.12+00  |      1     |
+-----|----------------------------+------------+
|  2  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+
|  3  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+

但结果应该是:

+-----+----------------------------+------------+
|  id |              date          | max_value  |
+-----+----------------------------+------------+
|  1  | 2017-02-13 13:03:58.12+00  |      1     |
+-----|----------------------------+------------+
|  2  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+
|  3  | 2017-02-13 13:10:00.291+00 |      3     |
+-----+----------------------------+------------+

所以第三个id = 3应该有max_value = 3而不是2 ,这是因为第一个用户选择max = 1并等待10 sec而第二个用户选择max = 1并在插入前等待3 sec ,但是正确的解决方案是: 我不能使用这个功能直到第一个完成 ,因为我想做一些安全和保护。

我的问题是:

  • 如何在函数内部创建一个Transaction块?
  • 你有什么建议我们怎样才能以安全的方式做到这一点?

谢谢。

好的,所以你不能在函数中进行COMMIT 您可以有一个保存点并回滚到保存点。

您可能的最小事务是服务器从客户端解析并执行的单个语句,因此每个事务都是一个函数。 但是,在交易中,您可以拥有保存点。 在这种情况下,您将查看PostgreSQL的异常处理部分来处理这个问题。

然而,这不是你想要的。 您希望(我认为?)数据在长时间运行的服务器端操作期间可见。 为此,你有点不走运。 在运行函数时,您无法真正增加事务ID。

根据我认为的良好做法(最好到最差),你有几个选择:

  1. 将逻辑分解为更小的片,每个片将数据库从一个一致状态移动到另一个状态,并在单独的事务中运行它们。
  2. 在数据库中使用消息队列(如pg_message_queue),加上外部工作程序,以及运行步骤并为下一步生成消息的内容。 缺点是这增加了更多的维护。
  3. 使用像dblink或pl / python或pl / perlu这样的函数或框架连接回db并在那里运行事务。 ICK ....

您可以使用dblink。 就像是 :

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
  max INT;
BEGIN
   SELECT INTO RES dblink_connect('con','dbname=local');
   SELECT INTO RES dblink_exec('con', 'BEGIN');
   ...
   SELECT INTO RES dblink_exec('con', 'COMMIT');
   SELECT INTO RES dblink_disconnect('con');
END;
$$ 
LANGUAGE plpgsql;

我不知道这是否是一个好方法,但如果我们使用LOCK TABLE ,例如:

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
  max INT;
     BEGIN
       -- Lock table so no one will use it until the first one is finish
       LOCK TABLE sch_lock.table_concurente IN ACCESS EXCLUSIVE MODE;

       SELECT MAX(max_value) INTO max FROM sch_lock.table_concurente;
       INSERT INTO sch_lock.table_concurente(max_value, date_insertion) VALUES(max + 1, now());
       PERFORM pg_sleep(time_to_wait);
       RETURN max;
     END;
     $$ 
LANGUAGE plpgsql;

它给了我正确的结果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM