华为云用户手册

  • 分析过程 和客户确认是部分业务慢,可以提供部分慢sql,打印执行计划,耗时主要在index scan上,怀疑是IO争抢导致,通过监控IO,发现并没有IO资源使用瓶颈。 查询当前活跃sql,发现有大量的create index语句,需要和客户确认该业务是否合理。 1 SELECT * from pg_stat_activity where state !='idle' and usename !='Ruby'; 根据执行计划,发现在部分DN上耗时较高,查询表的倾斜情况,并未发现有倾斜的情况。 1 SELECT table_skewness('table name'); 联系运维人员登录集群实例,检查内存相关参数,设置不合理,需要优化。 单节点总内存大小为256G max_process_memory为12G,设置过小 shared_buffers为32M,设置过小 work_mem:CN:64M 、DN:64M max_active_statements: -1(不限制并发数) 按以下值设置: gs_guc set -Z coordinator -Z datanode -N all -I all -c "max_process_memory=25GB" gs_guc set -Z coordinator -Z datanode -N all -I all -c "shared_buffers=8GB" gs_guc set -Z coordinator -Z datanode -N all -I all -c "work_mem=128MB" 进一步分析扫描慢的原因,发现表数据膨胀严重,对其中一张8G大小的表,总数据量5万条,做完vacuum full后大小减小为5.6M。
  • 处理方法 请使用具有schema权限的用户登录数据库,执行以下命令将schema中的表权限赋给指定的用户: 1 2 GRANT USAGE ON SCHEMA schema_name TO u1; GRANT SELECT ON ALL TABLES IN SCHEMA schema_name TO u1; 执行以下命令,将schema中未来新建的表的权限也赋予指定的用户: 1 ALTER DEFAULT PRIVILEGES IN SCHEMA schema_name GRANT SELECT ON TABLES TO u1; 上述SQL语句中的“GRANT SELECT”表示赋予的是表的查询权限,如果需要给其他用户赋予表的其他权限,请参考GRANT语法说明。 如果需要给某个用户赋权“可查询数据库所有schema里所有表”,可通过系统表PG_NAMESPACE查询出schema后授权。例如: 1 SELECT 'grant select on all tables in '|| nspname || 'to u1' FROM pg_namespace;
  • 问题现象 连接 GaussDB (DWS) 数据库时报错,提示客户端连接数太多。 使用gsql等SQL客户端工具连接数据库时,出现如下所示的报错信息: 1 FATAL: Already too many clients, active/non-active/reserved: 5/508/3. 使用客户端并发连接数据库时,出现如下所示的报错信息: 1 2 [2019/12/25 08:30:35] [ERROR] ERROR: pooler: failed to create connections in parallel mode for thread 140530192938752, Error Message: FATAL: dn_6001_6002: Too many clients already, active/non-active: 468/63. FATAL: dn_6001_6002: Too many clients already, active/non-active: 468/63.
  • 处理方法 可优先通过如下方法进行应急处理: 临时将所有non-active的连接释放掉。 1 SELECT PG_TERMINATE_BACKEND(pid) from pg_stat_activity WHERE state='idle'; 在GaussDB(DWS) 控制台设置会话闲置超时时长session_timeout,在闲置会话超过所设定的时间后服务端将主动关闭连接。 session_timeout默认值为600秒,设置为0表示关闭超时限制,一般不建议设置为0。 session_timeout设置方法如下: 登录GaussDB(DWS) 管理控制台。 在左侧导航栏中,单击“集群管理”。 在集群列表中找到所需要的集群,单击集群名称,进入集群“基本信息”页面。 单击“参数修改”页签,修改参数“session_timeout”,然后单击“保存”。 在“修改预览”窗口,确认修改无误后,单击“保存”。 如果上述方式未能满足业务需求,可继续参考以下方式进行处理: 当前数据库连接已经超过了最大连接数场景处理方式: 查看CN上的连接来自哪里,总数量以及是否超过当前max_connections(默认值CN节点为800,DN节点为5000)。 1 SELECT coorname, client_addr, count(1) FROM pgxc_stat_activity group by coorname, client_addr order by coorname; 判断是否可以调大max_connections。调整策略如下: 调大CN的max_connections会造成并发连接到DN的查询变多,所以需要同步调大DN的max_connections和comm_max_stream。 CN/DN的max_connections一般按照原来的2倍调大,原值比较小的集群可以调节到4倍。 为避免在准备步骤失败,max_prepared_transactions的值不能小于max_connections,建议至少将其设置为等于max_connections,这样每个会话都可以有一个等待中的预备事务。 若上一步中判断可以调整max_connections,则可通过管理控制台调整最大连接数max_connections。 在管理控制台上,集群“基本信息”页面,单击“参数修改”页签,修改参数“max_connections”为合适的值,然后单击“保存”。 针对设置了用户最大连接数的场景处理方式: 在创建用户时由CREATE ROLE命令的CONNECTION LIMIT connlimit子句直接设定,也可以在设定以后用ALTER ROLE的CONNECTION LIMIT connlimit子句修改。 使用PG_ROLES视图查看指定用户的最大连接数。 1 2 3 4 5 SELECT ROLNAME,ROLCONNLIMIT FROM PG_ROLES WHERE ROLNAME='role1'; rolname | rolconnlimit ---------+-------------- role1 | 10 (1 row) 修改用户的最大连接数。 1 ALTER ROLE role1 connection limit 20;
  • 磁盘空间告警订阅 为了减轻客户自行的运维压力,DWS提供了告警订阅的功能:即当集群的磁盘使用率大于设置的阈值时,系统会以短信、邮件形式通知到用户。 设置告警阈值: 登录DWS控制台,左侧选择“告警管理”,单击“告警规则管理”。 在告警规则列表中,单击“节点数据盘使用率超阈值”右侧的“修改”。 在集群列表中单击指定集群,进入集群详情页面。告警策略设置如下: 规则绑定集群:所有集群 告警策略:将数据盘的重要告警配置为70%,持续10分钟,紧急告警配置为75%,持续10分钟。如下设置: 在 消息通知 服务( SMN )创建主题。 切换到消息通知服务控制台,单击“创建主题”。如下设置“主题名称”和“企业项目”。 主题创建成功后,单击右侧的“添加订阅”。根据需要选择“短信”、“邮件”方式,订阅终端输入对应的手机号或邮箱地址。 输入的手机号或邮箱地址会受到确认短信或邮件,单击确认,即可完成订阅。 添加告警订阅。 回到DWS控制台,左侧选择“告警管理”,切换到“订阅”,单击“创建订阅”。 订阅名称输入“dms_alarm”,告警级别选择“紧急”、“重要”,消息通知主题名称选择上一步创建的主题“dms_alarm”。 单击“确认”。整个告警订阅成功,后续磁盘使用率大于70%和75%则发出通知。
  • 单语句空间管控 GaussDB(DWS)支持语句磁盘空间管控相关的参数sql_use_spacelimit和temp_file_limit,用于业务运行时,避免由于不合理的数据量,引发磁盘空间暴增,触发告警/只读,主动识别不合理的大批量倒数业务或者高数据量入库业务。 登录DWS控制台,左侧单击“集群管理”,在集群列表中单击指定集群,进入集群详情页面。 单击“参数修改”,搜索栏中搜索sql_use_spacelimit和temp_file_limit(参见磁盘空间),根据业务进行调整。 建议设置sql_use_spacelimit为实例所在磁盘空间总容量的10%(例如购买时空间为100G/每节点,则配置该参数为10G)。 上述配置生效后,如果单语句运行过程中超过该配置参数,则SQL语句会被主动中止。如需临时放开,可以在session会话中执行以下语句进行临时关闭。 1 SET sql_use_spacelimit=0;
  • 原因分析 增加分区时需同时满足以下条件: 新增分区名不能与已有分区名相同。 新增分区的边界值必须大于最后一个分区的上边界。 新增分区的边界值要和分区表的分区键的类型一致。 已有分区p1的边界为(-∞,20221010),而新增分区p0的上边界为20221009,落在分区p1内;已有分区p4的边界为[20221012,+∞),而新增分区p5的上界为20221013,落在分区p4内。新增分区p0、p5不满足使用ADD PARTITION增加分区的条件,因此执行新增分区语句报错。
  • 处理方法 使用ALTER TABLE SPLIT PARTITION分割已有分区,也能达到新增分区的目的。同样, SPLIT PARTITION的新分区名称也不能与已有分区相同。 使用split子句分割p4分区[20221012,+∞)为p4a分区范围为[20221012,20221013)和p4b分区范围为[20221013,+∞)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ——SPLIT PARTITION分割前的分区 SELECT relname, boundaries FROM pg_partition p where p.parentid='studentinfo'::regclass ORDER BY 1; relname | boundaries -------------+------------------------- p1 | {"2022-10-10 00:00:00"} p2 | {"2022-10-11 00:00:00"} p3 | {"2022-10-12 00:00:00"} p4 | {NULL} studentinfo | (5 rows) ALTER TABLE studentinfo SPLIT PARTITION p1 AT('2022-10-09 00:00:00+08') INTO (PARTITION P1a,PARTITION P1b); ALTER TABLE studentinfo SPLIT PARTITION p4 AT('2022-10-13 00:00:00+08') INTO (PARTITION P4a,PARTITION P4b); ——执行SPLIT PARTITION分割后的分区 SELECT relname, boundaries FROM pg_partition p where p.parentid='studentinfo'::regclass ORDER BY 1; relname | boundaries -------------+------------------------- p1a | {"2022-10-09 00:00:00"} p1b | {"2022-10-10 00:00:00"} p2 | {"2022-10-11 00:00:00"} p3 | {"2022-10-12 00:00:00"} p4a | {"2022-10-13 00:00:00"} p4b | {NULL} studentinfo | (7 rows) 如果对分区名称有要求,可以在分割后再使用rename partition统一分区名。 ALTER TABLE studentinfo RENAME PARTITION p1a to p0;
  • 问题现象 创建范围分区表后增加新的分区,使用ALTER TABLE ADD PARTITION语句报错upper boundary of adding partition MUST overtop last existing partition。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ——创建范围分区表studentinfo CREATE TABLE studentinfo (stuno smallint, sname varchar(20), score varchar(20), examate timestamp) PARTITION BY RANGE (examate) ( PARTITION p1 VALUES LESS THAN ('2022-10-10 00:00:00+08'), PARTITION p2 VALUES LESS THAN ('2022-10-11 00:00:00+08'), PARTITION p3 VALUES LESS THAN ('2022-10-12 00:00:00+08'), PARTITION p4 VALUES LESS THAN (maxvalue) ); ——添加边界值为2022-10-9 00:00:00+08的分区p0 ALTER TABLE studentinfo ADD PARTITION p0 values less than ('2022-10-9 00:00:00+08'); ERROR: the boundary of partition "p0" is less than previous partition's boundary ——添加边界值为2022-10-13 00:00:00+08的分区p5 ALTER TABLE studentinfo ADD PARTITION p5 values less than ('2022-10-13 00:00:00+08'); ERROR: the boundary of partition "p5" is equal to previous partition's boundary
  • 场景三:系统表过大导致VACUUM FULL执行慢 在排除IO/网络问题后,对空表执行VACUUM FULL,即使是空表执行VACUUM FULL也比较慢,则说明是系统表较大导致。因为VACUUM FULL任意一张表时,都会扫描pg_class、pg_partition、pg_proc三张系统表,当这三个系统表过大时,也会导致VACUUM FULL执行较慢。 处理方法:GaussDB(DWS)支持对系统表执行VACUUM FULL,但是会产生八级锁,涉及相关系统表的业务会被阻塞,注意要在业务空闲时间窗或停止业务期间且没有DDL操作时清理系统表。 有关清理系统表的操作,请参考哪些系统表不能做VACUUM FULL。
  • 场景一:存在锁等待导致VACUUM FULL执行慢 8.1.x及以上集群版本的处理方法: 通过查询pgxc_lock_conflicts视图查看锁冲突情况。 1 SELECT * FROM pgxc_lock_conflicts; 在查询结果中查看granted字段为“f”,表示VACUUM FULL语句正在等待其他锁。granted字段为“t”,表示INSERT语句是持有锁。nodename,表示锁产生的位置,即CN或DN位置,例如cn_5001,继续执行2。 如果查询结果为0 rows,则表示不存在锁冲突。则需排查其它场景。 根据语句内容判断是终止持锁语句后继续执行VACUUM FULL还是在业务低峰期选择合适的时间执行VACUUM FULL。 如果要终止持锁语句,则执行以下语句。pid从上述步骤1获取,cn_5001为所查询到的nodename。 1 execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)'; 语句终止后,再重新执行VACUUM FULL。 1 VACUUM FULL table_name; 8.0.x及以前版本的处理方法: 在数据库中执行语句,获取VACUUM FULL操作对应的query_id。 1 SELECT * FROM pgxc_stat_activity WHERE query LIKE '%vacuum%'AND waiting = 't'; 根据1获取的query_id,执行以下语句查看是否存在锁等待。 1 SELECT * FROM pgxc_thread_wait_status WHERE query_id = {query_id}; 查询结果中“wait_status”存在“acquire lock”表示存在锁等待。同时查看“node_name”显示在对应的CN或DN上存在锁等待,记录相应的CN或DN名称,例如cn_5001或dn_600x_600y,继续执行3。 查询结果中“wait_status”不存在“acquire lock”,排除锁等待情况,继续排查其它场景。 执行以下语句,到等锁的对应CN或DN上从pg_locks中查看VACUUM FULL操作在等待哪个锁。以cn_5001为例,如果在DN上等锁,则改为相应的DN名称。pid为2获取的tid。 回显中记录relation的值。 1 execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE pid = {tid} AND granted = ''f'''; 根据获取的relation,从pg_locks中查看当前持有锁的pid。relation从3获取。 1 execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE relation = {relation} AND granted = ''t'''; 根据pid,执行以下语句查到对应的SQL语句。pid从4获取。 1 execute direct on (cn_5001) 'SELECT query FROM pg_stat_activity WHERE pid ={pid}'; 根据语句内容判断是终止持锁语句后继续执行VACUUM FULL还是在业务低峰期选择合适的时间执行VACUUM FULL。 如果要终止持锁语句,则执行以下语句。pid从上述4获取,cn_5001为所查询到的nodename。 1 execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)'; 语句终止后,再执行VACUUM FULL。 1 VACUUM FULL table_name;
  • 场景四:列存表使用了局部聚簇(PCK)时,VACUUM FULL执行慢 对列存表执行VACUUM FULL时,如果存在PCK,就会将PARTIAL_CLUSTER_ROWS中多条记录全都加载到内存中再进行排序,如果表较大或psort_work_mem设置较小,会导致PCK排序时产生下盘(数据库选择将临时结果暂存到磁盘),进行外部排序;一旦进行外部排序,时间消耗就会增加很多。 处理方法:根据表中数据的tuple length,合理调整PARTIAL_CLUSTER_ROWS和psort_work_mem的大小。 执行以下语句查看表定义。回显中存在“PARTIAL CLUSTER KEY”信息,表示存在PCK。 1 SELECT * FROM pg_get_tabledef('table name'); 登录DWS管理控制台,左侧选择“集群管理”。 单击对应的集群名称,进入集群详情页。 左侧选择“参数修改”,在搜索栏中输入psort_work_mem进行搜索,将CN参数值和DN参数值同时调大,单击“保存”。 再重新执行VACUUM FULL操作。
  • 处理方法 可以通过以下命令找出哪些表做过update及delete操作: 1 2 3 4 5 6 7 8 9 ANALYZE tablename; SELECT n.nspname , c.relname, pg_stat_get_tuples_deleted(x.pcrelid) as deleted, pg_stat_get_tuples_updated(x.pcrelid) as updated FROM pg_class c INNER JOIN pg_namespace n ON n.oid = c.relnamespace INNER JOIN pgxc_class x ON x.pcrelid = c.oid WHERE c.relkind = 'r' and c.relname='tablename' ;
  • 问题现象 DWS中有两种情况需要关注表是否做过update及delete操作: 对表频繁做update或者delete操作会产生大量的磁盘页面碎片,从而逐渐降低查询的效率,需要将磁盘页面碎片恢复并交换操作系统,即vacuum full操作,这时候需要找出那些表做过update; 判断一张表是否是维度表,是否可以从Hash表变更为复制表,可以查看这张表是否做过update或delete,如果做过update或delete操作,则不可以修改为复制表。
  • 场景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产生大量X LOG ,触发主备同步慢及大量IO消耗。 解决措施: 对该类一次性访问大量新数据的场景,修改为列存表 关闭wal_log_hints和enable_crc_check参数(故障期间有丢数风险,不推荐)。
  • 场景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效率下降。 解决办法:修改表的分布列使表的存储分布均匀,分布列选择原则参见选择分布列。
  • 场景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分区替代。
  • 原因分析 MySQL5.0.3之前varchar(n)这里的n表示字节数。 MySQL5.0.3之后varchar(n)这里的n表示字符数,比如varchar(200),不管是英文还是中文都可以存放200个。 GaussDB(DWS)的varchar(n)这里的n表示字节数。 根据字符集,字符类型如果为gbk,每个字符占用2个字节;字符类型如果为utf8,每个字符最多占用3个字节。根据转换规则,同样的字段长度,会导致GaussDB(DWS)出现字段超长的问题。
  • 处理方法 根据评估,内存充足,将comm_max_stream参数值调大为2048。(参数值2048仅适用示例场景,请根据实际业务的内存查询结果进行参数值调整。) 登录GaussDB(DWS) 管理控制台。 在左侧导航栏中,单击“集群管理”。 在集群列表中找到所需要的集群,单击集群名称,进入集群“基本信息”页面。 单击“参数修改”页签,修改参数“comm_max_stream”,然后单击“保存”。 在“修改预览”窗口,确认修改无误后,单击“保存”。 参数“comm_max_stream”所在行“是否重启”列显示为“否”,表示该参数修改后无需进行重启操作,即修改后立即生效。
  • 分析过程 GUC参数comm_max_stream表示任意两个DN之间stream的最大数量。 在CN上查询当前任意两个DN之间stream情况: 1 SELECT node_name,remote_name,count(*) FROM pgxc_comm_send_stream group by 1,2 order by 3 desc limit 100; 在DN上查询当前DN与其他DN之间stream情况: 1 SELECT node_name,remote_name,count(*) FROM pg_comm_send_stream group by 1,2 order by 3 desc limit 100; comm_max_stream参数值必须大于并发数*每并发平均stream算子数*(smp的平方)。 该参数默认值为:通过公式min(query_dop_limit * query_dop_limit * 2 * 20, max_process_memory(字节) * 0.025 /(最大CN数+当前DN数) / 260)计算,小于1024按照1024取值,其中query_dop_limit = 单个机器CPU核数 / 单个机器DN数。 不建议该参数值设置过大,因为comm_max_stream会占用内存(占用内存=256byte*comm_max_stream*comm_max_datanode),如果并发数据流数过大,查询较为复杂及smp过大都会导致内存不足。 如果comm_max_datanode参数值较小,进程内存充足,可以适当将comm_max_stream值调大。
  • 原因分析 表关联的toast表的data发生损坏。 toast是The OverSized Attribute Storage Technique(超尺寸字段存储技术)的缩写,是超长字段在GaussDB(DWS)的一种存储方式。当某表中有超长字段的时候,那么该表会有与之相关联的toast表。根据toast表的命名规则,假设存在表名为test的OID为2619,那么如果存在与之相关联的toast表,则toast表名为pg_toast_2619。此报错中pg_toast_2619非固定表名,可根据实际报错对pg_toast_2619进行替换。
  • 处理方法 通过toast的OID(示例中为2619,来源于报错信息的pg_toast_2619)查询出哪张表发生了损坏: 1 2 3 4 5 SELECT 2619::regclass; regclass -------------- pg_statistic (1 row) 对已定位的损坏表(步骤1中查询得到的表pg_statistic),执行REINDEX和VACUUM ANALYZE操作。显示REINDEX/VACUUM,表示修复完成。如果修复过程中出现报错,请继续执行步骤3。 1 2 3 REINDEX table pg_toast.pg_toast_2619; REINDEX table pg_statistic; VACUUM ANALYZE pg_statistic; 执行以下的命令定位该表中损坏的数据行。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 DO $$ declare v_rec record; BEGIN for v_rec in SELECT * FROM pg_statistic loop raise notice 'Parameter is: %', v_rec.ctid; raise notice 'Parameter is: %', v_rec; end loop; END; $$ LANGUAGE plpgsql; NOTICE: 00000: Parameter is: (46,9) ERROR: XX000: missing chunk number 0 for toast value 30982 in pg_toast_2619 CONTEXT: PL/pgSQL function inline_code_block line 7 at RAISE 将步骤3中定位的损坏数据行记录删除。 1 DELETE FROM pg_statistic WHERE ctid ='(46,9)'; 重复执行步骤3、步骤4,直到全部有问题的数据记录被删除。 损坏的数据行被删除后,执行步骤2中的REINDEX和VACUUM ANALYZE操作对该表重新进行修复。
  • 问题现象 场景一:执行 vacuum full 时报错:Can't fit xid into page, now xid is 34181619720, base is 29832807366, min is 3, max is 3. 场景二:其他非vacuum full操作,例如某局点给用户赋权function时,报错信息如下:Can't fit xid into page. relation "pg_proc", now xid is 34181619720, base is 29832807366, min is 3, max is 3.
共100000条