PostgreSQL实战-10-性能优化

影响数据库性能的因素:
    在硬件层面:
        CPU
        IO
        内存
        网络
    在软件层面:
        操作系统配置
        中间件配置
        数据库参数配置
        运行在数据库之上的查询和命令
        等
        ......
性能检测
系统监控

在进行系统性能优化时, 应当先着眼全局进行分析,再逐步深入到细节。

PostgreSQL 数据库的SQL 服务器应用通常分为
    OLTP
    数据仓库
    
数据库性能分析【硬件】:
    最先到达瓶颈的,通常是磁盘IO
        1.建议使用固定存储SSD
            如目前使用广泛的SATA SSD 和PCie SSD
            注意区分消费级SSD和企业级SSD
            
        2. 使用外部存储设备加载到服务器也是比较常见的,例如SAN (存储区域网络)和NAS (网络接入存储) 。
            使用SAN 设备时通过块接口访问,对服务器来说就像访问本地磁盘一样; 
            NAS 则是使用标准文件协议进行访问,例如NFS 等。
    CPU 也会经常成为性能瓶颈
        更高的CPU 主频可以提供更快的运算速度,更多的核心数则能大大提高并发能力
        注意:有一些服务器在BIOS 中可以设置CPU 的性能模式,可能的模式有高性能模式、普通模式和节能模式,在数据库服务器中,不建议使用节能模式。
            这种模式会在系统比较空闲的时候对CPU 主动降频,达到降温和节电的目的,但对于数据库来说,则会产生性能波动
    内存的使用对数据库系统非常重要:
        缓存
    网络:内存和网络通常比较充裕不会是瓶颈,但是需要密切监控
        
数据库性能分析【操作系统】:
    常用Linux 性能工具:top 、free 、vmstat 、iostat 、mpstat 、sar, pidstat 等。
        除了top 和free 外,其他工具均位于sysstat 包中
        ~]# yum install -y sysstat
        
        top:
            top 命令的输出被一行空行分为两部分,空行以上的信息为服务器状态的整体统计信息,空行以下部分为各个进程的状态信息。
            
            top - 12:01:03 up 93 days , 23 : 30 , 1 user, load average : 1.08, 1.09, 1.08
            Tasks : 1042 total , 3 running , 1039 sleeping , 0 stopped, 0 zombie
            Cpu (s): 5.3 us 1.6 sy , 0.0 ni , 92.5% id , 0.2 wa , 0.0 hi , 0.3 si , 0.0 st
            Mem : 330604220k total, 323235920k used, 7368300k free , 248996k buffers
            Swap: 67108860k total, Ok used , 67108860k free, 295053464k cached
            如果把这一部分输出翻译为可读语言,其内容如下所示:
            top -当前时间12:01: 03 , 系统已运行93 天23 小时30 分没有重启, 当前有1 个用户登录操作系统, 最近l 分钟、5分钟、15 分钟的系统负载分别是: 1.0 8 , 1.09, 1.08
            任务运行情况: 当前一共有1042 个进程, 3个正在运行, 1039 个在睡眠, 0个进程停止, 0 个僵尸进程
            CPU : 用户CPU 占用5.3 告, 内核CPU 占用1. 6 苦,特定优先级的进程CPU 占用0.0 号, 空闲CPU为92.5%, 因为IO 等待的CPU 占用0.2%, 硬中断CPU 占用0.0%, 软中段CPU占用0.3 %, 虚拟机盗取占用0.0%
            内存: 共有330604220k ,已使用323235920k ,可用7368300k ,b uffers 使用了248996k
            虚拟内存:共有67108860k , 已使用Ok , 可用67108860 k, cache使用了295053464k
            
            各个进程的状态信息区域的输出值所代表的含义是:
            口PID :进程id
            口USER :进程所有者
            口PR :进程优先级
            口NI :进程优先级的修正值
            口VIRT :进程使用的虚拟内存总量
            口RES :进程使用的物理内存大小
            口SHR :共享内存大小
            口S :进程状态。D=不可中断的睡眠状态【一般就是由于等待IO 而造成所谓的“非中断睡眠”,】 R=运行 S=睡眠 T=跟踪/停止 Z= 僵尸进程
            口%CPU :上次更新到现在的CPU 时间占用百分比
            口%MEM :进程使用的物理内存百分比
            口TIME +:进程使用的CPU 时间总计,单位1/100 秒
            口COMMAND :进程运行的命令名   按下C键查看具体命令
        free:free 命令显示当前系统的内存使用情况,
            ~]# free -g
                          total        used        free      shared  buff/cache   available
            Mem:             15           9           0           0           5           5
            Swap:             0           0           0
            
            其中buffers 和cache 都是由操作系统为提高IO性能而分配管理的, buffers 是将被写入到磁盘的缓冲区的数据, cache是从磁盘读出到缓存的数据。
            经过一段时间的运行,可能会发现free 命令的输出结果中,可用内存越来越少, 通常都是因为缓存。
            这是由于Linux 为了提升I/O 性能和减小磁盘压力,使用了buffer cache 和 page cache, 
                buffer cache 针对磁盘的写进行缓存, 直接对磁盘进行操作的数据会缓存到buffer cache ,
                而文件系统中的数据则是交给page cache 进行缓存,即使数据库任务运行结束, cache也不会被主动释放。
                ===========================================================================
                Page cache缓存文件的页以优化文件IO。Buffer cache缓存块设备的块以优化块设备IO。
                在linux kernel 2.4之后,这两种caches统一了。虚拟内存子系统通过page cache驱动IO。如果数据同时在page cache和buffer cache中,buffer cache会简单的指向page cache,数据只会在内存中保有一份。
            所以, 是否使用到Swap 可以作为判断内存是否够用的一个简单标准,只要没有使用到Swap ,就说明内存还够用,在数据库需要内存时, cache 可以很快被回收。
                如果想把缓存释放出来,可以使用如下命令:
                [root@pghostl ~]# sync
                [root@pghostl ~] # echo 1 > / proc/sys/vm/drop_caches
                
        vmstat
            vmstat delay count
            
            ~]# vmstat 3 5
            procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
             r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
             1  0      0 447280   2104 5726036    0    0     0    20    3    2  2  1 97  0  0
             0  0      0 445636   2104 5726052    0    0     0   157 5858 10826  2  1 97  0  0
            
            - r :当前CPU 队列中有几个进程在等待,持续为l 说明有进程一直在等待,超过核心数说明压力过大;
            -b :当前有多少个进程进入不可中断式睡眠状态;
            - swpd : 已经使用的交换分区的大小;
            - free :当前的空闲内存;
            - buff :已经使用的buffer 的大小,特指buffer cach e (存在用来描述文件元数据的cache);
            - cache : 已经使用的page cache 的大小,特指文件page 的cach巳;
            - si/so :从磁盘交换到Swap 分区和从Swap 分区交换到磁盘的大小;
            - bi/bo :从磁盘读出和写入到磁盘的大小,单位blocks/s;
            - in : 每秒被中断的进程数;
            - cs :每秒多少个CPU 进程在进进出出。

        mpstat:mp stat 返回CPU 的详细性能信息,如果只需要观察某一个CPU ,加上参数-P n, n 为要观察的core 的索引。
            ~]# mpstat 3 5
            Linux 3.10.0-1160.31.1.el7.x86_64 (seliius26148)        07/15/2021      _x86_64_        (8 CPU)

            05:01:35 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
            05:01:38 AM  all    5.54    0.00    4.94    0.30    0.00    0.90    0.00    0.00    0.00   88.32
            05:01:41 AM  all    5.47    0.00    4.57    0.09    0.00    0.95    0.00    0.00    0.00   88.93

        
        
            - %usr :用户花费的时间比例;
            - %nice :特定优先级进程的CPU 时间百分比;
            - %sys : 系统花费的时间比例;
            - %iowait: I/O 等待;
            - %irq :硬中断花费的CPU 时间;
            - %soft: 软中断花费的CPU 时间;
            - %steal, %guest :这两个参数与虚拟机相关,略;
            - %idle : 空闲比率。
        
        sar
            sar 是性能统计非常重要的工具。sar 每隔一段时间进行一次统计,它的配置在/etc/cron.d/sysstat中,默认为10 分钟:
            ]# cat /etc/cron.d/sysstat
                # Run system activity accounting tool every 10 minutes
                */10 * * * * root /usr/lib64/sa/sa1 1 1
                
            ( 1 )汇总CPU 状况
            汇总CPU 状况的命令如下所示:
            ~]# sar -q 12
                Linux 3.10.0-1160.31.1.el7.x86_64 (seliius26148)        07/15/2021      _x86_64_        (8 CPU)

                05:12:14 AM   runq-sz  plist-sz   ldavg-1   ldavg-5  ldavg-15   blocked
                05:12:26 AM         4      3264      1.86      1.37      1.02         0
                05:12:38 AM         0      3264      1.60      1.33      1.01         0
                05:12:50 AM         4      3263      1.43      1.31      1.01         0
                05:13:02 AM         3      3252      1.37      1.30      1.01         0
                05:13:14 AM         0      3275      1.29      1.28      1.01         0
                05:13:26 AM         0      3276      1.09      1.24      1.00         0
                05:13:38 AM         3      3276      0.92      1.20      0.98         0
                05:13:50 AM         2      3279      0.94      1.19      0.98         0
            - runq-sz :运行队列平均长度;
            - plist-sz :进程列数;
            - ldavg-1 , ldavg-5, ldavg-15 : 每分、每5 分钟、每15 分钟的平均负载。
            
            ( 2 )汇总IO 状况
                ~]# sar -b 3
                    Linux 3.10.0-1160.31.1.el7.x86_64 (seliius26148)        07/15/2021      _x86_64_        (8 CPU)

                    05:16:06 AM       tps      rtps      wtps   bread/s   bwrtn/s
                    05:16:09 AM     18.67      0.00     18.67      0.00    434.00
                    05:16:12 AM     19.00      0.00     19.00      0.00    481.33
                    05:16:15 AM     17.67      0.00     17.67      0.00    335.67
                    05:16:18 AM      6.00      0.00      6.00      0.00    146.67
                    05:16:21 AM     18.00      0.00     18.00      0.00    421.00
                    05:16:24 AM      9.00      0.00      9.00      0.00    214.33
                    05:16:27 AM      4.33      0.00      4.33      0.00     98.67
                    05:16:30 AM     19.00      0.00     19.00      0.00    335.00
                    05:16:33 AM     18.00      0.00     18.00      0.00    388.67
                    ^C

                    05:16:34 AM     10.61      0.00     10.61      0.00    218.18
                    Average:        14.23      0.00     14.23      0.00    312.64

                tps, rtps, wtps: TPS 数;
                bread/s, bwrtn/s : 每秒读写block 的大小,需要注意:这里每个block 的大小为512字节,不要与数据库中的Block 混淆了。
                
            ( 3 )历史数据的汇总
                sar 的历史数据保存在/var/log/sa/目录,可以设置sar 历史数据的保留天数,编辑/etc/sysconfig/sysstat中的HISTORY 值。
                ~]# vi /etc/sysconfig/sysstat
                HISTORY=28
                
                ~]# sar -q -f /var/log/sa/sa15 -s 22:00:00 -e 23:00:00
                
            其他性能工具和方法
                nmon
                iotop+pidstat
                
    Linux 系统的I/O 调度算法
        对于数据库的读写操作, Linux 操作系统在收到数据库的请求时, Linux 内核并不是立即执行该请求,而是通过IO 调度算法,先尝试合并请求,再发送到块设备中。
        查看当前系统支持的调度算法:
        ~]# dmesg | grep -i scheduler
            [    0.804844] io scheduler noop registered
            [    0.804848] io scheduler deadline registered (default)
            [    0.804878] io scheduler cfq registered
            [    0.804881] io scheduler mq-deadline registered
            [    0.804885] io scheduler kyber registered
            
            cfg 称为绝对公平调度算法,它为每个进程和线程单独创建一个队列来管理该进程的I/O请求,为这些进程和线程均匀分布I/O 带宽,比较适合于通用服务器, 是Linux 系统中默认的I/O调度算法。
            noop 称为电梯调度算法,它基于FIFO 队列实现,所有I/O请求先进先出,适合SSD 。
            deadline 称为绝对保障算法, 它为读和写分别创建了FIFO 队列,当内核收到请求时,先尝试合井,不能合并则尝试排序或放入队列中,并且尽量保证在请求达到最终期限时进行调度,避免有一些请求长时间不能得到处理适合虚拟机所在宿主机器或I/ O 压力比较重的场景,例如数据库服务器。
            
            可以通过以下命令查看磁盘sda 的的I/ O 调度算法:
            ~]# cat /sys/block/sda/queue/scheduler
                noop [deadline] cfq
                
            修改【临时】
            ~]# echo noop >> /sys/block/sda/queue/scheduler
            修改【永久】
             /etc/grub.conf
    Linux预读参数调整
        查看磁盘预读扇区大小
        ~]# /sbin/blockdev --getra /dev/sda
        8192
        
        设置
        ~]# /sbin/blockdev --setra 16384 /dev/sda
        或
        ~]# /sbin/blockdev --setra 16384 /sys/block/sda/queue/read_ahead_kb
        
        设置多块设备
        ~]# echo "/sbin/blockdev --setra 16384 /dev/dda /dev/sda1" >> /etc/re.local
        
    内存的优化:
        1.禁用swap
            ~]# swapoff - a
        2. 关闭透明大页
            透明大页( Transparent HugePages )在运行时动态分配内存,而运行时的内存分配会有延误,对于数据库管理系统来说并不友好,所以建议关闭透明大页。
                ~]# cat /sys/kernel/mm/transparent_hugepage/enabled
                [always] madvise never
                
                ~]# echo never >> /sys/kernel/mm/transparent_hugepage/enabled
                
            永久禁用透明大页可以通过编辑/etc/re.local,加入以下内容:
                if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
                echo never > /sys/kernel/mm/transparent_hugepage/enabled
                fi
                if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
                echo never > /sys/kernel/mm/transparent_hugepage/defrag
                fi
            还可以通过修改/etc/grub.conf,在kernel的行末加上transparent_hugepage=never 禁用透明大页,如下所示:
        3. NUMA
            NUMA 架构会优先在请求线程所在的CPU 的local 内存上分配空间,如果local 内存不足,优先淘汰local 内存中无用的页面,这会导致每个CPU 上的内存分配不均,虽然可以通过配置NUMA 的轮询机制缓解,但对于数据库管理系统仍不又好,建议关闭NUMA 。
            ~]# numactl --hardware
            ~]# numastat
            关闭NUMA 最直接的方法是从服务器的BIOS 中关闭,
            还可以通过修改/etc/grub.conf,在kernel的行末加上numa=off 禁用透明大页,如下所示:

数据库调优:
    全局参数调整:
        shared_buffers:
            在PostgreSQL 数据库启动时,就会分配所有的共享内存,即使没有请求,共享内存也会保持固定的大小,共享内存大小由shared_buffers 参数决定。
            当PostgreSQL 在接收到客户端请求时,服务进程会首先在shared buffers 查找所需的数据,如果数据已经在sharedbuffers 中,服务进程可以在内存中进行客户端请求的处理;如果shared buffers 中没有所需数据,则会从操作系统请求数据,多数情况这些数据将从磁盘进行加载--速度要慢很多
            
            增加shared buffers 能使服务进程尽可能从shared buffers 中找到所需数据,避免去读磁盘。
            PostgreSQL 依赖于操作系统缓存的方式, shared_buffers 的值也不是越大越好,建议该参数根据不同的硬件配置,使用pgbench 进行测试得到一个最佳值。
                pg=# show shared_buffers;
                 shared_buffers
                ----------------
                 2GB
                (1 row)
        work_mem 
            work_mem 用来限制每个服务进程进行排序或hash 时的内存分配,指定内部排序和hash 在使用临时磁盘文件之前能使用的内存数量,它的默认值是4MB ,因为它是针对每个服务进程设置的,所以不宜设置太大。
            当每个进程得到的work mem 不足以排序或hash使用时,排序会借助磁盘临时文件,使得排序和hash 的性能严重下降。
            配置该参数时,有必要了解服务器上所运行的查询的特征,如果主要运行一些小数据量排序的查询,可以不用设置过大。PostgreSQL 在排序时有Top-N heapsort 、Quick sort 、External merge 这几种排序方法,如果在查询计划中发现使用了External merge ,说明需要适当增加work_mem的值。
            pg=# show work_mem;
                 work_mem
                ----------
                 1GB
                (1 row)
        random_page_cost 
            random_page_cost:代表随机访问磁盘块的代价估计。参数的默认值是4 ,如果使用机械磁盘,这个参数对查询计划没有影响
            
    统计信息和查询计划
        在运行期间, PostgreSQL 会收集大量的数据库、表、索引的统计信息,查询优化器通过这些统计信息估计查询运行的时间然后选择最快的查询路径。
        统计信息都保存在PostgreSQL 的系统表中,这些系统表都以pg_stat 或pg_statio 开头。
        统计信息一类是支撑数据库系统内部方法的决策数据,例如决定何时运行autovacuum 和如何解释查询计划。
        决策数据保存在pg_statistics 中,这个表只有超级用户可读,普通用户没有权限,需要查看这些数据,可以从pg_stats 视图中查询。
        
        
        pg_stat_database:数据库级的统计信息
            pg=# \c cdm
            
            #查看其表结构
            cdm=# \d pg_stat_database
            
            口numbackends : 当前有多少个并发连接, 理论上控制在cpu 核数的1.5 倍可以获得更好的性能;
            口blks_read,blks_hit : 读取磁盘块的次数与这些块的缓存命中数;
            口xact_commit, xact_rollback :提交和回滚的事务数;
            口deadlocks :从上次执行pg_stat_reset 以来的死锁数量。
            
            通过下面的查询可以计算缓存命中率:
                cdm=# select blks_hit::float/(xact_commit+xact_rollback) as cache_hit_ratio from pg_stat_database where datname=current_database();
                 cache_hit_ratio
                ------------------
                 14.8076177713403
                (1 row)

                缓存命中率是衡量I/O 性能的最重要指标,它应该非常接近1 , 否则应该调整shared_buffers的配置,如果命中率低于99% ,可以尝试调大它的值。
            
            【事务提交率】
                cdm=# select xact_commit::float/(xact_commit+xact_rollback) as successful_xact_ratio from pg_stat_database where datname=current_database();
                 successful_xact_ratio
                -----------------------
                     0.999966277554639
                    
                事务提交率则可以知道我们应用的健康情况,它应该等于或非常接近1 ,否则检查是否死锁或其他超时太多。
            
            【注意】
                在pg_stat_database 系统视图的字段中,除numbackends 字段和stats_reset 字段外, 其他字段的值是自从stats_reset 字段记录的时间点执行pg_stat_reset ()命令以来的统计信息。
                建议使用者在进行优化和参数调整之后执行pg_stat_reset()命令, 方便对比优化和调整前后的各项指标。

        pg_stat_user_tables:表级的统计信息最常用的是pg_stat_user(all)_tables 视图
            查看表结构
            cdm=# \d pg_stat_user_tables;
            
            cdm=# \d pg_stat_all_tables;
            View "pg_catalog.pg_stat_all_tables"
            
            last_vacuum , last_analyze : 最后一次在此表上手动执行vacuum 和analyze 的时间。
            last_autovacuum , last_autoanalyze : 最后一次在此表上被autovacuum 守护程序执行autovacuum 和analyze 的时间。
            idx_scan, idx_tup_fetch :在此表上进行索引扫描的次数以及以通过索引扫描获取的行数。
            seq_scan, seq_tup_read : 在此表上顺序扫描的次数以及通过顺序扫描读取的行数。
            n_tup_ins , n_tup_upd , n_tup_del : 插入、更新和删除的行数。
            n_live_tup, n_dead_tup: live tuple 与dead tuple 的估计数。
            从性能角度来看,最有意义的数据是与索引 VS 顺序扫描有关的统计信息。
            因为实际的表数据存储在无序的堆中,读取行是一项耗时的操作,顺序扫描对于大表来说是成本非常高。因此,应该调整索引定义,以便数据库尽可能少地执行顺序扫描。索引扫描与整个数据库的所有扫描的比率可以计算
            
            cdm=# select sum(idx_scan)/(sum(idx_scan)+sum(seq_scan)) as idx_scan_ratio from pg_stat_all_tables where schemaname='profile_service';                                                                                    idx_scan_ratio
            ------------------------
             0.99334416858822793686
            (1 row)
            
            cdm=# select relname, idx_scan::float/(idx_scan+seq_scan+1) as idx_scan_ratio from pg_stat_all_tables where schemaname='profile_service' order by idx_scan_ratio asc;
                    relname         |  idx_scan_ratio
            ------------------------+-------------------
             device_thing           | 0.991454532506867
             event_config_thing     | 0.992206945210271
             network_identity_thing | 0.996677404782793
             thing                  | 0.999046827828177
            (4 rows)
            
        pg_stat_statements
            语句级的统计信息一般通过pg_stat_statements 、postgres日志、auto_explain 来获取。
            开启pg_stat_statements 需要在postgresql.conf 中配置, 如下所示:
                shared_preload_libraries = 'pg_stat_statements'
                pg_stat_statements.track = all
            然后执行CREATE EXTENSION 启用它, 如下所示:
            mydb=# CREATE EXTENSION pg_stat_statements ;
            CREATE EXTENSION

            #查看视图定义
            cdm=# \d pg_stat_statements;
                View "public.pg_stat_statements"
                
            查询平均执行时间最长的3 条查询,
            cdm=# select calls, total_time/calls as avg_time,left(query,80) from pg_stat_statements order by 2 desc limit 3;

        查看SQL 的执行计划
            执行计划,也叫作查询计划,会显示将怎样扫描语句中用到的表, 例如使用顺序扫描还是索引扫描等等,以及多个表连接时使用什么连接算法来把每个输入表的行连接在一起。
            在PostgreSQL 中使用EXPLAIN 命令来查看执行计划
            在EXPLAIN 命令后面还可以跟上ANALYZE 得到真实的查询计划,
            cdm=# EXPLAIN select * from profile_service.network_identity_thing;
                                              QUERY PLAN
            -------------------------------------------------------------------------------
             Seq Scan on network_identity_thing  (cost=0.00..3726.06 rows=5606 width=5716)
            (1 row)

            
            
            cdm=# EXPLAIN ANALYZE select * from profile_service.network_identity_thing;
                                                                     QUERY PLAN
            -----------------------------------------------------------------------------------------------------------------------------
             Seq Scan on network_identity_thing  (cost=0.00..3726.06 rows=5606 width=5716) (actual time=0.033..11.534 rows=5606 loops=1)
             Planning time: 0.142 ms
             Execution time: 11.781 ms
            (3 rows)

            【注意】
                使用ANALYZE 选项时语句会被执行,所以在分析INSERT、UPDATE 、DELETE 、CREATE TABLE AS 或者EXECUTE 命令的查询计划时,应该使用一个事务来执行,得到真正的查询计划后对该事务进行回滚,避免因为使用ANALYZE选项而修改了数据
                cdm=# BEGIN;
                BEGIN
                cdm=# EXPLAIN ANALYZE INSERT INTO device_repository.device_whitelist (whitelist_id, mvno_id, imsi_range, description, device_type, created_time, last_modified_time) VALUES('d79705bc-daf8-499f-a576-047cdd1c2555', NULL, '935711', 'steven test2', 'default','2020-12-24 00:52:13.925', '2020-12-24 00:52:13.925');
                cdm=# ROLLBACK;
                ROLLBACK
                cdm=#
                
                【查看查询计划】:
                在阅读查询计划时,有一个简单的原则:从下往上看,从右往左看。
                在每行计划中,都有几项值,( cost=0.00 .. xxx )预估该算子开销有多么“昂贵” 。“昂贵”按照磁盘读计算。
                这里有两个数值:
                    第一个表示算子返回第一条结果集最快需要多少时间;
                    第二个数值(通常更重要)表示整个算子需要多长时间。
                开销预估中的第二项(rows=xxx)表示PostgreSQL 预计该算子会返回多少条记录。
                最后一项(width=1917 ) 表示结果集中一条记录的平均长度(字节数), 由于使用了ANALYZE 选项,后面还会有实际执行的时间统计
                
                除了ANALYZE 选项,还可以使用COSTS 、BUFFERS 、TIMING 、FORMAT 这些选项输出比较详细的查询计划,例如:
                cdm=# EXPLAIN (ANALYZE on,TIMING on,VERBOSE on,BUFFERS on) select * from profile_service.network_identity_thing;
                                                    QUERY PLAN
                ------------------------------------------------------------------------------------------------
                 Seq Scan on profile_service.network_identity_thing  (cost=0.00..3726.06 rows=5606 width=5716) (actual time=0.009..4.789 rows=5606 loops=1)
                   Output: id, thing_id, external_id, reference_id, source_id, name, type, status, location, manufacturer, version, firmware, description, profile_creation_time, status_last_updated_time, last_updated_time, active
                _status, extension
                   Buffers: shared hit=3670
                 Planning time: 0.121 ms
                 Execution time: 5.055 ms
                (5 rows)
                
                可以使用session 级的log_xxx_stats 来判断问题。
                PostgreSQL 在initdb 后,log_statement_stats 参数默认是关闭的,因为打开它会在执行每条命令的时候,执行大量的系统调用来收集资源消耗信息,所以在生产环境中也应该关闭它,一般都在session 级别使用它。
                在postgresql.conf 中有log_parser_stats 、log_planner_stats 和log_statement_stats 这几个选项,默认值都是off,其中log_parser_stats 和log_planner_stats 这两个参数为一组,log_statement_stats 为一组,这两组参数不能全部同时设置为on 。
                查看parser 和planner 的系统资源使用的查询计划,如下所示:
                mydb=# set client_min_messages = log ;
                mydb=# set log_parser_stats = on ;
                mydb=# set log_planner_stats = on ;
                运行EXPLAIN ANALYZE 查看查询计划,如下所示:
                cdm=# EXPLAIN ANALYZE select * from profile_service.network_identity_thing;

                
                查看查询的系统资源使用,如下所示:
                cdm=# set client_min_messages = log ;
                cdm=# set log_parser_stats = off ;
                cdm=# set log_planner_stats = off ;
                cdm=# set log_statement_stats = on ;
                cdm=# EXPLAIN ANALYZE select * from profile_service.network_identity_thing;
                    LOG:  QUERY STATISTICS
                    DETAIL:  ! system usage stats:
                    !       0.004138 elapsed 0.003174 user 0.000963 system sec
                    !       [0.011975 user 0.013064 sys total]
                    !       0/0 [0/8] filesystem blocks in/out
                    !       0/4 [0/6533] page faults/reclaims, 0 [0] swaps
                    !       0 [0] signals rcvd, 0/0 [0/0] messages rcvd/sent
                    !       0/0 [10/0] voluntary/involuntary context switches
                                                                             QUERY PLAN
                    ----------------------------------------------------------------------------------------------------------------------------
                     Seq Scan on network_identity_thing  (cost=0.00..3726.06 rows=5606 width=5716) (actual time=0.005..3.487 rows=5606 loops=1)
                     Planning time: 0.195 ms
                     Execution time: 3.705 ms
                    (3 rows)

                    DETAIL:!
                        system usage stats :之后的前两行显示查询使用的用户和系统CPU 时间和已经消耗的时间。
                        第3 行显示存储设备(而不是内核缓存)中的I/O 。
                        第4 行涵盖内存页面错误和回收的进程地址空间。
                        第5 行显示信号和IPC 消息活动。
                        第6 行显示进程上下文切换。
                
    索引管理与维护
        PostgreSQL 有许多种索引类型来:B-tree 、Hash 、GiST 、SP-GiST 、GIN 、BRIN和Bloom 等等。
        最常用最常见的索引类型是B-tree。B-tree 索引在执行CREATE INDEX 命令时是默认的索引类型。
            B-tree 索引通常用于等值和范围查询; 
            Hash 索引只能处理简单等值查询; 
            GIN 索引是适合于包含多个组成值的数据值,例如数组; 
            GiST 索引并不是一种单独的索引,而是一个通用的索引接口, 可以使用GiST实现B-tree 、R-tree 等索引结构, 在PostGIS 中使用最广泛的就是GiST 索引。
            
            除了支持众多类型的索引, PostgreSQL 也支持唯一索引、表达式索引、部分索引和多列索引, B-tree 、GiST 、GIN 和BRIN 索引都支持多列索引,最多可以支持32个列的索引。
        
        在PostgreSQL 中执行CREATE INDEX 命令时,可以使用CONCURRENTLY 参数并行创建索引,使用CONCURRENTLY 参数不会锁表, 创建索引过程中不会阻塞表的更新、插入、删除操作。
        
        由于PostgreSQL 的MVCC 内部机制,当运行大量的更新操作后,会有“索引膨胀”的现象,这时候可以通过CREATE INDEX CONCURRENTLY 在不阻塞查询和更新的情况下,在线重新创建索引,     
            1. 创建好新的索引
            2. 删除原先有膨胀的索引,减小索引尺寸,提高查询速度。
        对于主键也可以使用这种方式进行重建,重建方法如下:
        cdm=# CREATE UNIQUE INDEX CONCURRENTLY ON mytbl USING btree (id);
            CREATE INDEX
            
        开启事务删除主键索引,同时将第二索引更新为主键的约束,如下所示:
            cdm=# BEGIN;
            BEGIN
            cdm=# ALTER TABLE mytbl DROP CONSTRAINT mytbl_pkey ;
                ALTER TABLE
            cdm=# ALTER TABLE mytbl ADD CONSTRAINT mytbl_id_idx PRIMARY KEY USING INDEX mytable_id_index.
            cdm=# END;
            END
            cdm=# COMMIT;
        #查询相关的数据    
        select schemaname,relname,indexrelname,pg_relation_size(indexrelid) as index_size,idx_scan,idx_tup_read,idx_tup_fetch from pg_stat_user_indexes where indexrelname in (select indexname from pg_indexes where schemaname='profile_service' and tablename='network_identity_thing');

            
        通过pg_repack 工具进行定时的索引重建。
        
        好的监控系统,历史数据的分析,慢查询的优化都需要不断完善,才能保障业务系统稳定高效运转。


    定时进行VACUUM 操作和ANALYZE 操作,在业务低谷时段定时进行VACUUM FREEZE 操作,及时处理慢查询,硬件的监控维护也很重要
        查看os进程
            $ ps -ef|grep -i post |grep -i stat
                postgres 10782 10770  0 May09 ?        00:02:42 postgres: stats collector process
        表的信息
            mondb=# select pg_size_pretty(pg_relation_size('t_gather_pgsql_space_table')), pg_size_pretty(pg_total_relation_size('t_gather_pgsql_space_table'));
            【实例】---可以通过DBeaver查看其大小,不必运行上面的查询
            select pg_size_pretty(pg_relation_size('device_profile_management.device')), pg_size_pretty(pg_total_relation_size('device_profile_management.device'));
            
            
            cdm=# vacuum full device_profile_management.device;
            cdm=# vacuum full device_profile_management.device_status;
            cdm=# vacuum full profile_service.device_thing;
            cdm=# vacuum full profile_service.network_identity_thing;
        表分析
            cdm=# \h analyze
                Command:     ANALYZE
                Description: collect statistics about a database
                Syntax:
                ANALYZE [ VERBOSE ] [ table_name [ ( column_name [, ...] ) ] ]

            cdm=# analyze verbose device_profile_management.device;
            cdm=# analyze verbose device_profile_management.device_status;
            cdm=# analyze verbose profile_service.device_thing;
            cdm=# analyze verbose profile_service.network_identity_thing;
            
            cdm=# show default_statistics_target;
            
            SET STATISTICS
            这种形式为后续的ANALYZE操作设置针对每列 的统计收集目标。目标可以被设置在范围 0 到 10000 之间,还可以 把它设置为 -1 来恢复到使用系统默认的统计目标( default_statistics_target)。
            cdm=# ALTER TABLE t_gather_pgsql_space_table ALTER COLUMN tab_name SET STATISTICS 200;

你可能感兴趣的:(数据库)