为了账号安全,请及时绑定邮箱和手机立即绑定

合并不同服务器的数据表数据(表结构一样)

标签:
PHP MySQL ThinkPHP

本认第一次写博客,可能有些地方表达不清晰,望大家见谅;

最近业务有个需求,需要将不同渠道的会员信息合并在一起,然后插入一张表中(每个渠道的会员数据在不同服务器,这点是操蛋的)


自己先百度了一哈,网上好像没有此类的文章,所以自己决定把这次的经验分享出来;

先说哈自己的整体思路:

  1. 先把其它服务器的数据库表复制映射到本地

  2. 合并成一张表

  3. 循环合并的数据插入到本地数据库表中并处理自己业务的逻辑


下面图文讲解

  •  首先需要把mysql的FEDERATED引擎开启,默认是关闭的,可以通过show engines 命令查看;


https://img1.sycdn.imooc.com//5bc0abd9000105c812130639.jpg

在mysql配置文件加上红色标注的

https://img1.sycdn.imooc.com//5bc0abf800014e5010230265.jpg

准备工作已ok,下面贴上代码


  • 1.配置信息

https://img1.sycdn.imooc.com//5bc0ae2f000154b207480713.jpg

  •  2.把其它服务器数据表映射到本地

 public function syncMemberTable()
    {        // thinking :不同服务器的数据表 通过fea引擎 把其它服务器的表 映射到本地数据库 在把所有的平台的member数据 合并成 在循环写入更新数据

        $merge_sql = 'create table ' . $this->member_table_merge . ' as   ';        //链接池
        foreach ($this->dsns as $key => $dsn) {
            $channel_table = $this->member_table . '_' . $key;            // 创建映射关系
            $show_create_sql = ' show create table ' . $this->member_table;

            $create_table_sql_result = Db::connect($dsn)->query($show_create_sql);
            $create_table_sql = null;
            $create_table_sql = $create_table_sql_result[0]['Create Table'];            // 把 table 名和engine 修改。并增加 connect  语句
            $fed_channel_table = $channel_table . '_FED';            //本地是否已经存在

            $fed_channel_table_has_sql = ' show tables like ' . '\'' . $fed_channel_table . '\'';//show tables like 后面表名比需要引号

            $fed_channel_table_has_result = Db::execute($fed_channel_table_has_sql);//有 1 没有 0
            if ($fed_channel_table_has_result == 0) {
                Log::record("原来的sql:" . $create_table_sql, 'INFO');
                $new_create_fed_sql = '';
                $new_create_fed_sql = str_replace($this->member_table, $fed_channel_table, $create_table_sql);
                $new_create_fed_sql = str_replace("InnoDB", 'FEDERATED  ', $new_create_fed_sql);
                $user = $dsn['username'];
                $password = $dsn['password'];
                $host = $dsn['hostname'];
                $database = $dsn['database'];
                $connection = " CONNECTION='mysql://$user:$password@$host:3306/$database/$this->member_table'";
                $new_create_fed_sql = $new_create_fed_sql . $connection;
                Log::record("替换FED的sql:" . $new_create_fed_sql, 'INFO');

                $new_create_result = Db::execute($new_create_fed_sql);//执行映射sql
                if ($new_create_result !== false) {

                    $msg = "$key 渠道的$this->member_table" . '表 映射到本地数据库成功';                    echo $msg;
                    Log::record($msg, 'INFO');

                } else {
                    $msg = "$key 渠道的$this->member_table" . '表 映射到本地数据库失败';                    echo $msg;
                    Log::record($msg, 'INFO');                    exit();//出错不执行
                }


            }            //映射成功后 在本地在新建一张表 因为映射的表不能操作表
            //先查看是否存在

            $new_create_table_has_sql = ' show tables like ' . '\'' . $channel_table . '\'';
            $result = Db::execute($new_create_table_has_sql);//存在1 不存在0

            if ($result == 0) {                /*--------------------------------------------------*/
                Log::record('---------------starting create table ------------------', 'debug');
                $new_create_sql = '';                //$new_create_sql = str_replace($this->member_table, $channel_table, $create_table_sql);
                $new_create_sql = ' create table ' . $channel_table . ' as select * from ' . $fed_channel_table;

                Log::record("create的sql:" . $new_create_sql, 'INFO');

                $result = Db::execute($new_create_sql);//执行建表

                if ($result != false) {
                    $msg = "$key 渠道的$this->member_table" . '表 copy到本地数据库成功';                    echo $msg;
                    Log::record($msg, 'INFO');

                } else {
                    $msg = "$key 渠道的$this->member_table" . '表 copy到本地数据库失败,Error:' . $result;                    echo $msg;
                    Log::record($msg, 'INFO');                    exit();
                }

            }            //判断字段是否存在
            $fields = Db::getTableInfo($channel_table, 'fields');            if (!in_array('channel_id', $fields)) {                //复制成功后新增渠道字段 后面做逻辑处理 渠道字段直接放在id后面
                $channel_id = $dsn['channel_id'];
                $channel_sql = " ALTER TABLE  `$channel_table` ADD COLUMN `channel_id`  int(10) UNSIGNED NOT NULL DEFAULT  $channel_id AFTER `id`";
                $channel_result = Db::execute($channel_sql);                if ($channel_result != false) {
                    $msg = "$key 渠道的$this->member_table" . '表 的渠道ID字段添加成功';                    echo $msg;
                    Log::record($msg, 'INFO');                    //添加成功后可以删除多余的映射表
                    Db::execute('DROP TABLE ' . $fed_channel_table);


                } else {
                    $msg = "$key 渠道的$this->member_table" . '表 的渠道ID字段添加失败';                    echo $msg;
                    Log::record($msg, 'INFO');                    exit();
                }

            }


            $keys = array_keys($this->dsns);
            $merge_sql .= ' select  distinct *  from ' . $channel_table . ($key != end($keys) ? ' union all  ' : '');


        }


        Log::record($merge_sql, 'INFO');        //不渠道的表合并成
        $result = Db::execute($merge_sql);        if ($result == false) {            echo $msg = $this->member_table . '合并表创建失败!';
            Log::record($msg, 'INFO');
        } else {            echo $msg = $this->member_table . '合并表创建成功!';
            Log::record($msg, 'INFO');
        }


    }




上面大概的思路是通过show create table 把需要合并的表的创建mysql 获取,然后把引擎改成FEDERATED,然后在本地再次创建一份视图或者表,因为我这里需要新增一个channel_id字段到其它服务器映射到本地的表中,但是FEDERATED引擎不能操作表,所以需要在多一步步骤;

表创建过后,然后通过mysql 的 union 或者 union all 合并成一张表,到此其它服务器的表在本地数据库已合并在一起,有数据了接下来就好办了; 

效果:

某个服务器的表

https://img1.sycdn.imooc.com//5bc0ad7000017ae508000479.jpg

合并过后的表

https://img1.sycdn.imooc.com//5bc0aee70001b35308000488.jpg

  •  3.同步数据

//同步会员表数据
    public function SynMemberTableData()
    {
        //把会员插入的信息另写一个日志
        Log::init([
            'type' => 'File',
            'path' => LOG_PATH . 'MemberLog' . DS,
        ]);
        $ts = microtime(true);
        Log::record("-----------------start写入会员信息---------执行时间-----------" . date("H:i:s"), 'INFO');
        Debug::remark('begin');


        //统计一共有多少用户

        $total = Db::table($this->member_table_merge)->group('mobile,channel_id')->count();

        Log::record('一共有' . $total . '个用户', 'INFO');

        if (!empty($total)) {
            //进程处理数据条数
            $limit = 10000;
            $pages = ceil($total / $limit);

            for ($i = 1; $i <= $pages; $i++) {
                $members = Db::table($this->member_table_merge)->group('mobile,channel_id')->order('channel_id')->page($i, $limit)->select();
                //多进程
                foreach ($members as $k => $member) {
                    //同个渠道的人是否已经有了
                    $has = $this->member_model->where(['mobile' => $member['mobile'], 'channel_id' => $member['channel_id']])->count();

                    if (empty($has)) {
                        unset($member['id']);
                        $insert_id = $this->member_model->insertGetId($member);
                        if ($insert_id) {
                            $msg = '用户:' . $member['username'] . '[channel_id=' . $member['channel_id'] . ']数据插入成功,插入ID为:' . $insert_id;
                            echo $msg;
                            Log::record($msg, 'INFO');
                        } else {
                            $msg = '用户:' . $member['username'] . '[channel_id=' . $member['channel_id'] . ']数据插入失败';
                            echo $msg;
                            Log::record($msg, 'Error');
                            continue;
                        }
                    } else {
                        continue;
                    }

                    unset($member[$k]);//unset()
                }


//
//                $reserveProcess = new \Swoole\Process(function ($worker) use ($i, $limit, $member_model, $member_dis_model) {
//
//
//
//                }, false);
                Debug::remark('end');
                Log::record("-----------------会员信息写入end-------------结束时间-------" . date("H:i:s"), 'INFO');
                Log::record('写入耗时' . (microtime(true) - $ts) . 's ', 'debug');
                Log::record('所耗内存' . Debug::getRangeMem('begin', 'end') . 'kb', 'debug');
                Log::save();
                echo "end SynMemberTableData task..." . (microtime(true) - $ts) . "s " . "\r\n";
            }


        }

        $this->SynMemberTableData();//


    }



插入到本地数据库的表

https://img1.sycdn.imooc.com//5bc0af000001176108000461.jpg

到此大致上已完成了自己的业务需求了;

希望本文能够对你有些帮助



点击查看更多内容
3人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
PHP开发工程师
手记
粉丝
0
获赞与收藏
3

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消