写脚本的一些心得-----------------------历史数据迁移到分表

历史数据迁移到分表的。(以前单表几十G的表,需要做优化分表)

背景:

之前项目因为历史原因使用的是mssql,其中有大量的各种log表,需要完整地迁移到mysql的按照日期分表的分表里。由于数据量大和表当初设计的一些缺陷,所以在迁移的时候要考虑查询效率和执行效率问题。

我采用的方式如下:

每一个表一个function去处理.其实写这种导数据的应该对传入参数减少。
我当初写的是开始时间和天数的传入,然后根据时间戳算出连续时间的字符串,用来去生成对应的天表表面中的字符串。
其实后来想想,完全可以传入2个时间段,结束的时间可以不传,默认为执行该脚本当天。
代码如下:

public function moveGameClickLogToMySql()
{
    // 根据迁移日期查询数据(from mssql)
    $startDate = $this->input->get_post(‘startTime‘);

    if (!$startDay) exit(‘请输入开始日期‘);
    $endDate = $this->input->get_post(‘endTime‘);

    $dateArray = $this->getSqeDate($startDate, $endDate);
    $upTime = $this->input->get_post(‘upTime‘); // 上线时间 ,时间戳
    $upDate = date(‘Ymd‘, $upTime);
    // tGameClickLog
    set_time_limit(0);
    $searchDate = array();
    foreach ($dateArray as $d) {
        array_push($searchDate, date(‘Y-m-d H:i:s‘, strtotime($d)));
    }
    $mysql = $this->load->database(‘mysqllog‘, true);

    foreach ($searchDate as $day) {
        $startTime = $day;
        $endTime = date(‘Y-m-d 23:59:59‘, strtotime($day) + 1);

        $sqlStart = "select min(id) as start_id from someClick where dtInsert>=‘{$startTime}‘";//" and dtInsert<= ‘{$endTime}‘";
        $sqlEnd = "select min(id) as end_id from tGameClickLog where dtInsert>=‘{$endTime}‘";
        $startRes = $this->mssql->getOne($sqlStart);

        $endRes = $this->mssql->getOne($sqlEnd);

        if ($startRes && $endRes) {
            $end_id = $endRes[‘end_id‘] - 1;
            $start_id = $startRes[‘start_id‘];
            echo "coming in <br/>";

            $oneDaySql = "select * from tGameClickLog where id >= {$startRes[‘start_id‘]} and id <= {$end_id} ";
            $countSql = "select count(id) as count from someClick where id >= {$startRes[‘start_id‘]} and id <= {$end_id}";
            $count = $this->mssql->getOne($countSql);
            $countNum = $count[‘count‘];
            //var_dump($count);die();
            // 每10000条查询一次
            $times = ceil($countNum / 10000);

            // 插入mysql
            $d = date(‘Ymd‘, strtotime($day));
            $tableName = ‘game_click_log_‘ . $d;
            // 先清空原来该分表数据
            $where = ‘‘;
            if ($upDate == $d) $where = ‘ where created <=‘ . $upTime;
            $mysql->query(‘delete from ‘ . $tableName . $where);

            for ($i = 1; $i <= $times; $i++) {
                $insertData = array();
                // 分批查询和插入
                $partData = $this->mssql->select($oneDaySql, array(), $i, 10000);
                foreach ($partData as $p) {
                    // 特殊处理原表中的插入时间
                    $created = (array)$p[‘dtInsert‘];
                    $created = strtotime($created[‘date‘]);
                    $insertData[] = array(
                        ‘netbar_id‘ => $p[‘iUserID‘],
                        ‘game_id‘ => $p[‘iGameID‘],
                        ‘click‘ => $p[‘iClick‘],
                    // some other colums
                    );
                }
                $allData = array_chunk($insertData, 1000); // 分批插入
                $num = count($allData);
                for ($n = 0; $n < $num; $n++) {
                    $mysql->insert_batch($tableName, $allData[$n]);
                }
            }
        } else {
            continue;
        }
        var_dump(‘finish insert ‘ . $d . " data");
    }

}

整个代码中其实大致总结一下步骤:
1.处理开始时间和结束时间,先生成连续时间字符串数组,比如我传入 2015-11-12,第二个参数结束日期不传。
那么通过getSqeDate函数可以生成
array(
‘2015-11-12‘,
‘2015-11-13‘,
‘2015-11-14‘,
.........
)
2.因为考虑到上线后才执行迁移数据的脚本,所以要考虑先清空原来该分表数据。因为后面会走mysql,所以就不去读上线之后mssql的表中数据,防止出现重复冗余数据。

3.由于数据量太大,且mssql的日期是字符串没有索引,所以还不能直接用日期来做查询把某天的数据直接弄出来。曲线救国,先根据开始时间和结束时间获取当天数据中最大的主键id和最小的主键id,然后再根据这两个id作为条件查询出整天的数据。

大家看到这儿是不是就舒了一口气,然并卵。因为数据量较大,比如游戏点击日志,一天的记录就上亿,一次性取出和一次性插入也是难事。所以还得分批取出来,分批插入到mysql.

分批查询关键代码如下:

$count = $this->mssql->getOne($countSql);
$countNum = $count[‘count‘];
//var_dump($count);die();
// 每10000条查询一次
$times = ceil($countNum / 10000);

for ($i = 1; $i <= $times; $i++) {
$insertData = array();
// 分批查询和插入
$partData = $this->mssql->select($oneDaySql, array(), $i, 10000);// some other codes .......
$allData = array_chunk($insertData, 1000); // 分批插入
$num = count($allData);
for ($n = 0; $n< $num; $n++) {
$mysql->insert_batch($tableName , $allData[$n]);
}
// other codes
}

每一天的跑完之后会输出提示信息。

其实这里可以做一些日志记录,比如利用error_log,类似:

error_log(date(‘H:i:s‘) . " 同步统计数据到mysql游戏点击表gameClick_{$date}_log \t",3, "./application/logs/SyncGameClick.log");

以此作为一些凭证。
时间: 2024-12-18 02:36:21

写脚本的一些心得-----------------------历史数据迁移到分表的相关文章

写一些脚本的心得总结 ---- 创建分表

前段时间一直在写一些跑数据的脚本,今天刚好有时间总结一下. 一般来说,分成2种,一种是写在单个文件中的任务,数据库驱动之类的直接include进来.运行的时候:cd /path/to/project/usr/local/php5.x/php task.php另外一种是套在框架内部的某个contorller文件里面,调用方式可以参考CI的方式.比如:cd /path/to/project/usr/local/php5.x/php index.php tempscript methodName pa

记一次从oracle到hive的历史数据迁移(一)

介绍一下具体场景 需要从现有在用的oracle中将历史数据迁移出来并导入到hive中,存在几个问题:1.现有oracle数据库在使用无法整个库导出,数据库服务器没有权限:2.由于各个数据提供方的各次升级,oracle数据库采取添加字段而不删除字段的方式更新,而hive中建立的表数据结构要按新的来,也就是说oracle导出的数据到hive表中需要做字段对应. 决定先将oracle导入到另一个oracle中,将oracle中表字段和hive中的表字段进行比对得到各个的对应关系,之后写对应的sqoop

写脚本时整数比较与字符串的比较

写脚本时整数比较与字符串的比较: 整数比较-eq 等于,如:if [ "$a" -eq "$b" ]-ne 不等于,如:if [ "$a" -ne "$b" ]-gt 大于,如:if [ "$a" -gt "$b" ]-ge 大于等于,如:if [ "$a" -ge "$b" ]-lt 小于,如:if [ "$a" -lt &q

用分区表分区交换做历史数据迁移

一.说明: OLTP库中有些表数据量大,且每月有持续的大量数据增加,由于历史数据在此库中不再做访问,而是在另1个OLAP库中做分析,所以会对历史数据迁移至OLAP库中.对这种历史数据迁移的操作,较好的办法是该表采用分区表.按时间分区后,可以对分区进行迁移.通过分区交换和表空间传输会很容易完成,而且性能上影响很小. 关于分区表更多内容:    http://blog.csdn.net/tanqingru/article/category/1397435 关于表空间传更多内容: http://blo

运维常写脚本总结

常写脚本分类 - 监控脚本 - 备份脚本 - 部署脚本 - 业务脚本 监控脚本(如): 监控TCP各连接状态脚本,监控端口脚本-- 备份脚本(如) 备份数据库脚本,备份目录脚本-- 部署脚本(如): 游戏环境安装脚本:开服.合服脚本:系统初始化脚本-- 业务脚本(如): 游戏日志合并脚本.查询数据库脚本.更新脚本--

《神秘的程序员们》57:为什么程序员们喜欢写脚本?

投递人 itwriter 发布于 2016-09-25 13:11 评论(6) 有674人阅读 原文链接 [收藏] « » 想更深入了解这个话题的同学,请移步霍老爷的文章<程序员爱写脚本是种病>:https://news.cnblogs.com/n/553596/ 抢房子/工作/老婆那一段台词来自王渊命同学的吐槽,已获得改编授权,感谢隔壁老王. 8 0 推荐成功 来自: mp.weixin.qq.com

LoadRunner JAVA Vuser协议手工写脚本Failed to find Classes.zip entry in Classpath问题 .

JAVA Vuser协议手工写脚本Failed to find Classes.zip entry in Classpath问题解决方法: 前提条件: 环境变量:正确的配置了JAVA_HOME,path正确的指明了JAVA_HOME的bin路径和LR的bin路径,classpath中指明了jdk的lib路径和LR的lib路径. 当前LoadRunner的版本是9.0版. 当前的JDK版本是1.7. 一.错误内容如下: 通知: Found jdk version: 1.7.0. [MsgId: M

Oracle用分区表分区交换做历史数据迁移

一. 说明: OLTP库中有些表数据量大,且每月有持续的大量数据添加.因为历史数据在此库中不再做訪问,而是在另1个OLAP库中做分析.所以会对历史数据迁移至OLAP库中.对这样的历史数据迁移的操作.较好的办法是该表採用分区表.按时间分区后,能够对分区进行迁移.通过分区交换和表空间传输会非常easy完毕.并且性能上影响非常小. 关于分区表很多其它内容:    http://blog.csdn.net/tanqingru/article/category/1397435 关于表空间传很多其它内容:

Linux shell脚本数值计算个人心得

Linux shell脚本数值计算个人心得 本篇博客为,个人对与bash数值计算的一些心得与用法. Bash对于数值是没有明确定义的,在shell里面所有的数值都默认为字符串,并不是单纯的数值. 比如:a=1+2,a并不等于3,而是等于字符串1+2. 整数计算: 整数计算为整数型的数值加减乘除,没有浮点数的数值,就算浮点数的数值进行加减也会忽略小数点后面的数值. 表达式计算方法: 表达式1:$[EXPRESSION] 表达式2:$((EXPRESSION)) [ ] 和 (( )) 也被称为运算