删除具有大量传入外键的行时共享内存不足
Out of shared memory when deleting rows with lots of incoming foreign keys
我开发了一个多租户应用程序,其中我们有一个主架构来跟踪租户,以及 99 个应用程序数据库来分配负载。每个应用程序数据库中的 33 tables 中的每一个都有一个指向主模式的租户列。这意味着有 3,267 个外键指向主模式的租户 ID,大约有 6000 个触发器与租户相关 table.
最近,我添加了一个 table 并开始在我们删除测试租户的测试套件的拆卸部分出现此错误:
psycopg2.errors.OutOfMemory: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
CONTEXT: SQL statement "SELECT 1 FROM ONLY "test2"."item" x WHERE OPERATOR(pg_catalog.=) "tenant" FOR KEY SHARE OF x"
For query
SET CONSTRAINTS ALL IMMEDIATE
按照建议提高 max_locks_per_transaction 可以解决问题,删除一些应用架构也是如此。这里明显的解决方案是减少冗余模式的数量或删除外键约束,这样我们就不必持有那么多锁,但我很好奇这里是否还有其他问题。
我以为只有要删除的行(与测试模式相关联)会被锁定,因此只有测试模式会被锁定。无论如何,此时没有数据指向租户 table,因此锁定在实践中几乎是多余的。
更新:
为了了解更多上下文,我在这里并没有做任何真正花哨的事情。下面是我的架构和查询的简化示例:
CREATE SCHEMA master;
CREATE table master.tenant (id uuid NOT NULL PRIMARY KEY);
CREATE SCHEMA app_00;
CREATE table app_00.account (id uuid NOT NULL PRIMARY KEY, tenant uuid NOT NULL);
ALTER TABLE app_00.account ADD CONSTRAINT fk_tenant FOREIGN KEY (store) REFERENCES master.store(id) DEFERRABLE;
CREATE table app_00.item (id uuid NOT NULL PRIMARY KEY, tenant uuid NOT NULL);
ALTER TABLE app_00.item ADD CONSTRAINT fk_tenant FOREIGN KEY (store) REFERENCES master.store(id) DEFERRABLE;
实际上,我正在为 app_00..99 的每个模式创建 33 个 table。现在假设我的数据库中填充了数据,因上述错误而失败的查询是:
DELETE FROM master.tenant WHERE id = 'some uuid';
您没有告诉我们太多有关设置的信息,但可能涉及分区或继承。这些功能通常要求语句在查询计划或执行期间递归到 table 分区或继承子级。无论如何,您的 SQL 陈述必须涉及许多 table。
现在,每当 PostgreSQL 接触到 table 时,它都会对其加锁以避免并发执行冲突。如果涉及很多 table,则可能是最初具有 max_connections * max_locks_per_transaction
个条目的锁 table 已用完。
解决办法就是增加max_locks_per_transaction
。别担心,提高该参数不会产生负面影响,只会在服务器启动期间分配更多的共享内存。
我开发了一个多租户应用程序,其中我们有一个主架构来跟踪租户,以及 99 个应用程序数据库来分配负载。每个应用程序数据库中的 33 tables 中的每一个都有一个指向主模式的租户列。这意味着有 3,267 个外键指向主模式的租户 ID,大约有 6000 个触发器与租户相关 table.
最近,我添加了一个 table 并开始在我们删除测试租户的测试套件的拆卸部分出现此错误:
psycopg2.errors.OutOfMemory: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
CONTEXT: SQL statement "SELECT 1 FROM ONLY "test2"."item" x WHERE OPERATOR(pg_catalog.=) "tenant" FOR KEY SHARE OF x"
For query
SET CONSTRAINTS ALL IMMEDIATE
按照建议提高 max_locks_per_transaction 可以解决问题,删除一些应用架构也是如此。这里明显的解决方案是减少冗余模式的数量或删除外键约束,这样我们就不必持有那么多锁,但我很好奇这里是否还有其他问题。
我以为只有要删除的行(与测试模式相关联)会被锁定,因此只有测试模式会被锁定。无论如何,此时没有数据指向租户 table,因此锁定在实践中几乎是多余的。
更新:
为了了解更多上下文,我在这里并没有做任何真正花哨的事情。下面是我的架构和查询的简化示例:
CREATE SCHEMA master;
CREATE table master.tenant (id uuid NOT NULL PRIMARY KEY);
CREATE SCHEMA app_00;
CREATE table app_00.account (id uuid NOT NULL PRIMARY KEY, tenant uuid NOT NULL);
ALTER TABLE app_00.account ADD CONSTRAINT fk_tenant FOREIGN KEY (store) REFERENCES master.store(id) DEFERRABLE;
CREATE table app_00.item (id uuid NOT NULL PRIMARY KEY, tenant uuid NOT NULL);
ALTER TABLE app_00.item ADD CONSTRAINT fk_tenant FOREIGN KEY (store) REFERENCES master.store(id) DEFERRABLE;
实际上,我正在为 app_00..99 的每个模式创建 33 个 table。现在假设我的数据库中填充了数据,因上述错误而失败的查询是:
DELETE FROM master.tenant WHERE id = 'some uuid';
您没有告诉我们太多有关设置的信息,但可能涉及分区或继承。这些功能通常要求语句在查询计划或执行期间递归到 table 分区或继承子级。无论如何,您的 SQL 陈述必须涉及许多 table。
现在,每当 PostgreSQL 接触到 table 时,它都会对其加锁以避免并发执行冲突。如果涉及很多 table,则可能是最初具有 max_connections * max_locks_per_transaction
个条目的锁 table 已用完。
解决办法就是增加max_locks_per_transaction
。别担心,提高该参数不会产生负面影响,只会在服务器启动期间分配更多的共享内存。