云服务器内容精选

  • 场景10:小文件多IOPS高 某业务现场一批业务起来后,整个集群IOPS飙高,另外当出现集群故障后,长期Building不成功,IOPS飙高,相关表信息如下: SELECT relname,reloptions,partcount FROM pg_class c INNER JOIN ( SELECT parentid,count(*) AS partcount FROM pg_partition GROUP BY parentid ) s ON c.oid = s.parentid ORDER BY partcount DESC; 触发因素:某业务库大量列存多分区(3000+)的表,导致小文件巨多(单DN文件2000w+),访问效率低,故障恢复Building极慢,同时Building也消耗大量IOPS,反向影响业务性能。 解决办法: 整改列存分区间隔,减少分区个数来降低文件个数。 列存表修改为行存表,行存的存储特征决定其文件个数不会像列存那么膨胀严重。
  • 场景8:大量数据带索引导入 某客户场景数据往DWS同步时,延迟严重,集群整体IO压力大。 后台查看等待视图有大量wait wal sync和WALWriteLock状态,均为xlog同步状态。 触发因素:大量数据带索引(一般超过3个)导入(insert/copy/merge into)会产生大量xlog,导致主备同步慢,备机长期Catchup,整体IO利用率飙高。历史案例参考:实例长期处于catchup问题分析。 解决方案: 严格控制每张表的索引个数,建议3个以内。 大量数据导入前先将索引删除,导数完毕后再重新建索引。
  • 场景9:行存大表首次查询 某客户场景出现备DN持续Catcup,IO压力大,观察某个sql等待视图在wait wal sync。 排查业务发现某查询语句执行时间较长,执行kill命令后恢复。 触发因素:行存表大量数据入库后,首次查询触发page hint产生大量XLOG,触发主备同步慢及大量IO消耗。 解决措施: 对该类一次性访问大量新数据的场景,修改为列存表 关闭wal_log_hints和enable_crc_check参数(故障期间有丢数风险,不推荐)。
  • 场景4:无索引、有索引不走 某一次点查询,Seq Scan扫描需要3767ms,因涉及从4096000条数据中获取8240条数据,符合索引扫描的场景(海量数据中寻找少量数据),在对过滤条件列增加索引后,计划依然是Seq Scan而没有走Index Scan。 对目标表analyze后,计划能够自动选择索引,性能从3s+优化到2ms+,极大降低IO消耗。 常见场景:行存大表的查询场景,从大量数据中访问极少数据,没走索引扫描而是走顺序扫描,导致IO效率低,不走索引常见有两种情况: 过滤条件列上没建索引。 有索引但是计划没选索引扫描。 触发因素: 常用过滤条件列没有建索引。 表中数据因DML产生数据特征变化后未及时ANALYZE导致优化器无法选择索引扫描计划,ANALYZE介绍参见ANALYZE。 处理方式: 对行存表常用过滤列增加索引,索引基本设计原则: 索引列选择distinct值多,且常用于过滤条件,过滤条件多时可以考虑建组合索引,组合索引中distinct值多的列排在前面,索引个数不宜超过3个。 大量数据带索引导入会产生大量IO,如果该表涉及大量数据导入,需严格控制索引个数,建议导入前先将索引删除,导入完毕后再重新建索引。 对频繁做DML操作的表,业务中加入及时ANALYZE,主要场景: 表数据从无到有。 表频繁进行INSERT/UPDATE/DELETE。 表数据即插即用,需要立即访问且只访问刚插入的数据。
  • 场景5:无分区、有分区不剪枝 例如某业务表进场使用createtime时间列作为过滤条件获取特定时间数据,对该表设计为分区表后没有走分区剪枝(Selected Partitions数量多),Scan花了701785ms,IO效率极低。 在增加分区键creattime作为过滤条件后,Partitioned scan走分区剪枝(Selected Partitions数量极少),性能从700s优化到10s,IO效率极大提升。 常见场景:按照时间存储数据的大表,查询特征大多为访问当天或者某几天的数据,这种情况应该通过分区键进行分区剪枝(只扫描对应少量分区)来极大提升IO效率,不走分区剪枝常见的情况有: 未设计成分区表。 设计了分区没使用分区键做过滤条件。 分区键做过滤条件时,对列值有函数转换。 触发因素:未合理使用分区表和分区剪枝功能,导致扫描效率低。 处理方式: 对按照时间特征存储和访问的大表设计成分区表。 分区键一般选离散度高、常用于查询过滤条件中的时间类型的字段。 分区间隔一般参考高频的查询所使用的间隔,需要注意的是针对列存表,分区间隔过小(例如按小时)可能会导致小文件过多的问题,一般建议最小间隔为按天。
  • 场景6:行存表求count值 例如某行存大表频繁全表count(指不带过滤条件或者过滤条件过滤很少数据的count),其中Scan花费43s,持续占用大量IO,此类作业并发起来后,整体系统IO持续100%,触发IO瓶颈,导致整体性能慢。 对比相同数据量的列存表(A-rows均为40960000),列存的Scan只花费14ms,IO占用极低。 触发因素:行存表因其存储方式的原因,全表scan的效率较低,频繁的大表全表扫描,导致IO持续占用。 解决办法: 业务侧审视频繁全表count的必要性,降低全表count的频率和并发度。 如果业务类型符合列存表,则将行存表修改为列存表,提高IO效率。
  • 场景7:行存表求max值 例如求某行存表某列的max值,花费了26772ms,此类作业并发起后,整体系统IO持续100%,触发IO瓶颈,导致整体性能慢。 针对max列增加索引后,语句耗时从26s优化到32ms,极大减少IO消耗。 触发因素:行存表max值逐个scan符合条件的值来计算max,当scan的数据量很大时,会持续消耗IO。 解决办法:给max列增加索引,依靠btree索引天然有序的特征,加速扫描过程,降低IO消耗。
  • 场景2:脏数据&数据清理 某SQL总执行时间2.519s,其中Scan占了2.516s,同时该表的扫描最终只扫描到0条符合条件数据,过滤了20480条数据,即总共扫描了20480+0条数据却消耗了2s+,扫描时间与扫描数据量严重不符,此现象可判断为由于脏数据多从而影响扫描和IO效率。 查看表脏页率为99%,Vacuum Full后性能优化到100ms左右。 触发因素:表频繁执行update/delete导致脏数据过多,且长时间未VACUUM FULL清理。 处理方法 对频繁update/delete产生脏数据的表,定期VACUUM FULL,因大表的VACUUM FULL也会消耗大量IO,因此需要在业务低峰时执行,避免加剧业务高峰期IO压力。 当脏数据产生很快,频繁VACUUM FULL也会消耗大量IO,甚至加剧整个系统的IO瓶颈,这时需要考虑脏数据的产生是否合理。针对频繁delete的场景,可以考虑如下方案: 全量delete修改为truncate或者使用临时表替代。 定期delete某时间段数据,设计成分区表并使用truncate&drop分区替代。
  • 场景1:列存小CU膨胀 某业务SQL查询出390871条数据需43248ms,分析计划主要耗时在Cstore Scan。 Cstore Scan的详细信息中,每个DN扫描出2w左右的数据,但是扫描了有数据的CU(CUSome)155079个,没有数据的CU(CUNone) 156375个,说明当前小CU、未命中数据的CU极多,即CU膨胀严重。 触发因素:对列存表(尤其是分区表)进行高频小批量导入会造成CU膨胀。 处理方法 列存表的数据入库方式修改为攒批入库,单分区单批次入库数据量大于DN个数*6W为宜。 如果确因业务原因无法攒批,则考虑次选方案,定期VACUUM FULL此类高频小批量导入的列存表。 当小CU膨胀很快时,频繁VACUUM FULL也会消耗大量IO,甚至加剧整个系统的IO瓶颈,这时需考虑整改为行存表(CU长期膨胀严重的情况下,列存的存储空间优势和顺序扫描性能优势将不复存在)。
  • 场景3:表存储倾斜 例如表Scan的A-time中,max time DN执行耗时6554ms,min time DN耗时0s,DN之间扫描差异超过10倍以上,这种集合Scan的详细信息,基本可以确定为表存储倾斜导致。 通过table_distribution发现所有数据倾斜到了dn_6009单个DN,修改分布列使得表存储分布均匀后,max dn time和min dn time基本维持在相同水平400ms左右,Scan时间从6554ms优化到431ms。 触发因素:分布式场景,表分布列选择不合理会导致存储倾斜,同时导致DN间压力失衡,单DN IO压力大,整体IO效率下降。 解决办法:修改表的分布列使表的存储分布均匀,分布列选择原则参见选择分布列。
  • 原因分析 GaussDB(DWS)支持Hash、REPLICATION和ROUNDROBIN(8.1.2集群及以上版本支持ROUNDROBIN)分布方式。如果创建了Hash分布的表,未指定分布键,则选择表的第一列作为分布键,这种情况就可能存在倾斜。倾斜造成以下负面影响: SQL的性能会非常差,因为数据只分布在部分DN,那么SQL运行的时候就只有部分DN参与计算,没有发挥分布式的优势。 会导致资源倾斜,尤其是磁盘。可能部分磁盘的空间已经接近极限,但是其他磁盘利用率很低。 可能出现部分节点CPU过高等等问题。
  • 写入性能优化 基于Elasticsearch的数据写入流程分析,有以下几种性能优化方案。 表1 写入性能优化 序号 优化方案 方案说明 1 使用SSD盘或升级集群配置 使用SSD盘可以大幅提升数据写入与merge操作的速度,对应到CSS服务,建议选择“超高IO型”存储,或者超高IO型主机。 2 采用Bulk API 客户端采用批量数据的写入方式,每次批量写入的数据建议在1~10MB之间。 3 随机生成_id 如果采用指定_id的写入方式,数据写入时会先触发一次查询操作,进而影响数据写入性能。对于不需要通过_id检索数据的场景,建议使用随机生成的_id。 4 设置合适的分片数 分片数建议设置为集群数据节点的倍数,且分片的大小控制在50GB以内。 5 关闭副本 数据写入与查询错峰执行,在数据写入时关闭数据副本,待数据写入完成后再开启副本。 Elasticsearch 7.x版本中关闭副本的命令如下: PUT {index}/_settings{ "number_of_replicas": 0} 6 调整索引的刷新频率 数据批量写入时,可以将索引的刷新频率“refresh_interval”设置为更大的值或者设置为“-1”(表示不刷新),通过减少分片刷新次数提高写入性能。 Elasticsearch 7.x版本中,将更新时间设置为15s的命令如下: PUT {index}/_settings{ "refresh_interval": "15s"} 7 优化写入线程数与写入队列大小 为应对突发流量,可以适当地提升写入线程数与写入队列的大小,防止突发流量导致出现错误状态码为429的情况。 Elasticsearch 7.x版本中,可以修改如下自定义参数实现写入优化:thread_pool.write.size,thread_pool.write.queue_size; 8 设置合适的字段类型 指定集群中各字段的类型,防止Elasticsearch默认将字段猜测为keyword和text的组合类型,增加不必要的数据量。其中keyword用于关键词搜索,text用于全文搜索。 对于不需要索引的字段,建议“index”设置为“false”。 Elasticsearch 7.x版本中,将字段“field1”设置为不建构索引的命令如下: PUT {index}{ "mappings": { "properties": { "field1":{ "type": "text", "index": false } } }} 9 优化shard均衡策略 Elasticsearch默认采用基于磁盘容量大小的Load balance策略,多节点时,尤其是在新扩容的节点上,可能出现shard在各节点上分配不均的问题。为避免这类问题,可以通过设置索引级别的参数“routing.allocation.total_shards_per_node”控制索引分片在各节点的分布情况。此参数可以在索引模板中配置,也可以修改已有索引的setting生效。 修改已有索引的setting的命令如下: PUT {index}/_settings{"index": {"routing.allocation.total_shards_per_node": 2}}
  • 数据写入流程 图1 数据写入流程 当从客户端往Elasticsearch中写入数据时,写入流程如下: 客户端向Node1发送写数据请求,此时Node1为协调节点。 节点Node1根据数据的_id将数据路由到分片2,此时请求会被转发到Node3,并执行写操作。 当主分片写入成功后,它将请求转发到Node2的副本分片上。当副本写入成功后,Node3将向协调节点报告写入成功,协调节点向客户端报告写入成功。 Elasticsearch中的单个索引由一个或多个分片(shard)组成,每个分片包含多个段(Segment),每一个Segment都是一个倒排索引。 图2 Elasticsearch的索引组成 将文档插入Elasticsearch时,文档首先会被写入缓冲区中,然后在刷新时定期从该缓冲区刷新到Segment中。刷新频率由refresh_interval参数控制,默认每1秒刷新一次。 图3 文档插入Elasticsearch的流程