MySQL两千万数据优化&迁移

Wesley13
• 阅读 434

最近有一张2000W条记录的数据表需要优化和迁移。2000W数据对于MySQL来说很尴尬,因为合理的创建索引速度还是挺快的,再怎么优化速度也得不到多大提升。不过这些数据有大量的冗余字段和错误信息,极不方便做统计和分析。所以我需要创建一张新表,把旧表中的数据一条一条取出来优化后放回新表;

一. 清除冗余数据,优化字段结构

2000W数据中,能作为查询条件的字段我们是预知的。所以将这部分数据单独创建新的字段,对于有规则的数据合理改变字段结构,比如身份证就是varchar(18)。对于不重要的数据我们合并后存在一个结构为text的字段。

对于一些有关联的数据我们需要计算,常见的比如身份证种能获取到准确的性别,出生地、生日、年龄。

二. 数据迁移

我们从数据库中取出一条旧数据,再通过计算处理后得到想要的新数据,最后将新数据插入新表。不过在获取新数据时遇到如下问题。

  1. 1. 数据量太大,无法一次获取(2000W数据扔到内存挺可怕的);

    我们可以通过MySQL的limit语法分批获取。比如每次获取50000,SQL语句如下:

    SQL

    select * from table_name limit 15000000,50000;
    

    SQL

    Copy

    通过这种方法能解决数据量太大的问题,但是随着limit的第一个参数越来越大,查询速度会慢的吓人(上面这条SQL执行会花35秒)。时间就是生命,于是我们开始优化SQL语句,优化后变成下面这样:

    SQL

    select * from table_name order by id desc limit 5000000,50000;
    

    SQL

    Copy

    可通过二分法拆分2000W数据,当执行到1000W数据时,将数据倒序。优化后SQL执行效率显著提升,从35秒降到9秒;

    不过还是很慢,时间就是生命……还好我们有自增ID(创建数据表第一条定律,一定要有自增字段),优化后的SQl如下:

    SQL

    1. select * from table_name where id>15000000 and id<15050000;
    2. select * from table_name where id>15000000 limit 50000; 
    

    SQL

    Copy

    为了直观演示,我写了两条功能一样的SQL。相比第一条,第二条的limit会导致SQL的索引命中变差,效率同样也会下降。第一条SQL的执行时间是2毫秒,第二条执行时间5毫秒(我取的平均值)。每次数据的查询速度直接从35秒降到2毫秒……

  2. 2. 数据量太大并且数据无法预估,某些特殊数据会导致数据导入失败;

    我们有三种方案去将新数据存入新表,分别如下:

    1. 1. 一条一条插入数据;

      开始肯定会想这种方案一定不行,因为每次插入都会有一次数据库IO操作。但是该方案有个好处是能及时发现有问题的数据,修改后再继续执行; 在Oracle中使用『绑定变量』能带来性能提升,正好MySQL也提供了『绑定变量』的功能。于是在不改变逻辑的情况下,尝试优化数据存储速度。代码如下:

      PHP

      public function actionTest(array $data)
      {
          $mysqli = new mysqli("192.168.1.106", "username", "password", "test");
          $sql = "insert into table_name(name,identity) values (?,?)";
      
          $stmt = $connection->prepare($sql);
          $name = "";
          $identity = "";
          //使用绑定变量
          $stmt->bind_param("si", $name, $identity);
          foreach($data as $val)
          {
              $name = $val[name];
              $identity = $val[card_id];
              //执行
              $stmt->execute();
          }
          $stmt->close();
      }
      

      PHP

      Copy

      最后效果不怎么好,MySQL的『绑定变量』并没带来明显的速度提升,不过能有效的防止SQL注入;

    2. 2. 一次插入50000条数据;

      这是我最后选中的方案,一是能及时发现有问题的数据,二是导入数据非常稳定。就像支持断点续传一样,每一步都能看到效果。在执行脚本时,也能同步开始写分析逻辑;

    3. 3. 组装成SQL文件,最后统一导入; 

      组装一个大的SQL文件,最后通过MySQL自带的工具导入也是极好的。但如果有一条SQL有问题,你可能需要重跑一次脚本。因为在9G大小的文本文件中修改一个符号是很痛苦的事情……

三. 总结

通过各种优化,最后将脚本执行时间缩短到了20分钟内。优化后数据质量得到了较高保证,下次将尝试2亿数据的优化&迁移……

作者: 极客导航(http://it2048.cn/)极客博客(http://blog.it2048.cn/) 
文章地址: http://blog.it2048.cn/article\_2000w-data.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这