一个优化的SQL:
SELECT order_date,
order_source,
SUM(commodity_num) num,
SUM(actual_charge) charge
FROM (
SELECT to_char(oc.create_date, 'yyyyMMdd') AS order_date,
(CASE
WHEN oo.event_type = 'ONLINE_COMMODITY_ORDER' THEN
'线上'
ELSE
'线下'
END) order_source,
oc.commodity_num,
oc.actual_charge actual_charge
FROM ord.ord_commodity_hb_2017 AS oc, ord.ord_order_hb_2017 AS oo
WHERE oc.order_id = oo.order_id
AND oc.op_type = 3 -- 3个值 ,3->5000 大概1/20的数据
AND oc.create_date BETWEEN '2017-02-05' AND '2017-12-07' -- 无用
AND oc.corp_org_id = 106 -- 无用
AND oo.trade_state = 11 -- 3个值 11 --> 71万行,一半数据
AND oo.event_type IN (values('ONLINE_COMMODITY_ORDER'),
('USER_CANCEL'),
('USER_COMMODITY_UPDATE')) -- 大概1/10 数据
ORDER BY oc.create_date -- 若是业务不强制,最好去掉排序,若是不能去掉,最好等过滤数据量到尽可能小时再排序
) T
GROUP BY order_date, order_source;
View Code
下面默认以postgresql为例:
1、排序:
1. 尽可能避免
2. 排序的数据量尽可能少,并保证在内存里完成排序。
(至于具体什么数据量能在内存中完成排序,不一样数据库有不一样的配置:
oracle是sort_area_size;
postgresql是work_mem (integer),单位是KB,默认值是4MB。
mysql是sort_buffer_size 注意:该参数对应的分配内存是每链接独占!
)
2、索引:
1. 过滤的数据量比较少,通常来讲<20%,应该走索引。20%-40% 可能走索引也可能不走索引。> 40% ,基本不走索引(会全表扫描)
2. 保证值的数据类型和字段数据类型要一直。
3. 对索引的字段进行计算时,必须在运算符右侧进行计算。也就是 to_char(oc.create_date, 'yyyyMMdd')是没用的
4. 表字段之间关联,尽可能给相关字段上添加索引。
5. 复合索引,听从最左前缀的原则,即最左优先。(单独右侧字段查询没有索引的)
3、链接查询方式:
一、hash join
放内存里进行关联。
适用于结果集比较大的状况。
好比都是200000数据
二、nest loop
从结果1 逐行取出,而后与结果集2进行匹配。
适用于两个结果集,其中一个数据量远大于另一个时。
结果集一:1000
结果集二:1000000
4、多表联查时:
在多表联查时,须要考虑链接顺序问题。
一、当postgresql中进行查询时,若是多表是经过逗号,而不是join链接,那么链接顺序是多表的笛卡尔积中取最优的。若是有太多输入的表, PostgreSQL规划器将从穷举搜索切换为基因几率搜索,以减小可能性数目(样本空间)。基因搜索花的时间少, 可是并不必定能找到最好的规划。
二、对于JOIN,
LEFT JOIN / RIGHT JOIN 会必定程度上指定链接顺序,可是仍是会在某种程度上从新排列:
FULL JOIN 彻底强制链接顺序。
若是要强制规划器遵循准确的JOIN链接顺序,咱们能够把运行时参数join_collapse_limit设置为 1
5、PostgreSQL提供了一些性能调优的功能:
优化思路:
0、为每一个表执行 ANALYZE <table>。而后分析 EXPLAIN (ANALYZE,
BUFFERS) sql。
一、对于多表查询,查看每张表数据,而后改进链接顺序。
二、先查找那部分是重点语句,好比上面SQL,外面的嵌套层对于优化来讲没有意义,能够去掉。
三、查看语句中,where等条件子句,每一个字段能过滤的效率。找出可优化处。
好比oc.order_id = oo.order_id是关联条件,须要加索引
oc.op_type = 3 能过滤出1/20的数据,
oo.event_type IN (...) 能过滤出1/10的数据,
这两个是优化的重点,也就是实现确保op_type与event_type已经加了索引,其次确保索引用到了。
优化方案:
a) 总体优化:
一、使用EXPLAIN
EXPLAIN命令能够查看执行计划,这个方法是咱们最主要的调试工具。
二、及时更新执行计划中使用的统计信息
因为统计信息不是每次操做数据库都进行更新的,通常是在 VACUUM 、 ANALYZE 、 CREATE INDEX等DDL执行的时候会更新统计信息,
所以执行计划所用的统计信息颇有可能比较旧。 这样执行计划的分析结果可能偏差会变大。
如下是表tenk1的相关的一部分统计信息。
SELECT relname, relkind, reltuples, relpages
FROM pg_class
WHERE relname LIKE 'tenk1%';
relname | relkind | reltuples | relpages
----------------------+---------+-----------+----------
tenk1 | r | 10000 | 358
tenk1_hundred | i | 10000 | 30
tenk1_thous_tenthous | i | 10000 | 30
tenk1_unique1 | i | 10000 | 30
tenk1_unique2 | i | 10000 | 30
(5 rows)
其中 relkind是类型,r是自身表,i是索引index;reltuples是项目数;relpages是所占硬盘的块数。
估计成本经过
(磁盘页面读取【
relpages
】*seq_page_cost)+(行扫描【
reltuples
】*cpu_tuple_cost)计算。
默认状况下, seq_page_cost是1.0,cpu_tuple_cost是0.01。
名字 |
类型 |
描述 |
relpages |
int4 |
以页(大小为BLCKSZ)的此表在磁盘上的形式的大小。 它只是规划器用的一个近似值,是由VACUUM,ANALYZE 和几个 DDL 命令,好比CREATE INDEX更新。 |
reltuples |
float4 |
表中行的数目。只是规划器使用的一个估计值,由VACUUM,ANALYZE 和几个 DDL 命令,好比CREATE INDEX更新。 |
三、使用临时表(with)
对于数据量大,且没法有效优化时,可使用临时表来过滤数据,下降数据数量级。
四、对于会影响结果的分析,可使用 begin;...rollback;来回滚。
b) 查询优化:
一、明确用join来关联表,确保链接顺序
通常写法:SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id;
若是明确用join的话,执行时候执行计划相对容易控制一些。
例子:
SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
c) 插入更新优化
一、关闭自动提交(autocommit=false)
若是有多条数据库插入或更新等,最好关闭自动提交,这样能提升效率
二、屡次插入数据用copy命令更高效
咱们有的处理中要对同一张表执行不少次insert操做。这个时候咱们用copy命令更有效率。由于insert一次,其相关的index都要作一次,比较花费时间。
三、临时删除index【具体能够查看Navicat表数据生成sql的语句,就是先删再建的】
有时候咱们在备份和从新导入数据的时候,若是数据量很大的话,要好几个小时才能完成。这个时候能够先把index删除掉。导入后再建index。
四、外键关联的删除
若是表的有外键的话,每次操做都没去check外键整合性。所以比较慢。数据导入后再创建外键也是一种选择。
d) 修改参数:
选项 |
默认值 |
说明 |
是否优化 |
缘由 |
max_connections |
100 |
容许客户端链接的最大数目 |
否 |
由于在测试的过程当中,100个链接已经足够 |
fsync |
on |
强制把数据同步更新到磁盘 |
是 |
由于系统的IO压力很大,为了更好的测试其余配置的影响,把改参数改成off |
shared_buffers |
24MB |
决定有多少内存能够被PostgreSQL用于缓存数据(推荐内存的1/4) |
是 |
在IO压力很大的状况下,提升该值能够减小IO |
work_mem |
1MB |
使内部排序和一些复杂的查询都在这个buffer中完成 |
是 |
有助提升排序等操做的速度,而且减低IO |
effective_cache_size |
128MB |
优化器假设一个查询能够用的最大内存,和shared_buffers无关(推荐内存的1/2) |
是 |
设置稍大,优化器更倾向使用索引扫描而不是顺序扫描 |
maintenance_work_mem |
16MB |
这里定义的内存只是被VACUUM等耗费资源较多的命令调用时使用 |
是 |
把该值调大,能加快命令的执行 |
wal_buffer |
768kB |
日志缓存区的大小 |
是 |
能够下降IO,若是赶上比较多的并发短事务,应该和commit_delay一块儿用 |
checkpoint_segments |
3 |
设置wal log的最大数量数(一个log的大小为16M) |
是 |
默认的48M的缓存是一个严重的瓶颈,基本上都要设置为10以上 |
checkpoint_completion_target |
0.5 |
表示checkpoint的完成时间要在两个checkpoint间隔时间的N%内完成 |
是 |
能下降平均写入的开销 |
commit_delay |
0 |
事务提交后,日志写到wal log上到wal_buffer写入到磁盘的时间间隔。须要配合commit_sibling |
是 |
可以一次写入多个事务,减小IO,提升性能 |
commit_siblings |
5 |
设置触发commit_delay的并发事务数,根据并发事务多少来配置 |
是 |
减小IO,提升性能 |
autovacuum_naptime |
1min |
下一次vacuum任务的时间 |
是 |
提升这个间隔时间,使他不是太频繁 |
autovacuum_analyze_threshold |
50 |
与autovacuum_analyze_scale_factor配合使用,来决定是否analyze |
是 |
使analyze的频率符合实际 |
autovacuum_analyze_scale_factor |
0.1 |
当update,insert,delete的tuples数量超过autovacuum_analyze_scale_factor*table_size+autovacuum_analyze_threshold时,进行analyze。 |
是 |
使analyze的频率符合实际 |
下面介绍几个我认为重要的:
一、增长maintenance_work_mem参数大小
增长这个参数能够提高CREATE INDEX和ALTER TABLE ADD FOREIGN KEY的执行效率。
二、增长checkpoint_segments参数的大小
增长这个参数能够提高大量数据导入时候的速度。
三、设置archive_mode无效
这个参数设置为无效的时候,可以提高如下的操做的速度
・CREATE TABLE AS SELECT
・CREATE INDEX
・ALTER TABLE SET TABLESPACE
・CLUSTER等。
四、autovacuum相关参数
autovacuum:默认为on,表示是否开起autovacuum。默认开起。特别的,当须要冻结xid时,尽管此值为off,PG也会进行vacuum。
autovacuum_naptime
:下一次vacuum的时间,默认1min。 这个naptime会被vacuum launcher分配到每一个DB上。autovacuum_naptime/num of db。
log_autovacuum_min_duration:记录autovacuum动做到日志文件,当vacuum动做超过此值时。 “-1”表示不记录。“0”表示每次都记录。
autovacuum_max_workers:最大同时运行的worker数量,不包含launcher自己。
autovacuum_work_mem
:每一个worker可以使用的最大内存数。
autovacuum_vacuum_threshold
:默认50。与autovacuum_vacuum_scale_factor配合使用, autovacuum_vacuum_scale_factor默认值为20%。当update,delete的tuples数量超过autovacuum_vacuum_scale_factor*table_size+autovacuum_vacuum_threshold时,进行vacuum。若是要使vacuum工做勤奋点,则将此值改小。
autovacuum_analyze_threshold
:默认50。与autovacuum_analyze_scale_factor配合使用。
autovacuum_analyze_scale_factor
:
默认10%。当update,insert,delete的tuples数量超过autovacuum_analyze_scale_factor*table_size+autovacuum_analyze_threshold时,进行analyze。
autovacuum_freeze_max_age:
200 million。离下一次进行xid冻结的最大事务数。
autovacuum_multixact_freeze_max_age:400 million。离下一次进行xid冻结的最大事务数。
autovacuum_vacuum_cost_delay
:若是为-1,取vacuum_cost_delay值。
autovacuum_vacuum_cost_limit
:若是为-1,到vacuum_cost_limit的值,这个值是全部worker的累加值。