查询性能差的 PostgreSQL
Poor query performance PostgreSQL
我有一个问题:
SELECT
'{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
'\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type IN ('FOREIGN KEY')
WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
SELECT q.column_name FROM information_schema.constraint_column_usage q
inner join information_schema.table_constraints USING(constraint_name )
WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name
AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY')
ORDER BY c.ordinal_position;
它有效,但在具有 97 个模式的数据库中,运行 需要整整一分钟。
我该如何解决这个问题?
没有这一行:
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type = 'FOREIGN KEY'
不到1秒。
由于查询似乎都是关于 Postgres 关于您的数据库的元数据,因此大概这些查询不会像 table 中的一组常规 table 那样频繁更改查询会。
因此,我建议您使用 materialized view 设置为在您认为接受 table 的某个时间间隔内刷新数据货币。也许是每小时或每天——这完全取决于此数据更改的频率,以及您需要它的最新程度(这也是相互关联的)。
所以你会这样做:
CREATE MATERIALIZED VIEW mat_view AS
SELECT
'{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
'\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type IN ('FOREIGN KEY')
WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
SELECT q.column_name FROM information_schema.constraint_column_usage q
inner join information_schema.table_constraints USING(constraint_name )
WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name
AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY')
ORDER BY c.ordinal_position;
这实际上会在第一次 运行 时创建此视图结果的快照。然后可以通过REFRESH MATERIALIZED VIEW mat_view;
刷新
您可能还希望使用 CONCURRENTLY
选项,它可以防止阻塞,但可能需要更长的时间。
可以将 REFRESH MATERIALIZED VIEW
命令放在 cron 上,以确保它在您希望的时间间隔内完成。
注意: 实体化视图是 Postgres 9.3 中的一项新功能。 CONCURRENTLY
已添加到 Postgres 9.4.
根据 OP 的评论进行编辑:
关于 Postgres 8.4,这听起来似乎超出了您的控制范围,但您可能想用这些信息游说服务器管理员和 DBA,看看他们是否会改变主意:
- Postgres 8.4 发布将近 7 年前(2009 年 7 月)
- Postgres 8.4 在大约 2 年前(2014 年 7 月)停产(即不支持,不再更新)。
至于您在 8.4 上可以做的事情,它肯定更加有限...您可以尝试做一件事,它可能会给您带来一些改进(尽管不如 物化视图 可以在以后的版本中)将尝试将 JOIN
分解成几个不同的 temp table。这可能允许查询规划器更好地推理每个单独的查询并比整体更快地处理部分,尽管这肯定是一个高度可变的事情,取决于数据、服务器调整和其他因素。
此外,尝试为 WHERE
中的 NOT EXISTS
表达式中的子 select 创建一个 temp table在查询之前使用它。
确保它(和其他临时 tables)有你可能需要的任何索引,这样它就可以尽可能多地进行索引扫描,而不是完整的索引扫描。请参阅 EXPLAIN
语句(PgAdmin 也提供了一个可视化的语句)以帮助了解查询规划器对要开始的查询的看法以及它在您分解时如何变化成组件。
我读了这个 postgressql...
做了一个新查询
select
'{\"nombre\":\"'||columna.attname||'\",\"tipo\":\"'|| tipo.typname
||'\",\"is_nullable\":\"'|| CASE WHEN columna.attnotnull THEN 'NO' ELSE 'SI' END||'\",\"is_pk\":\"'|| CASE WHEN pk.conname IS NULL THEN 'NO' ELSE 'SI' END
||'\",\"max_length\":\"'||COALESCE(character_maximum_length::varchar,'')||'\",\"FK_schema\":\"'||COALESCE(fk_schema.nspname,'')||'\",\"FK_tabla\":\"'||
COALESCE(fk_tabla.relname,'')||'\",\"FK_columna\":\"'||COALESCE(fk_columna.attname,'')||
'\"}' as id, columna.attname,columna.*
from pg_catalog.pg_namespace pgschema
inner join pg_catalog.pg_class clase ON PGSCHEMA.OID = clase.relnamespace and relname ='".$_POST['tabla']."'
inner join pg_catalog.pg_attribute columna ON ATtReLID = clase.oid
inner join pg_catalog.pg_type tipo ON tipo.oid = columna.atttypid
inner join information_schema.columns ON table_schema = pgschema.nspname AND table_name = clase.relname AND column_name = columna.attname
left join pg_catalog.pg_constraint pk ON pk.contype = 'p' AND PK.conrelid = clase.oid and array[columna.attnum] && pk.conkey
left join pg_catalog.pg_constraint Fk ON FK.contype = 'f' AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_class Fk_tabla ON FK.confrelid = fk_tabla.oid -- AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_namespace Fk_schema ON FK_tabla.relnamespace = fk_schema.oid
left join pg_catalog.pg_attribute fk_columna ON fk_tabla.OID = fk_columna.attrelid AND ARRAY[fk_columna.attnum] && fk.confkey
where pgschema.nspname = '".$_POST['schema']."' and columna.attnum > 0;
不到1秒。
我有一个问题:
SELECT
'{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
'\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type IN ('FOREIGN KEY')
WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
SELECT q.column_name FROM information_schema.constraint_column_usage q
inner join information_schema.table_constraints USING(constraint_name )
WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name
AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY')
ORDER BY c.ordinal_position;
它有效,但在具有 97 个模式的数据库中,运行 需要整整一分钟。
我该如何解决这个问题?
没有这一行:
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type = 'FOREIGN KEY'
不到1秒。
由于查询似乎都是关于 Postgres 关于您的数据库的元数据,因此大概这些查询不会像 table 中的一组常规 table 那样频繁更改查询会。
因此,我建议您使用 materialized view 设置为在您认为接受 table 的某个时间间隔内刷新数据货币。也许是每小时或每天——这完全取决于此数据更改的频率,以及您需要它的最新程度(这也是相互关联的)。
所以你会这样做:
CREATE MATERIALIZED VIEW mat_view AS
SELECT
'{\"nombre\":\"'||c.column_name||'\",\"type\":\"'|| c.data_type
||'\",\"is_nullable\":\"'|| c.is_nullable||'\",\"is_pk\":\"'|| CASE WHEN constraint_type = 'PRIMARY KEY' THEN 'SI' ELSE 'NO' END
||'\",\"max_length\":\"'||COALESCE(c.character_maximum_length::VARCHAR,'')||'\",\"FK_schema\":\"'||COALESCE(ccu.table_schema,'')||'\",\"FK_tabla\":\"'||
COALESCE(ccu.table_name,'')||'\",\"FK_columna\":\"'||COALESCE(ccu.column_name,'')||
'\"}' as id, c.column_name
FROM information_schema.columns AS c
LEFT JOIN information_schema.key_column_usage i ON I.table_name=c.table_name AND I.table_schema=c.table_schema AND c.column_name = I.column_name
LEFT JOIN information_schema.table_constraints tc ON TC.constraint_name = I.CONSTRAINT_NAME AND constraint_type IN ('FOREIGN KEY','PRIMARY KEY')
LEFT JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
AND constraint_type IN ('FOREIGN KEY')
WHERE c.table_schema = '".$_POST['schema']."' AND c.table_name='".$_POST['tabla']."'
AND NOT EXISTS (
SELECT q.column_name FROM information_schema.constraint_column_usage q
inner join information_schema.table_constraints USING(constraint_name )
WHERE c.table_schema = q.table_schema AND q.table_name=c.table_name AND q.column_name = c.column_name
AND tc.constraint_name > q.constraint_name AND TC.constraint_type <> 'PRIMARY KEY')
ORDER BY c.ordinal_position;
这实际上会在第一次 运行 时创建此视图结果的快照。然后可以通过REFRESH MATERIALIZED VIEW mat_view;
您可能还希望使用 CONCURRENTLY
选项,它可以防止阻塞,但可能需要更长的时间。
可以将 REFRESH MATERIALIZED VIEW
命令放在 cron 上,以确保它在您希望的时间间隔内完成。
注意: 实体化视图是 Postgres 9.3 中的一项新功能。 CONCURRENTLY
已添加到 Postgres 9.4.
根据 OP 的评论进行编辑:
关于 Postgres 8.4,这听起来似乎超出了您的控制范围,但您可能想用这些信息游说服务器管理员和 DBA,看看他们是否会改变主意:
- Postgres 8.4 发布将近 7 年前(2009 年 7 月)
- Postgres 8.4 在大约 2 年前(2014 年 7 月)停产(即不支持,不再更新)。
至于您在 8.4 上可以做的事情,它肯定更加有限...您可以尝试做一件事,它可能会给您带来一些改进(尽管不如 物化视图 可以在以后的版本中)将尝试将 JOIN
分解成几个不同的 temp table。这可能允许查询规划器更好地推理每个单独的查询并比整体更快地处理部分,尽管这肯定是一个高度可变的事情,取决于数据、服务器调整和其他因素。
此外,尝试为 WHERE
中的 NOT EXISTS
表达式中的子 select 创建一个 temp table在查询之前使用它。
确保它(和其他临时 tables)有你可能需要的任何索引,这样它就可以尽可能多地进行索引扫描,而不是完整的索引扫描。请参阅 EXPLAIN
语句(PgAdmin 也提供了一个可视化的语句)以帮助了解查询规划器对要开始的查询的看法以及它在您分解时如何变化成组件。
我读了这个 postgressql...
做了一个新查询 select
'{\"nombre\":\"'||columna.attname||'\",\"tipo\":\"'|| tipo.typname
||'\",\"is_nullable\":\"'|| CASE WHEN columna.attnotnull THEN 'NO' ELSE 'SI' END||'\",\"is_pk\":\"'|| CASE WHEN pk.conname IS NULL THEN 'NO' ELSE 'SI' END
||'\",\"max_length\":\"'||COALESCE(character_maximum_length::varchar,'')||'\",\"FK_schema\":\"'||COALESCE(fk_schema.nspname,'')||'\",\"FK_tabla\":\"'||
COALESCE(fk_tabla.relname,'')||'\",\"FK_columna\":\"'||COALESCE(fk_columna.attname,'')||
'\"}' as id, columna.attname,columna.*
from pg_catalog.pg_namespace pgschema
inner join pg_catalog.pg_class clase ON PGSCHEMA.OID = clase.relnamespace and relname ='".$_POST['tabla']."'
inner join pg_catalog.pg_attribute columna ON ATtReLID = clase.oid
inner join pg_catalog.pg_type tipo ON tipo.oid = columna.atttypid
inner join information_schema.columns ON table_schema = pgschema.nspname AND table_name = clase.relname AND column_name = columna.attname
left join pg_catalog.pg_constraint pk ON pk.contype = 'p' AND PK.conrelid = clase.oid and array[columna.attnum] && pk.conkey
left join pg_catalog.pg_constraint Fk ON FK.contype = 'f' AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_class Fk_tabla ON FK.confrelid = fk_tabla.oid -- AND FK.conrelid = clase.oid and array[columna.attnum] && fk.conkey
left join pg_catalog.pg_namespace Fk_schema ON FK_tabla.relnamespace = fk_schema.oid
left join pg_catalog.pg_attribute fk_columna ON fk_tabla.OID = fk_columna.attrelid AND ARRAY[fk_columna.attnum] && fk.confkey
where pgschema.nspname = '".$_POST['schema']."' and columna.attnum > 0;
不到1秒。