热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Postgres性能和并发

参考文档:PostgreSQL9AdministrationCookbook(第二版)中文版第十章性能和并发寻找执行缓慢的SQL语句&#

参考文档: PostgreSQL 9 Administration Cookbook (第二版)中文版 第十章 性能和并发

寻找执行缓慢的SQL语句(设置有点类似mysql的慢查询),将该参数修改为10秒,日志中会记录查询超过10秒的语句

#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 millisecondspostgres=# show log_min_duration_statement;log_min_duration_statement
-----------------------------1
(1 row)postgres=# log_min_duration_statement = 10000postgres=# show log_min_duration_statement;log_min_duration_statement
----------------------------10s
(1 row)postgres=#

-- 或者通过pg_stat_activity视图来查询

select now()-query_start as running_for, query from pg_stat_activity order by 1 desc limit 5;
select now()-query_start as running_for, current_query from pg_stat_activity order where current_query != '' by 1 desc limit 5;

-- 或者反复执行以下查询,如果runing_for保持较大的数值,则可能耗费了大量的资源的查询.或者使用shell来处理

select now()-query_start as running_for, query from pg_stat_activity where state = 'active';while psql -qt -c "select current_query, now()-query_start as running_for
from pg_stat_activity" >>query_stats.txt ; do sleep 1; done -- 其中q是安静模式,A未对齐的格式,t是html。具体可以查看help

--另外可以通过pg_stat_user_tables和pg_statio_user_tables视图来找到可疑的信息

在pg_stat_user_tables中,seq_tup_read的快速增长表示有很多顺序扫描发生。seq_tup_read的快速增长表示有很多顺序扫描发生,seq_tup_read与seq_scan
的比率表示每次顺序扫描(seqscan)读取的元组数  
在pg_statio_user_tables中,heap_blks_hit和idx_blks_read字段,提供了有多少数据是从共享池被发现的(heap_blks_hit),有多少数据是从磁盘读取的(idx_blks_read). 如果没有足够的数据被填充到共享缓冲区中,则会发生大量的相同数据被重复读取的情况,然后引起性能问题。具体表现为在查询的前后,pg_stat*中的heap_blks_read、idx_blks_read和toast_blks_read有很大的变化。

-- 也可以通过安装扩展pgstatslog,通过日志来收集一些信息 (pg中默认没有这个扩展,需要额外安装,略)

-- 研究是什么样sql变慢
运行带explain analyze的查询,确认完成这个查询需要多少数据

mydb=# explain analyze select count(*) from t;QUERY PLAN
-----------------------------------------------------------------------------------------------------Aggregate (cost=38.25..38.26 rows=1 width=8) (actual time=0.005..0.005 rows=1 loops=1)-> Seq Scan on t (cost=0.00..32.60 rows=2260 width=0) (actual time=0.002..0.002 rows=0 loops=1)Planning time: 0.052 msExecution time: 0.060 ms
(4 rows)mydb=#

通过空闲系统中,对比该语句被执行前后的输出,看有多少行被处理,以及多少数据被访问。

select
s.relid, s.schemaname, s.relname,
seq_scan,seq_tup_read,
idx_scan,idx_tup_fetch,
heap_blks_read,heap_blks_hit,
idx_blks_read,idx_blks_hit,
toast_blks_read,toast_blks_hit
from pg_stat_user_tables s
join pg_statio_user_tables sio on s.relid = sio.relid
where s.schemaname = 'public' and s.relname = 't'

例如,得到一个百万行表的最后三行记录

SELECT * FROM EVENTS ORDER BY ID DESC LIMIT 3;

或者,使用索引,对比索引前后的情况,发现执行时间有明显的改善,

create table events(id serial);
insert into events select generate_series(1,1000000);
explain analyse SELECT * FROM EVENTS ORDER BY ID DESC LIMIT 3;
create index events_id_ndx on events(id);
explain analyse SELECT * FROM EVENTS ORDER BY ID DESC LIMIT 3;mydb=# explain analyse SELECT * FROM EVENTS ORDER BY ID DESC LIMIT 3;QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------Limit (cost=30292.79..30292.79 rows=3 width=4) (actual time=187.357..187.360 rows=3 loops=1)-> Sort (cost=30292.79..33113.72 rows=1128375 width=4) (actual time=187.355..187.356 rows=3 loops=1)Sort Key: id DESCSort Method: top-N heapsort Memory: 25kB-> Seq Scan on events (cost=0.00..15708.75 rows=1128375 width=4) (actual time=0.018..89.370 rows=1000000 loops=1)Planning time: 0.149 msExecution time: 187.383 ms
(7 rows)mydb=# mydb=# explain analyse SELECT * FROM EVENTS ORDER BY ID DESC LIMIT 3;QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------
--------------Limit (cost=0.42..0.56 rows=3 width=4) (actual time=0.054..0.055 rows=3 loops=1)-> Index Only Scan Backward using events_id_ndx on events (cost=0.42..43680.43 rows=1000000 width=4) (actual time=0.053..0.054 ro
ws=3 loops=1)Heap Fetches: 3Planning time: 0.391 msExecution time: 0.082 ms
(5 rows)mydb=#

-- 查看锁的问题
select * from pg_locks where not granted;   -- 如果锁已授予则为真,如果锁被等待则为假 。如果返回空或者只有1行或者两行。则说明锁没有问题
一个行的granted为真表示一个被指定进程持有的锁。为假表示该进程当前正在等待获取这个
锁,这意味着至少一个其他进程正持有或等待同一个可锁对象上的一个冲突锁。该等待进程
将一直休眠直到其他锁被释放(或者一个死锁状态被检测到)。单个进程在同一时间只能等
待最多一个锁。

-- 查看哪些查询正在等待其他查询

select
a1.query as blocking_query,
a2.query as waiting_query,
t.schemaname ||'.'||t.relname as locked_table
from pg_stat_activity a1
join pg_locks p1 on a1.pid = p1.pid and p1.granted
join pg_locks p2 on p1.relation = p2.relation and not p2.granted
join pg_stat_activity a2 on a2.pid = p2.pid
join pg_stat_all_tables t on p1.relation = t.relid;

-- 减少返回的行 。使用limit和offset等

SELECT title, ts_rank_cd(body_tsv, query, 20) AS text_rank
FROM articles, plainto_tsquery('spicy potatoes') AS query
WHERE body_tsv @@ query
ORDER BY rank DESC
LIMIT 20
;SELECT title, ts_rank_cd(body_tsv, query, 20) AS text_rank
FROM articles, plainto_tsquery('spicy potatoes') AS query
WHERE body_tsv @@ query
ORDER BY rank DESC, articles.id
OFFSET 20 LIMIT 20
;

-- 使用临时表

BEGIN;
CREATE TEMPORARY TABLE nlqp_temp ON COMMIT DROP
AS
SELECT
spoint_id,
extract('quarter' from sale_time) as sale_quarter,
sum(sale_price) - sum(cost) AS profit,
count(*) AS nr_of_sales
FROM sale s
JOIN item_in_wh iw ON s.item_in_wh_id=iw.id
JOIN item i ON iw.item_id = i.id
JOIN salespoint sp ON s.spoint_id = sp.id
JOIN location sploc ON sp.loc_id = sploc.id
JOIN warehouse wh ON iw.whouse_id = wh.id
JOIN location whloc ON wh.loc_id = whloc.id
WHERE sale_time >= '2009-01-01'
AND sale_time <&#39;2010-01-01&#39;
AND sploc.id !&#61; whloc.id
GROUP BY 1,2
;SELECT shop.sp_name AS shop_name,
q1_NLP.profit as q1_profit,
q2_NLP.profit as q2_profit,
q3_NLP.profit as q3_profit,
q4_NLP.profit as q4_profit,
year_NLP.profit as year_profit
FROM (SELECT * FROM salespoint ORDER BY sp_name) AS shop
LEFT JOIN nlqp_temp AS q1_NLP
ON shop.id &#61; Q1_NLP.spoint_id AND q1_NLP.sale_quarter &#61; 1
LEFT JOIN nlqp_temp AS q2_NLP
ON shop.id &#61; Q2_NLP.spoint_id AND q2_NLP.sale_quarter &#61; 2
LEFT JOIN nlqp_temp AS q3_NLP
ON shop.id &#61; Q3_NLP.spoint_id AND q3_NLP.sale_quarter &#61; 3
LEFT JOIN nlqp_temp AS q4_NLP
ON shop.id &#61; Q4_NLP.spoint_id AND q4_NLP.sale_quarter &#61; 4
LEFT JOIN (
select spoint_id, sum(profit) AS profit FROM nlqp_temp GROUP
BY 1
) AS year_NLP
ON shop.id &#61; year_NLP.spoint_id
ORDER BY 1
;
COMMIT; -- 到这里&#xff0c;临时表消失

-- 使用物化视图

create materialized view nlqp_temp as select * from xxx where xxx ;

--加速查询的执行&#xff0c;而无须重写
第一步&#xff0c;从为优化器提供更好的信息开始。如果explain analyze表明估计值和实际值相差很大&#xff0c;则要告诉pg收集更细粒度的统计信息

mydb&#61;# show default_statistics_target; default_statistics_target
---------------------------100
(1 row)mydb&#61;#

针对单个db设置

alter database mydb set default_statistics_target &#61; 200; -- 该参数&#xff0c;设置后&#xff0c;需要重新连接数据库才生效
mydb&#61;# show default_statistics_target;default_statistics_target
---------------------------100
(1 row)mydb&#61;# \c postgres
You are now connected to database "postgres" as user "postgres".
postgres&#61;# \c mydb
You are now connected to database "mydb" as user "postgres".
mydb&#61;# show default_statistics_target;default_statistics_target
---------------------------200
(1 row)mydb&#61;#

如果设置的过高&#xff0c;则会影响analyze的速度 &#xff0c;pg提供了一个更细粒度的&#xff0c;通过字段来设置

alter table mytable alter col_with_bad_stats set statistics 500;

-- postgres的直接索引扫描&#xff08;index-only scan&#xff09;&#xff0c;这个功能也就是覆盖索引&#xff08;covering index &#xff09;

CREATE INDEX t1_a_b_ndx ON t1(a,b); -- 在t1的字段a上查询数据&#xff0c;然后在字段b上排序。创建索引&#xff0c;一次性实现
CREATE INDEX t1_proc_ndx ON t1(i1) WHERE needs_processing &#61; TRUE; -- pg的条件所引
SELECT id, ... WHERE needs_processing;

-- 在频繁更新的情况下&#xff0c;设置表的填充率
如果只是经常更新某些表&#xff0c;并且可以通过调整查询语句来使更新操作不会影响索引字段&#xff0c;则可以设置表的fillfactor(填充率)为一个比默认值100低的值来触发PG启用堆内元组(heap-only tuples,HOT)更新&#xff0c;这比普通更新快一个数量级。HOT更新不仅可以避免索引项&#xff0c;还可以在页内执行快速的微型vacuum,从而为新行腾出空间。
ALTER TABLE t1 SET (fillfactor &#61; 70);  -- 这个命令告诉PG在插入表t1时候&#xff0c;每个页填充70%&#xff0c;剩余的30%用于页内&#xff08;HOT&#xff09;更新。

-- 为什么一个查询不用索引 &#xff08;下面的例子中&#xff0c;pg认为&#xff0c;使用顺序扫描更好一些。&#xff09;

create table itable(id int primary key );
insert into itable select generate_series(1,10000);
analyse;
explain analyse select count(*) from itable where id > 500;
set enable_seqscan to false;
explain analyse select count(*) from itable where id > 500;mydb&#61;# explain analyse select count(*) from itable where id > 500;QUERY PLAN ---------------------------------------------------------------------------------------------------------
-----Aggregate (cost&#61;193.75..193.76 rows&#61;1 width&#61;8) (actual time&#61;1.719..1.719 rows&#61;1 loops&#61;1)-> Seq Scan on itable (cost&#61;0.00..170.00 rows&#61;9500 width&#61;0) (actual time&#61;0.045..1.085 rows&#61;9500 loop
s&#61;1)Filter: (id > 500)Rows Removed by Filter: 500Planning time: 0.382 msExecution time: 1.764 ms
(6 rows)mydb&#61;# set enable_seqscan to false; -- 允许或禁止查询规划器使用顺序扫描计划类型。它不可能完全禁止顺序扫描&#xff0c;但是关闭
这个变量将使得规划器尽可能优先使用其他方法。默认值是on。
SET
mydb&#61;# explain analyse select count(*) from itable where id > 500;QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------
-Aggregate (cost&#61;352.29..352.30 rows&#61;1 width&#61;8) (actual time&#61;2.210..2.212 rows&#61;1 loops&#61;1)-> Index Only Scan using itable_pkey on itable (cost&#61;0.29..328.54 rows&#61;9500 width&#61;0) (actual time&#61;0.041..1.563 rows&#61;9500 loops&#61;1)Index Cond: (id > 500)Heap Fetches: 9500Planning time: 0.085 msExecution time: 2.330 ms
(6 rows)mydb&#61;#

-- 强制让一个查询使用索引

set enable_seqscan to false;
create table table_with_no_index(id int);
set enable_seqscan to false;
explain select * from table_with_no_index where id > 10;
create index table_with_no_index_now_has_one on table_with_no_index(id);
explain select * from table_with_no_index where id > 10;
set enable_seqscan to true;
explain select * from table_with_no_index where id > 10;--random_page_cost &#xff0c;将该值设置为较低&#xff0c;使PG在大部分场景下更加倾向于索引扫描。

-- 使用乐观锁 。通过加锁select... for update转换成乐观锁&#xff0c;可以获得一些性能提升。乐观锁假定其他人不更新相同的记录。并在更新时检查&#xff0c;而不是在客户端上需要处理信息时锁定记录。

BEGIN;
SELECT * FROM ACCOUNTS WHERE HOLDER_NAME &#61;&#39;BOB&#39; FOR UPDATE;

UPDATE ACCOUNTS SET BALANCE &#61; 42.00 WHERE HOLDER_NAME &#61;&#39;BOB&#39;;
COMMIT;

-- 可以重写上面的语句&#xff0c;为以下的方式

BEGIN;
SELECT A.*, (A.*::text) AS OLDACCINFO
FROM ACCOUNTS A WHERE HOLDER_NAME &#61;&#39;BOB&#39;;

UPDATE ACCOUNTS SET BALANCE &#61; 42.00
WHERE HOLDER_NAME &#61;&#39;BOB&#39;
AND (A.*::text) &#61; ;
COMMIT;

-- 将以上事务用数据库中的一个函数来代替

CREATE OR REPLACE FUNCTION consume_balance (
i_username text, i_amount numeric(10,2), max_credit numeric(10,2),
OUT success boolean, OUT remaining_balance numeric(10,2)) AS
$$
BEGIN
UPDATE accounts SET balance &#61; balance - i_amount
WHERE username &#61; i_username
AND balance - i_amount > - max_credit
RETURNING balance
INTO remaining_balance;
IF NOT FOUND THEN
success :&#61; FALSE;
SELECT balance
FROM accounts
WHERE username &#61; i_username
INTO remaining_balance;
ELSE
success :&#61; TRUE;
END IF;
END;
$$ LANGUAGE plpgsql;

-- 在客户端调用函数

SELECT * FROM consume_balance (&#39;bob&#39;, 7, 0);

END


推荐阅读
  • 本文介绍了在MySQL8.0中如何查看性能并解析SQL执行顺序。首先介绍了查询性能工具的开启方法,然后详细解析了SQL执行顺序中的每个步骤,包括from、on、join、where、group by、having、select distinct、union、order by和limit。同时还介绍了虚拟表的概念和生成过程。通过本文的解析,读者可以更好地理解MySQL8.0中的性能查看和SQL执行顺序。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • MySQL外键1对多问题的解决方法及实例
    本文介绍了解决MySQL外键1对多问题的方法,通过准备数据、创建表和设置外键关联等步骤,实现了用户分组和插入数据的功能。详细介绍了数据准备的过程和外键关联的设置,以及插入数据的示例。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
author-avatar
林志群晴梦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有