PostgreSQL 性能优化概述(译)
概述
Checking active queries and processes
SELECT * FROM pg_stat_activity \gx
注意:使用\gx行终止序列代替传统的分号(;)告诉PostgreSQL对当前查询使用扩展的输出模式。这将垂直而不是水平地显示每条记录的列和相关值,这在某些情况下可以提高可读性。
- state:进程的当前状态。active表示当前正在执行。其他状态包括等待新客户端命令的进程处于空闲状态,在事务上下文中等待命令的进程处于事务空闲状态,在语句导致错误的事务中处于事务空闲状态(中止)。
- query:最近执行的查询。对于活动进程,这将是当前正在执行的查询。
- usename:与进程关联的用户名。
- application_name:连接到进程的应用程序的名称。
- datname:用户所连接数据库的名称。
- wait_event:进程正在等待的事件的名称(如果有)。如果进程处于活动状态并且存在wait_event,则表示查询当前被系统的其他部分阻塞。
- wait_event_type:进程正在等待的事件的类别。
- pid:进程的进程号。
- query_start:对于活动查询,当前查询开始的时间戳。
- xact_start:如果进程正在执行事务,则当前事务开始的时间戳。
SELECT age(clock_timestamp(), query_start), usename, datname, query FROM pg_stat_activity WHERE state != 'idle' AND query NOT ILIKE '%pg_stat_activity%' ORDER BY age desc;
这将显示非空闲查询的执行时间、用户名、数据库和查询语句。我们将结果从运行时间最长的倒序排序,并从结果中排除此特定查询。
ELECT usename, datname, query, wait_event_type, wait_event FROM pg_stat_activity WHERE state != 'idle' AND query wait_event != ''
Check other system statistics
Viewing database statistics
SELECT * FROM pg_stat_database \gx . . . [ RECORD 2 ]------------+------------------------------ datid | 13921 datname | postgres numbackends | 1 xact_commit | 266 xact_rollback | 9 blks_read | 229 blks_hit | 11263 tup_returned | 118708 tup_fetched | 3563 tup_inserted | 0 tup_updated | 0 tup_deleted | 0 conflicts | 0 temp_files | 0 temp_bytes | 0 deadlocks | 0 checksum_failures | checksum_last_failure | blk_read_time | 0 blk_write_time | 0 session_time | 5303626.534 active_time | 200.906 idle_in_transaction_time | 0 sessions | 2 sessions_abandoned | 0 sessions_fatal | 0 sessions_killed | 0 stats_reset | 2022-11-06 20:20:18.279798+01 . . .
- blks_read:在数据库中读取的磁盘块数量。
- blks_hit:在缓冲区缓存中找到磁盘块的次数(避免从磁盘读取缓慢)。
- xact_commit:提交的事务数。
- xact_rollback:回滚的事务数。
SELECT datname, 100 * blks_hit / (blks_hit + blks_read) as cache_hit_ratio FROM pg_stat_database WHERE (blks_hit + blks_read) > 0; datname | cache_hit_ratio -----------+----------------- | 99 postgres | 98 template1 | 99 (3 rows)
Viewing table statistics
SELECT * FROM pg_stat_all_tables \gx . . . [ RECORD 104 ]-----+------------------------ relid | 1262 schemaname | pg_catalog relname | pg_database seq_scan | 5168 seq_tup_read | 20655 idx_scan | 20539 idx_tup_fetch | 20539 n_tup_ins | 0 n_tup_upd | 0 n_tup_del | 0 n_tup_hot_upd | 0 n_live_tup | 0 n_dead_tup | 0 n_mod_since_analyze | 0 n_ins_since_vacuum | 0 last_vacuum | last_autovacuum | last_analyze | last_autoanalyze | vacuum_count | 0 autovacuum_count | 0 analyze_count | 0 autoanalyze_count | 0
这些视图中一些有趣的列:
- seq_scan:在表上运行的顺序扫描次数。
- seq_tup_read:顺序扫描返回的行数。
- idx_scan:对表运行索引扫描的次数。
- idx_tup_fetch:通过索引检索的行数。
Viewing index hits
SELECT * FROM pg_stat_all_indexes \gx . . . [ RECORD 6 ]-+---------------------------------------------- relid | 1249 indexrelid | 2659 schemaname | pg_catalog relname | pg_attribute indexrelname | pg_attribute_relid_attnum_index idx_scan | 822 idx_tup_read | 1670 idx_tup_fetch | 1670 . . .
Viewing lock information
SELECT * FROM pg_locks \gx -[ RECORD 1 ]------+---------------- locktype | relation database | 13921 relation | 12290 page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | 3/3920 pid | 967262 mode | AccessShareLock granted | t fastpath | t waitstart | -[ RECORD 2 ]------+---------------- locktype | virtualxid database | relation | page | tuple | virtualxid | 3/3920 transactionid | classid | objid | objsubid | virtualtransaction | 3/3920 pid | 967262 mode | ExclusiveLock granted | t fastpath | t waitstart |
输出将提供有关PostgreSQL中所有锁的信息。这可以帮助您诊断当不同的进程请求对相同对象的控制时可能发生的争用问题。
- locktype:可锁定对象的类型
- database/relation/page/tuple:锁定项的对象ID。对于database和relations,它们可以在pg_database和pg_class中交叉引用(原文是 cross-referenced ,意思是不同的数据行可能会共享一些对象)。
- mode:执行或请求的锁模式。
- granted:一个布尔值,表示锁是否被授予。
Enable slow query logging
Check if PostgreSQL is already logging slow queries
SELECT * FROM pg_settings WHERE name = 'log_min_duration_statement'\gx [ RECORD 1 ]---+--------------------------------------------------------------------------- name | log_min_duration_statement setting | -1 unit | ms category | Reporting and Logging / When to Log short_desc | Sets the minimum execution time above which all statements will be logged. extra_desc | Zero prints all queries. -1 turns this feature off. context | superuser vartype | integer source | default min_val | -1 max_val | 2147483647 enumvals | boot_val | -1 reset_val | -1 sourcefile | sourceline | pending_restart | f
如果您检查short_desc和extra_desc列的值,您将发现允许我们评估当前是否启用了慢查询日志记录的信息。我们可以看到,当前没有启用慢速查询日志记录,因为setting列当前被设置为-1。
Configure PostgreSQL to log slow queries
Logging slow queries globally
SHOW config_file; config_file ----------------------------------------- /etc/postgresql/14/main/postgresql.conf (1 row)
在文件中,搜索log_min_duration_statement设置。如果上面的示例输出值是从配置文件中读取的,那么它将被设置为-1,表示当前禁用了该功能。还有一些其他相关的设置,你可以根据自己的需要进行调整:
. . . # Query logging configuration #log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements # and their durations, > 0 logs only # statements running at least this number # of milliseconds #log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements # and their durations, > 0 logs only a sample of # statements running at least this number # of milliseconds; # sample fraction is determined by log_statement_sample_rate #log_statement_sample_rate = 1.0 # fraction of logged statements exceeding # log_min_duration_sample to be logged; # 1.0 logs all such statements, 0.0 never logs #log_transaction_sample_rate = 0.0 # fraction of transactions whose statements # are logged regardless of their duration; 1.0 logs all # statements from all transactions, 0.0 never logs . . .
目前,log_min_duration_statement设置被注释掉,其当前值设置为-1以表示默认值。其他设置在文件中有很好的注释,允许您对超过最小值的语句进行采样,而不是记录所有语句。最后一个设置还允许对事务中出现的语句进行抽样。
例如,我们可以将它设置为5秒来记录任何需要超过5秒才能完成的语句:log_min_duration_statement = 5s
保存文件后,你可以在PostgreSQL中重新加载你的PostgreSQL服务器,输入:
SELECT pg_reload_conf()
您可以通过再次检查当前值来验证服务器是否正在使用您的新设置:
SELECT * FROM pg_settings WHERE name = 'log_min_duration_statement'\gx [ RECORD 1 ]---+--------------------------------------------------------------------------- name | log_min_duration_statement setting | 5000 unit | ms category | Reporting and Logging / When to Log short_desc | Sets the minimum execution time above which all statements will be logged. extra_desc | Zero prints all queries. -1 turns this feature off. context | superuser vartype | integer source | configuration file min_val | -1 max_val | 2147483647 enumvals | boot_val | -1 reset_val | 5000 sourcefile | /etc/postgresql/14/main/postgresql.conf sourceline | 506 pending_restart | f
现在,设置字段被设置为5000,单位字段被设置为ms,这表明我们设置的5秒已被转换为5000毫秒并应用。sourcefile行还确认正在从我们修改过的配置文件中读取该值
Logging slow queries per database
要打开单个数据库的慢速查询日志记录,使用ALTER database命令:
ALTER DATABASE helloprisma SET 'log_min_duration_statement' = 2000; ALTER DATABASE
\drds List of settings Role | Database | Settings -----+-------------+------------------------------- | helloprisma | log_min_duration_statement=2000 (1 row)
SELECT * FROM pg_settings WHERE name = 'log_min_duration_statement'\gx [ RECORD 1 ]---+--------------------------------------------------------------------------- name | log_min_duration_statement setting | 5000 unit | ms category | Reporting and Logging / When to Log short_desc | Sets the minimum execution time above which all statements will be logged. extra_desc | Zero prints all queries. -1 turns this feature off. context | superuser vartype | integer source | configuration file min_val | -1 max_val | 2147483647 enumvals | boot_val | -1 reset_val | 5000 sourcefile | /etc/postgresql/14/main/postgresql.conf sourceline | 506 pending_restart | f
Testing slow query logging
通过发出超过最小日志记录持续时间的语句来测试设置:
SELECT pg_sleep(10); pg_sleep --------- (1 row)
检查日志,您应该会发现指示长时间运行查询发生的语句:
2022-11-11 17:58:04.719 CET [1121088] postgres@postgres STATEMENT: select sleep(10); 2022-11-11 17:58:42.635 CET [1121088] postgres@postgres LOG: duration: 10017.171 ms statement: select pg_sleep(10);
由于我们对全局限制和特定表有不同的阈值,因此可以通过使用触发其中一个而不触发另一个的查询时间来测试是否正确应用了这两个阈值。
例如,我们可以连接到具有较低阈值的数据库并休眠4秒,这应该会触发日志行:
\c helloprisma SELECT pg_sleep(4);
我们的日志显示:
2022-11-13 14:46:07.361 CET [1252789] postgres@helloprisma STATEMENT: alter database helloprisma set log_min_duration_statement=2s; 2022-11-13 14:53:05.027 CET [1309069] postgres@helloprisma LOG: duration: 4022.546 ms statement: select pg_sleep(4);
现在,我们可以切换到只受全局设置影响的另一个数据库。同样的sleep语句不应该触发日志行:
\c postgres SELECT pg_sleep(4);
没有新的日志会被记录下来
总结
在本文中,我们介绍了如何查看和理解PostgreSQL提供的一些性能信息。查看这些信息可以让您深入了解系统资源、查询模式和配置设置中的不同瓶颈。当您遇到性能变慢时,您可以检查PostgreSQL提供的信息来开始调查问题的所在。
我们还讨论了如何使用慢速查询日志来精确查明哪些查询占用了系统资源,并且执行时间比预期长。记录这些数据并评估结果日志可以帮助您确定哪些地方可能需要额外的索引、不同的查询结构或更有效的查询设计。
了解如何识别这些昂贵的操作是运行功能更强大的数据库支持的应用程序的第一步。