Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails PostgreSQL无间隙序列_Ruby On Rails_Postgresql_Sequence - Fatal编程技术网

Ruby on rails PostgreSQL无间隙序列

Ruby on rails PostgreSQL无间隙序列,ruby-on-rails,postgresql,sequence,Ruby On Rails,Postgresql,Sequence,我正在从MySql迁移到Postgres,我注意到,当您从MySql中删除行时,在创建新行时,这些行的唯一ID将被重新使用。对于Postgres,如果创建行并删除行,则不会再次使用唯一ID 在研究生中有这种行为的原因吗?在这种情况下,我能让它更像MySql吗?序列有间隙,允许并发插入。试图避免间隙或重复使用已删除的ID会造成可怕的性能问题。看 PostgreSQL用于分配ID。它们只会不断增加,并且不受通常事务回滚规则的约束,以允许多个事务同时获取新ID。这意味着,如果事务回滚,这些ID将被“丢

我正在从MySql迁移到Postgres,我注意到,当您从MySql中删除行时,在创建新行时,这些行的唯一ID将被重新使用。对于Postgres,如果创建行并删除行,则不会再次使用唯一ID


在研究生中有这种行为的原因吗?在这种情况下,我能让它更像MySql吗?

序列有间隙,允许并发插入。试图避免间隙或重复使用已删除的ID会造成可怕的性能问题。看

PostgreSQL用于分配ID。它们只会不断增加,并且不受通常事务回滚规则的约束,以允许多个事务同时获取新ID。这意味着,如果事务回滚,这些ID将被“丢弃”;没有“免费”ID的列表,只有当前ID计数器。如果数据库不干净地关闭,序列通常也会递增

合成密钥(ID)无论如何都是毫无意义的。它们的顺序并不重要,它们唯一重要的特性是唯一性。您无法有意义地测量两个ID之间的“距离”,也无法有意义地说一个ID大于或小于另一个ID。你所能做的就是说“平等”或“不平等”。其他任何东西都是不安全的。你不应该在意差距


如果您需要重新使用已删除ID的无间隙序列,您可以拥有一个,您只需为此放弃大量性能—特别是,您根本无法在
INSERT
s上实现任何并发,因为您必须扫描表以查找最低的可用ID,锁定表进行写入,以便其他事务不能声明相同的ID。请尝试搜索“postgresql无间隙序列”

最简单的方法是使用计数器表和获取下一个ID的函数;不过,它不会重复使用ID

CREATE TABLE thetable_id_counter ( last_id integer not null );
INSERT INTO thetable_id_counter VALUES (0);

CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;

COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';
用法:

INSERT INTO dummy(id, blah) 
VALUES ( get_next_id('thetable_id_counter','last_id'), 42 );
请注意,当一个打开的事务获得ID时,尝试调用
get\u next\u ID
的所有其他事务将被阻止,直到第一个事务提交或回滚。这对于无间隙ID来说是不可避免的,并且是设计的

如果要在一个表中存储多个用于不同目的的计数器,只需向上述函数中添加一个参数,向计数器表中添加一列,并向
UPDATE
中添加一个
WHERE
子句,该子句将参数与添加的列相匹配。这样,您就可以拥有多个独立锁定的计数器行。不要只为新计数器添加额外的列

此函数不重复使用已删除的ID,它只是避免引入间隙

要重复使用ID,我建议。。。不要重复使用ID

如果确实必须这样做,您可以在感兴趣的表上添加
ON INSERT或UPDATE或DELETE
触发器,将已删除的ID添加到自由列表边表,并在插入
时将其从自由列表表中删除。将
UPDATE
视为
DELETE
,然后是
INSERT
。现在修改上面的ID生成函数,使其在更新限制1的空闲ID中执行
选择空闲ID到下一个值,如果找到,
删除该行<代码>如果未找到
将正常从生成器表中获取新ID。以下是先前功能的未经测试的扩展,以支持重用:

CREATE OR REPLACE FUNCTION get_next_id_reuse(countertable regclass, countercolumn text, freelisttable regclass, freelistcolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('SELECT %I FROM %s FOR UPDATE LIMIT 1', freelistcolumn, freelisttable) INTO next_value;
    IF next_value IS NOT NULL THEN
        EXECUTE format('DELETE FROM %s WHERE %I = %L', freelisttable, freelistcolumn, next_value);
    ELSE
        EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    END IF;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;

序列具有允许并发插入的间隙。试图避免间隙或重复使用已删除的ID会造成可怕的性能问题。看

PostgreSQL用于分配ID。它们只会不断增加,并且不受通常事务回滚规则的约束,以允许多个事务同时获取新ID。这意味着,如果事务回滚,这些ID将被“丢弃”;没有“免费”ID的列表,只有当前ID计数器。如果数据库不干净地关闭,序列通常也会递增

合成密钥(ID)无论如何都是毫无意义的。它们的顺序并不重要,它们唯一重要的特性是唯一性。您无法有意义地测量两个ID之间的“距离”,也无法有意义地说一个ID大于或小于另一个ID。你所能做的就是说“平等”或“不平等”。其他任何东西都是不安全的。你不应该在意差距


如果您需要重新使用已删除ID的无间隙序列,您可以拥有一个,您只需为此放弃大量性能—特别是,您根本无法在
INSERT
s上实现任何并发,因为您必须扫描表以查找最低的可用ID,锁定表进行写入,以便其他事务不能声明相同的ID。请尝试搜索“postgresql无间隙序列”

最简单的方法是使用计数器表和获取下一个ID的函数;不过,它不会重复使用ID

CREATE TABLE thetable_id_counter ( last_id integer not null );
INSERT INTO thetable_id_counter VALUES (0);

CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;

COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';
用法:

INSERT INTO dummy(id, blah) 
VALUES ( get_next_id('thetable_id_counter','last_id'), 42 );
请注意,当一个打开的事务获得ID时,尝试调用
get\u next\u ID
的所有其他事务将被阻止,直到第一个事务提交或回滚。这对于无间隙ID来说是不可避免的,并且是设计的

如果要在一个表中存储多个用于不同目的的计数器,只需向上述函数中添加一个参数,向计数器表中添加一列,并向
UPDATE
中添加一个
WHERE
子句,该子句将参数与添加的列相匹配。这样,您就可以拥有多个独立锁定的计数器行。不要只为新计数器添加额外的列

此函数不重复使用已删除的ID,它只是避免引入间隙

要重复使用ID,我建议。。。不要重复使用ID

如果您确实必须这样做,您可以通过在感兴趣的表上添加
ON INSERT或UPDATE或DELETE
触发器来实现,该触发器将删除的ID添加到自由列表边表,并在插入
时将其从自由列表表中删除。处理<