• 【原创】关于表结构更改的大量数据的迁移方案


    前言

    由于公司要求做产品之间的对接,需要统一表结构和字段名,但是有些表有上亿条数据,如果直接修改表字段名和加字段每一条SQL都要执行大量时间,大概时间为加一个字段1.5小时/1亿条数据,这耗时实在太长了,而且有些表需要涉及6~8个字段更新,这么耗时显然是不行的。因此需要别的解决方案。

    总体解决方案

    第一步:先创建新表,新表包含了目前最新的表结构。

    第二步:使用insert select方式从目前的表查询数据,然后insert到新表中,一切操作都在MYSQL内部执行,且insert完的数据就是修改完表结构的数据, 也就是说insert完一遍后表结构更改也就完成了。

    第三步:将目前的表的表名改成别的,将新表改成原来的表名即可,修改表名是非常快的。

    遇到的问题

    1、insert select这个思路是对的,就是select太坑了,我去傻乎乎的直接去select没加limit,导致一下子查询了上亿条数据,导致服务器内存直接100%,MYSQL因为内存不够直接crash了!这下出大事了!赶紧让现场人员重启MYSQL,还好只是内存溢出导致的crash,数据本身没有受到影响。

    大家千万别我犯一样的错啊!!

    2、有些新增字段没有数据怎么办

    问题解决方案

    问题一:

    使用MyBatis,先select count(0),统计出所有数据的量,然后通过for循环的方式去分页select,这样就能保证内存不溢出了。

    问题二:

    这个就很简单了,能够用mysql直接算出来的就用mysql直接算出来就行了,比如缺时间,那就用NOW()补充就完事了,有些新增列有默认值的就不管了,或者insert一个默认值进去,以后用到了再更新都行。

    上代码

    由于公司的代码必须严格保密,这里就是用最最最常用的学生表来举个栗子:

    假设从表my_student(旧表)迁移数据到dct_student(新表),my_student表的结构为:

    dct_student表的结构为:

     my_student表数据为:

     使用SQL:

    1. insert into dct_student (
    2. stu_id,
    3. stu_name,
    4. gender,
    5. state,
    6. birthday,
    7. register_time,
    8. father_name,
    9. mother_name,
    10. del,
    11. create_time,
    12. update_time)
    13. select
    14. id,
    15. name,
    16. gender,
    17. 1,
    18. birthday,
    19. register_time,
    20. NULL,
    21. NULL,
    22. del,
    23. create_time,
    24. NOW()
    25. from my_student limit 0,500000;

     即可完成迁移。

    注意!limit必须要加防止因为数据库内数据量过大,导致服务器内存不够,结果导致MYSQL崩溃。因此limit内的值不能过大,过大内存可能扛不住,过小则每次迁移的量实在太小,我个人测试下来50W条一次差不多。

    那可能就有人要问了,如果数据量超过50W条怎么办,难道还得手动编辑分页不成?

    那这就得交给Java程序了,首先先selectCount一下,算出一共多少条数据,然后按50W条进行分页,然后每页执行这个insert select即可。

    大概的Mapper长这样:

    1. @Mapper
    2. public interface DctStudentMapper extends BaseMapper {
    3. @Select("insert into dct_student (\n" +
    4. "stu_id,\n" +
    5. "stu_name,\n" +
    6. "gender,\n" +
    7. "state,\n" +
    8. "birthday,\n" +
    9. "register_time,\n" +
    10. "father_name,\n" +
    11. "mother_name,\n" +
    12. "del,\n" +
    13. "create_time,\n" +
    14. "update_time)\n" +
    15. "\n" +
    16. "select \n" +
    17. "id,\n" +
    18. "name,\n" +
    19. "gender,\n" +
    20. "1,\n" +
    21. "birthday,\n" +
    22. "register_time,\n" +
    23. "NULL,\n" +
    24. "NULL,\n" +
    25. "del,\n" +
    26. "create_time,\n" +
    27. "NOW()\n" +
    28. "from my_student limit #{pageStart}, #{pageSize};")
    29. void pageInsertSelect(long pageStart,int pageSize);
    30. }

    统计完总数后一个for循环就搞定了,这我就不再赘述了,相信大家都会操作。

    迁移完成的结果长这样:

    时间统计

    公司里的项目试过,1亿条数据,修改一个字段耗时大概90分钟,6个字段就要大概7小时。而这个insert select1亿条数据大概1小时多点就能完成,比修改字段快太多。

     

  • 相关阅读:
    Android 10 如何在通知栏下拉状态栏会暂停第三方应用播放视频
    SI好文翻译:铜表面纹理对损耗的影响:一个有效的模型(一)
    具有独特底部轮廓的剥离光刻胶的开发
    Oracle/PLSQL: Sinh Function
    一文了解Go语言的函数
    运维专题.Docker+Nginx服务器的SSL证书安装
    本地离线模型搭建指南-中文大语言模型底座选择依据
    NVIDIA:应将USD作为3D互联网的HTML标准语言
    浅析如何在抖音快速通过新手期并积累粉丝
    uboot启动学习笔记六-uboot启动参数传递及第二阶段函数board_init_f
  • 原文地址:https://blog.csdn.net/DCTANT/article/details/128075930