监控指标常见问题

本文为您介绍Hologres监控指标相关的常见问题。

连接数过多时如何查看有哪些连接以及Kill连接?

连接数包括实例中总的SQL连接数,包括Active、Idle状态的JDBC/PSQL等连接。实例的连接数通常与实例的规格有关,如果您发现连接数过多,甚至出现超出实例最大连接数的情况,或者遇到如下报错:

  • 产生FATAL: sorry, too many clients already connection limit exceeded for superusers报错。

  • 产生FATAL: remaining connection slots are reserved for non-replication superuser connections报错。

说明实例连接数已达上限,通过HoloWeb或者SQL的方式查看当前的连接情况,详情请参见连接数管理。可以使用Superuser账号对不符合预期或者Idle的连接进行Kill操作。

查询延迟过高时如何解决?

常见的延迟过高有以下几种原因,可以先通过慢Query日志查找对应的慢SQL并根据如下的情况进行解决,详情请参见慢Query日志查看与分析

  • QPS较低,但查询SQL较复杂,导致查询延迟高。

    解决方法:对SQL进行优化,设置合理的索引,以提高查询性能,详情请参见优化查询性能优化MaxCompute外部表的查询性能

  • 查询的QPS较高,导致查询延迟较高。

    解决方法:若是SQL已经优化完毕,但需要更高的QPS以及更低的延迟,可以扩容实例,用更多的资源获取更好的性能,详情请参见实例升配

  • 查询时还有大量的写入,写入影响查询,导致查询延迟高。

    解决方法:写入影响了查询性能,可以进行如下操作。

    • 写入在查询低峰期进行,降低对查询的影响。

    • 降低写入的并发度,提高查询效率,如果是外表写入,可以用以下参数降低并发度。

      --设置MaxCompute执行的最大并发度,默认为128,建议数值设置小一些,避免一个Query影响其他Query,导致系统繁忙导致报错。
      set hg_experimental_foreign_table_executor_max_dop = 32; --优先考虑设置
      
      --调整每次读取MaxCompute表batch的大小,默认8192。
      set hg_experimental_query_batch_size = 1024;
      
      --直读orc
      set hg_experimental_enable_access_odps_orc_via_holo = on;
      
      --设置MaxCompute表访问切分spilit的数目,可以调节并发数目,默认64MB,当表很大时需要调大,避免过多的split影响性能。
      set hg_experimental_foreign_table_split_size = 512MB;

内存使用率高的原因及解决方法?

Hologres实例的内存使用率为内存综合使用率。Hologres的内存资源采用预留模式,在没有查询的时候,也会有数据表的元数据、索引、数据缓存等加载到内存中,以便加快检索和计算,此时内存使用率不为零是正常情况。理论上在无查询的情况,内存使用率达到30%~40%左右都属于正常现象。

一些情况下,会使得内存使用率持续升高,甚至接近80%。主要原因如下:

  • 表越来越多,数据总量越来越大,以至于数据规模远大于当前计算规格。由于内存使用率和元数据、索引量存在正相关关系,因此,表的数量越多,数据量越大,索引越多,都会导致内存使用率升高。

  • 索引不合理,例如表的列特别多,TEXT列居多,设置了过多的Bitmap或Dictionary索引。此情况可以考虑去掉一些Bitmap或者Dictionary索引,详情请参见ALTER TABLE

但是当内存使用率稳定增长,长期接近80%时,通常意味着内存资源可能成为了系统的瓶颈,可能会影响实例的稳定性和或性能。稳定性影响体现在当元数据等过大,超额占据了正常Query可用的内存空间时,在查询过程中,可能会偶发SERVER_INTERNAL_ERRORERPC_ERROR_CONNECTION_CLOSEDTotal memory used by all existing queries exceeded memory limitation等报错。性能影响体现在当元数据等过大,超额占据了正常Query本来能够使用的缓存空间,从而缓存命中会减少,Query延迟会增加。

因此当内存长期接近80%时,有如下几个操作建议。

  • 删除不再查询的数据,以释放元数据等占用的内存。

  • 设置合理的索引,若是业务场景用不上的bitmap和dictionary,可以去掉,但不建议直接去掉,需要根据业务情况具体分析。

  • 升配实例的计算和存储资源。对于升配的建议是:

    • 普通场景:可以容许读磁盘数据的延迟,响应时间要求不严格,1CU(1Core+4GB内存)可以支持50~100GB的数据存储。

    • 响应时间要求低的Serving场景:最好查询热点数据全在内存的缓存中。内存中缓存的比例默认占总内存的30%,即1CU(1Core+4GB内存)其中1.3GB用于数据缓存,同时数据缓存还会被表的元数据所使用一些。举个例子,低响应要求的场景,热点数据如果是100GB,那么最好要求100GB 在缓存可用(实际上数据读出来解压后,占用内存不止100GB),因此至少需要约320GB内存以上,从而推算计算资源至少需要96CU左右。

为什么只有一个任务,Hologres实例CPU使用率就达到100%?

Hologres实例的CPU使用率为实例的CPU综合使用率。Hologres因其可以充分发挥多核并行计算的能力,通常来说单个查询可以迅速将CPU使用率提高到100%,这说明计算资源得到了充分利用。CPU使用率高不是问题,CPU使用率高了之后,查询慢写入慢才是问题,需要综合分析。

写入慢如何解决?

执行insertinsert on conflictupdate命令时,耗时比较长即写入性能较差,通常原因是因为SQL没有走Fixed Plan,没有走Fixed Plan的SQL命令存在表锁,并发执行时会等锁,导致耗时较长,实时写入RPS监控指标会显示写入类型为insert。可以查看Query的特征改写为走Fixed Plan的方式,让监控指标的写入类型变成SDK提升写入性能,详情请参加Fixed Plan加速SQL执行

CPU使用率长期达到100%如何解决?

当Hologres实例CPU使用率长期接近100%时(例如CPU使用率连续3小时满载100%,或者连续12小时达到90%以上等),说明实例负载非常高,这通常意味着CPU资源成为了系统的瓶颈,需要分析具体的业务场景和查询,以判断原因。可以从以下几方面进行排查。

  • 排查一:QPS或者RPS明显上涨。

    对比CPU使用率上涨前和上涨后的QPS和RPS监控指标,如果有明显的上涨趋势,而导致CPU使用率上涨。

    解决方法如下。

    • 查询数据(select)行为导致CPU使用率上涨,可以通过慢Query日志,排查耗时较长的Query,对Query进行针对性优化。

    • 执行insertupdatedelete操作导致CPU使用率上涨,建议通过慢Query日志排查Query是否未走Fixed Plan,如下SQL所示。未走Fixed Plan的insertupdatedelete命令会产生表锁,Query并发会造成锁等待,可以从业务上判断是否可以改写为走Fixed Plan的SQL,避免表锁,降低CPU水位。

      --示例查看过去一小时未走fixed plan的insert/update/delete
      select *
      from hologres.hg_query_log
      where query_start >= now() - interval '3 h'
          and command_tag in ('INSERT','UPDATE','DELETE')
          and 'HQE'=ANY(engine_type)
      order by query_start desc limit 500;
    • SQL都已经合理,但是CPU使用率还是比较高,说明实例资源已到瓶颈,可以适当的扩容或者使用多实例共享存储部署从而读写分离,详情请参见实例升配主从实例读写分离部署(共享存储)

  • 排查二:未有明显的QPS或者RPS上涨,存在运行时间较长的Query。

    通过监控指标发现没有明显的QPS或者RPS上涨,但是CPU使用率却突然上涨并且持续一段时间,可以通过正在运行Query持续时长监控指标查看是否有运行时间较长的Query,若监控指标显示有运行时长超过半个小时或者一小时的Query,说明是该Query导致的CPU使用率高。使用如下命令通过活跃Query查询运行的Query,并结束Query,从而降低CPU使用率。

    --查看运行时间较长的query
    SELECT current_timestamp - query_start as runtime, datname::text, usename, query, pid::text
        FROM pg_stat_activity
        WHERE state != 'idle'
        order by 1 desc;
    
    --取消query执行
    select pg_cancel_backend(<pid>);
  • 排查三:未有明显的QPS或者RPS上涨,存在消耗CPU较高的Query。

    通过监控指标发现没有明显的QPS或者RPS上涨,但是CPU使用率却突然上涨并且持续一段时间,可以通过如下命令从慢Query日志查看消耗CPU较高的Query,从而定位CPU消耗的操作,并根据Query优化SQL。

    -- 查询最近3小时消耗比较高的Query
    select status as "状态",
    duration as "耗时(ms)",
    query_start as "开始时间",
    (read_bytes/1048576)::text || ' MB' as "读取量",
    (memory_bytes/1048576)::text || ' MB' as "内存",
    (shuffle_bytes/1048576)::text || ' MB' as "Shuffle",
    (cpu_time_ms/1000)::text || ' s' as "CPU时间",
    physical_reads as "读盘量",
    query_id as "QueryID",
    query
    from hologres.hg_query_log
    where query_start > current_timestamp - interval'3 h'
    and command_tag in ('SELECT','INSERT','UPDATE','DELETE')
    and duration > 1000
    order by duration desc,
    read_bytes desc,
    shuffle_bytes desc,
    memory_bytes desc,
    cpu_time_ms desc,
    physical_reads desc
    limit 500;
  • 排查四:PQE的SQL导致CPU使用率达到100%。

    通过监控指标发现未有明显的QPS或者RPS上涨,可以使用如下SQL命令从慢Query日志查询是否有新增的PQE SQL,导致CPU使用率上涨。如果存在PQE SQL,则需要优化SQL中走PQE引擎的算子,详情请参见优化查询性能

    --查询最近3小时走PQE的query
    select *
    from hologres.hg_query_log
    where query_start > current_timestamp - interval'3 h'
        and 'PQE'=ANY(engine_type)
    order by query_start desc limit 500;
  • 排查五:对表修改过Bitmap或者Dictionary索引。

    对表修改过Bitmap或者Dictionary索引,修改后会触发后台异步执行Compaction,占用一定的CPU资源,同时实例的存储量可能会出现先上升后回落的情况,可以使用如下SQL命令示例从慢Query日志查询是否有修改过索引。

    --查询最近3小时内执行过修改索引的记录
    select *
    from hologres.hg_query_log
    where query_start >= now() - interval '3 h'
    and command_tag in ('CALL')
    order by query_start desc limit 500;

正在运行Query时长较长如何解决?

监控指标正在运行Query持续时长中有长时间运行的Query,例如运行时长大于1小时。当出现有长时间运行的Query时,可以先通过活跃Query页面查看正在运行的Query,详情请参见Query管理。正在运行Query时长较长,通常有以下几种情况,可以根据实际情况进行排查。

  • 情况一:实例有较长时间的写入。

    解决方法:通过监控指标的实时写入RPS指标查看是否有持续的写入任务,从而导致出现Query运行时间较长的情况。

  • 情况二:事务空闲(idle in transaction)。

    • 客户端打开事务,进行DDL后未进行commit操作,使用如下SQL命令查询活跃Query状态,Query的状态显示为idle in transaction,且运行时间较长。

      --查看运行时间较长的query
      SELECT current_timestamp - query_start as runtime, datname::text, usename, query, pid::text
          FROM pg_stat_activity
          WHERE state != 'idle'
          order by 1 desc;
    • Query因为等锁卡住等原因,导致长时间运行中。

    解决方法:通过以下示例SQL查询出运行时间较长的Query,如果是因为idle in transaction导致运行时间较长,可以客户端关闭事务或者设置合理的空闲事务超时时间,详情请参见修改空闲Query超时时间

    --查看运行时间较长的query
    SELECT current_timestamp - query_start as runtime, datname::text, usename, query, pid::text
        FROM pg_stat_activity
        WHERE state != 'idle'
        order by 1 desc;
    
    --superuser取消query执行
    select pg_cancel_backend(<pid>);
  • 情况三:SQL运行复杂且有PQE的Query。

    解决方法:通过以下命令查询当前正在运行且运行时间较长的Query,然后通过执行计划(explain sql)查看当前有SQL走了PQE引擎(执行计划中有External SQL(Postgres)),导致执行时间较长。

    --查看运行时间较长的query
    SELECT current_timestamp - query_start as runtime, datname::text, usename, query, pid::text
        FROM pg_stat_activity
        WHERE state != 'idle'
        order by 1 desc;
    
    --查看query的执行计划
    explain sql
    • 使用Superuser账号Kill运行时间较长的Query。

    • 优化SQL中走PQE引擎的算子,详情请参见优化查询性能

  • 情况四:并发执行DDL导致抢锁。

    并发执行DDL时会锁表,导致互相抢锁,从而出现等锁,导致运行时间较长。

    解决方法:

    • 可以通过如下命令查看是否有DDL正在执行中,并Kill掉对应的DDL,释放锁。

      SELECT datname::text,usename,query,pid::text,state
         FROM pg_stat_activity
         WHERE state != 'idle' ;
    • 串行执行DDL。

失败Query如何排查?

失败Query代表每秒钟失败的Query。Query总数量是时间范围乘以QPS个数,即时间范围内的面积。不建议依赖QPS来判断失败的总数量。可以通过慢Query日志排查Query总的失败数量和失败原因,并根据报错针对性地解决,详情请参见慢Query日志查看与分析

Worker CPU负载不均如何解决?

在Hologres中数据分片(Shard)决定了数据的分布情况,一个Worker在计算时可能会访问一个或者多个Shard的数据。同一个实例中,一个Shard同一时间只能被一个Worker访问,不能同时被多个Worker访问。如果实例中每个Worker访问的Shard总数不同,那么就有可能出现Worker资源负载不均的情况,主要原因如下:

  • 原因一:存在数据倾斜。

    如果数据存在严重的倾斜,那么Worker的负载就会访问固定的Shard,导致出现CPU负载不均的情况。

    解决方法:需要通过以下语句排查数据倾斜。如示例结果,某个Shard的数值比其他Shard大太多,从而出现数据倾斜。可根据业务情况处理倾斜数据或者设置合适的Distribution Key,详情请参见优化查询性能

    select hg_shard_id,count(1) from <table_name> group by hg_shard_id;
    
    --示例结果:shard 39的count值较大,存在倾斜
    hg_shard_id | count
    -------------+--------
              53 |  29130
              65 |  28628
              66 |  26970
              70 |  28767
              77 |  28753
              24 |  30310
              15 |  29550
              39 | 164983
  • 原因二:实例设置的Shard数和Worker个数不是整倍数关系。

    当Table Group中设置的Shard数和实例的总Worker数不是整倍数关系时,意味着不同的Worker上分配的Shard数不同,从而导致负载不均。

    解决方法:根据实例规格,设置合理的Shard数,详情请参见Table Group与Shard Count操作指南。一般这种情况出现在较大规格(大于256Core)的实例上,小规格实例可以使用默认Shard数,无需更改。

  • 原因三:有Worker Failover后导致Shard分配不均。

    当有Worker因为OOM等原因而被终止(Kill)时,为了能快速恢复Worker的查询,系统会将该Worker对应的Shard,快速迁移至其他Worker。当被Kill的Worker被拉起后,系统会再分配部分Shard给它,从而出现Worker间Shard分配不均的现象。

    解决方法:如果实例负载较低,可忽略该负载分配不均的问题。如果实例负载较高,可以重启实例以重新均匀分配Shard资源。