PG明明业务进行的是SELECT,为什么监控磁盘,写负载那么大呢?

简介: PG明明业务进行的是SELECT,为什么监控磁盘,写负载那么大呢?

问题


进行测试时,预制完数据后立即进行SELECT测试查询场景,或者预制完数据立即重启服务,然后进行SELECT场景测试,监控磁盘负载时,发现写负载特别大。

这就有疑惑了,测试场景都是查询,没有INSERT\UPDATE\DELETE,哪来的写呢?


分析


最简单的方法就是在有负载时,跟踪其堆栈了,通过pstack跟踪checkpointpgwriterstat进程,监控堆栈中是否有write了。有这个思路,立即做检查。

发现stat进程中有write,继续分析,发现该进程会更新统计信息持久化到pg_stat目录下文件。但是该文件更新数据量没那么大,监控到磁盘负载每秒达到大几十M,不太可能是因为这个导致的。

那么,继续跟踪用户连接上来后fork的进程,发现有大量的write。用户只有select,难道之前因为预制产生很多死记录,导致需要刷写脏页?那么测试前执行vacuum然后再进行测试是否还会有写负载呢?说做就做,立即执行vacuum,再进行测试。奇迹发生了,磁盘写负载立即下降下来了。到此,问题就知道在哪了,因为内存中有很多脏页,当进行select负载测试时,需要不断从磁盘加载数据,此时需要将内存中数据页进行替换以存储磁盘页,若该内存页为脏,需要先将它刷写下来。

编译一个debug版本,进行gdb跟踪可以详细了解到底write发生在哪个流程中。我们编译好后,立即替换postgres可执行文件,重启,在write函数上打断点,进行SELECT测试。

跟踪到heapam_index_fetch_tuple函数进行索引扫描时,会调用到smgrwriteSmgrwrite函数为磁盘IO的函数,该函数对应mdwrite,继续调用FileWrite->pgwrite64

根据堆栈信息,分析源码:


heapam_index_fetch_tuple->
  hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
          hscan->xs_base.rel,
          ItemPointerGetBlockNumber(tid));
    |-  ReadBuffer
      |-  ReadBufferExtended
        |-  ReadBuffer_common
          |-  bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,strategy, &found);
            |-  INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
              newHash = BufTableHashCode(&newTag);
              newPartitionLock = BufMappingPartitionLock(newHash);
              buf_id = BufTableLookup(&newTag, newHash);
              if(buf_id >=0){
                ...
                内存中命中改页
                return 该buf;
              }
              for(;;){//需要在内存找一个空闲页以供存储磁盘上加载的页
                buf = StrategyGetBuffer(strategy, &buf_state);
                oldFlags = buf_state & BUF_FLAG_MASK;
                if (oldFlags & BM_DIRTY){
                  FlushBuffer(buf, NULL);
                }
            |-  }
FlushBuffer(buf, NULL);
  recptr = BufferGetLSN(buf);
  XLogFlush(recptr);
  smgrwrite(reln,
        buf->tag.forkNum,
        buf->tag.blockNum,
        bufToWrite,
        false);
  |-  mdwrite->FileWrite->pgwrite64

 

和之前分析一致,读取数据页时,需要先从内存中找改页,若命中,则直接返回。若不命中,需要找一个空闲数据页,没有空闲页就会进行数据页驱逐,若此时该数据页是脏页,那么就需要先将它刷写下去。当然刷写前需要先将脏页对应的日志持久化。

这就需要注意了,进行测试时,预制完数据需要将其进行vacuum,消除后续进行vacuum对测试的影响。要不然,对测试结果影响太大了。

目录
相关文章
|
20天前
|
关系型数据库 MySQL
图解MySQL【日志】——磁盘 I/O 次数过高时优化的办法
当 MySQL 磁盘 I/O 次数过高时,可通过调整参数优化。控制刷盘时机以降低频率:组提交参数 `binlog_group_commit_sync_delay` 和 `binlog_group_commit_sync_no_delay_count` 调整等待时间和事务数量;`sync_binlog=N` 设置 write 和 fsync 频率,`innodb_flush_log_at_trx_commit=2` 使提交时只写入 Redo Log 文件,由 OS 择机持久化,但两者在 OS 崩溃时有丢失数据风险。
38 3
|
4天前
|
SQL 数据库
【YashanDB知识库】表收集统计信息默认阈值引起SQL执行效率差
【性能优化】表新增87万数据后,因自动收集统计信息任务未启动,导致SQL执行计划变差。原因分析:插入81万数据未达统计信息失效阈值(10%),故未触发收集。虽统计信息未失效,但执行计划不同,因缺乏历史视图无法精准分析。解决方法:合理设置大表统计信息收集阈值,并获取SQL执行计划历史变更功能。此问题严重降低SQL执行效率,修复版本为23.2补丁版本。
|
2月前
|
存储 SQL 数据库
性能调优:优化 GROUP BY——使用索引字段分组减少临时文件生成
性能调优:优化 GROUP BY——使用索引字段分组减少临时文件生成
102 1
|
SQL 存储 关系型数据库
mysql 利用 performance_schema 排查 qps 过高过程记录
mysql 利用 performance_schema 排查 qps 过高过程记录
414 0
|
SQL 存储 关系型数据库
记一次MySQL CPU被打满的SQL优化案例分析
记一次MySQL CPU被打满的SQL优化案例分析
262 0
|
SQL 缓存 关系型数据库
PG空闲连接对性能的影响
PG空闲连接对性能的影响
344 0
|
SQL 监控 关系型数据库
慢sql较多,导致数据库cpu打满,造成系统无法正常使用
慢sql较多,导致数据库cpu打满,造成系统无法正常使用
399 0
慢sql较多,导致数据库cpu打满,造成系统无法正常使用
|
SQL 存储 缓存
关于xxl-job中的慢sql引发的磁盘I/O飙升导致拖垮整个数据库服务
关于xxl-job中的慢sql引发的磁盘I/O飙升导致拖垮整个数据库服务
274 0
|
SQL 存储 SpringCloudAlibaba
MySQL 千万数据量深分页优化, 拒绝线上故障!
MySQL 千万数据量深分页优化, 拒绝线上故障!
684 0
MySQL 千万数据量深分页优化, 拒绝线上故障!
|
SQL 关系型数据库 MySQL
mysql性能优化:单表1400w查询最后十条数据(耗时0.036s)
看几个关键字段,type、key、extra,不算完美,但也还行,毕竟我们这种非DBA选手,sql能力有限 顺便科普下这个执行计划,看id列,1 1 2,执行顺序是第三行 第一行 第二行,记住口诀:id不同大的先走,id相同,从上往下
1491 0
mysql性能优化:单表1400w查询最后十条数据(耗时0.036s)