热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

expdp导出表结构_超强技术案例!86万张表迁移的优化历程

本文转载自:华为云社区86万张表迁移的优化历程问题背景:2019年12月份的时候DRS项目组接到了一个线上问题:XX客户将mysql数据库

c13d9e18d3dfa3b9923ce7a46b3ac11a.png

本文转载自:华为云社区

86万张表迁移的优化历程问题背景:2019年12月份的时候DRS项目组接到了一个线上问题:XX客户将mysql数据库从友商迁移至华为云的时候性能很慢,而且出现报错。运维同学定位发现用户的某几个实例存在大约2000个库,86万张表,而出错的原因是查询源库数据库超时。和开发同学联系后得到了第一个解决方案:增大和源库的socketTimeout值,保证查询不超时。然而,运维同学在后续保障的过程中发现……

01

86万张表迁移的优化历程

  • 问题背景

2019年12月份的时候DRS项目组接到了一个线上问题:XX客户将mysql数据库从友商迁移至华为云的时候性能很慢,而且出现报错。运维同学定位发现用户的某几个实例存在大约2000个库,86万张表,而出错的原因是查询源库数据库超时。和开发同学联系后得到了第一个解决方案:增大和源库的socketTimeout值,保证查询不超时。然而,运维同学在后续保障的过程中发现超时问题得以解决但是性能无法保证,而且容易出现OOM问题。客户要求在一天内要完成割接,然而我们目前的迁移时间远超24个小时,由于项目的进度问题,留给DRS的时间只剩一个周末。

02

问题定位过程

  • 问题定位

饭要一口口吃,问题也得一个个定位,回到最初的超时问题,我们首先找到了出现超时的sql语句:

SELECT TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = '****';

这个sql用于查询每个库下面的所有触发器(其他对象的查询sql也有这个问题),我们发现这个sql执行了超过5分钟,这个结果令我们很意外。因为理论上而言只查询一个库下面的所有触发器,怎么会这么慢呢?explain这个sql我们发现:

aced798bdcff240f9c1f84cbd58a0102.png

Extra中表示Scanned all databases!!!这意味着虽然我们只是在查询一个库下面的触发器,但是其实它会扫描所有库对应的frm文件。DEBUG了一下mysql的源码,我们发现虽然只是查询了一个库下面的trigger,但是由于information_schema.triggers表里面并不是一张物理表,导致每次查询都会去打开所有的frm文件,我们知道frm文件是用来存放mysql元数据信息的物理文件,对于用户这个表数量,可想而知这个会对用户的源库IO造成多大的影响,即使有buffer pool也扛不住这么多表的缓存。更关键的是,用户有2000个库,也就是说单就查询trigger这一个对象,我们就需要耗费2000*5=10000分钟的时间,迁移性能可想而知。

  • 优化思路

有了上面的结果,其实采取的优化方式很简单了。首先,我们根据对象的类型来区分查询所有对象的方式,对于表,我们可以使用逐库查询,因为查询表的操作不会Scanned all databases;对于其他对象,我们使用只查询一次的sql:

SELECT TRIGGER_SCHEMA, TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA NOT in('mysql','information_schema','sys', 'performance_schema');

这样我们的查询时间大大缩短!

然而,现实是残酷的,虽然我们缩短了从源库查询对象列表的时间,但是导出对象到回放对象的性能还是不够出色,不能满足用户的12小时时间限制,此外偶发的OOM问题也随之没有解决。

03

OOM问题

  • 问题定位

用MAT分析了heap堆存储后发现,heap里面存在大量的DbSqlData类的对象,正是大量的这种对象导致堆内存溢出。结合我们的架构可以发现,我们在导出表结构的时候会先把所有对象结构分类型存储在ehcache中,然后在读取的时候又会把数据按类型导出到内存中。针对用户的场景,我们可以计算出(用户的表结构平均占用堆内存4000Byte)86万张表总共需要3.44G,而我们的永久代内存只配置了2G,OOM问题可想而知。

if [ -z "$REPLICATOR_HEAP_SIZE" ]; then
  REPLICATOR_HEAP_SIZE="-Xms3072m -Xmx3072m -XX:NewRatio=3 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"
fi

  • 优化思路

其实这一切的问题来源都是因为我们按对象类型对ehcache进行存取,这种方式不是流式进行的,很有可能发生OOM问题。因此,解决的思路就是我们按照单个对象的粒度进行存取,并且使用内存控制的方式控制读到堆内的对象数目,保证流式结构的回放,如何进行流式结构的回放将会在下面介绍。

04

性能问题

  • 问题定位

为了满足客户的迁移时间需求,我们统计了各个阶段DRS内核的工作时间,大致分为下面几个阶段:

bcce95c785b9f5bb55039f545275fbf4.png

这三个阶段的执行顺序是串行执行的,其中查询对象列表花了共计1个小时,导出对象结构共计10个小时,回放共计8个小时,总计19个小时。这个结果远远超出了客户的预期,为了解决这个问题,我们对整个迁移流程进行了重新梳理,发现有以下2个改进点:

1.查询对象列表的同时可以将已查询对象列表的结构导出2.导出对象结构可以由单线程导出演进为多线程并发导出

按照上述优化流程,假定查询对象列表时间为A,单线程导出结构所需时间为B,根据CPU核数/IO线程比我们设定导出结构的线程数为8,在源库性能足够的情形下,查询对象列表+导出对象结构的时间应该等于

CostTime(hour)=max(A, B/8) —— 约等于2

这样我们的总时间为2 + 8 = 10小时,满足客户的需求!

  • 优化思路

有了上面的分析,结合OOM问题和查询时间优化的思路,我们有了以下的设计:

690c6de7aa27ee12058203d601b1d36d.png

上面图略去了数据的回放模型,主要突出了结构的回放,同时在两个store中加入了内存控制,防止出现OOM问题。

05

优化结果

在通宵达旦的开发和验证后,我们终于构建了以上的框架,并且成功将86万张表的总时长优化到了10小时以内,更为可喜的是整个流程中的full gc次数为0,最终客户的需求得以实现,技术人员也从中学习到了新的知识。

  • 思考

优化是无止境的,其实上述的架构还存在优化空间,比如以下的两点:

1.结构的导入和数据的回放可以并行执行,针对用户表多数据少的场景,统计发现表结构的导入花了4个小时而数据的导入也只花了4个小时,这一个阶段可以优化(DRS已经做了这个优化,会在后续的博客中给大家科普实现方式)2.结构的导出和结构回放是否可以并行执行,他们之间的限制关系是什么?

如果有小伙伴对我们的架构有看法也可以积极留言,我们会去认真观摩!

1a83cc049081235ce6290138d5557815.png

↓点击



推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了一个误删Oracle数据文件导致数据库无法打开的问题,并提供了解决方式。解决方式包括切换到mount状态、离线删除报错的数据文件等。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 基于事件驱动的并发编程及其消息通信机制的同步与异步、阻塞与非阻塞、IO模型的分类
    本文介绍了基于事件驱动的并发编程中的消息通信机制,包括同步和异步的概念及其区别,阻塞和非阻塞的状态,以及IO模型的分类。同步阻塞IO、同步非阻塞IO、异步阻塞IO和异步非阻塞IO等不同的IO模型被详细解释。这些概念和模型对于理解并发编程中的消息通信和IO操作具有重要意义。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文介绍了如何使用PHP代码将表格导出为UTF8格式的Excel文件。首先,需要连接到数据库并获取表格的列名。然后,设置文件名和文件指针,并将内容写入文件。最后,设置响应头部,将文件作为附件下载。 ... [详细]
  • 本文介绍了使用数据库管理员用户执行onstat -l命令来监控GBase8s数据库的物理日志和逻辑日志的使用情况,并强调了对已使用的逻辑日志是否及时备份的重要性。同时提供了监控方法和注意事项。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文提供了关于数据库设计的建议和注意事项,包括字段类型选择、命名规则、日期的加入、索引的使用、主键的选择、NULL处理、网络带宽消耗的减少、事务粒度的控制等方面的建议。同时还介绍了使用Window Functions进行数据处理的方法。通过遵循这些建议,可以提高数据库的性能和可维护性。 ... [详细]
  • 本文介绍了在PostgreSQL中批量导入数据时的优化方法。包括使用unlogged表、删除重建索引、删除重建外键、禁用触发器、使用COPY方法、批量插入等。同时还提到了一些参数优化的注意事项,如设置effective_cache_size、shared_buffer等,并强调了在导入大量数据后使用analyze命令重新收集统计信息的重要性。 ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
author-avatar
福田商务汽车--南宁鑫来
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有