Mysql批量操作整理

3 篇文章 0 订阅

背景

面对批量频繁的数据库操作,本文尝试从经验整理mysql批量查询,插入等操作的方法

批量查询

情景1:查询同个字段多个值

采用方法:
采用In方式。返回的结果顺序对应In中值的顺序。这种同一字段不同值,采用In方式可以避免多次的任务提交。当然,也可以通过任务的START TRANSACTION和COMMIT方式将多个select语句一次性进行提交。这种事务命令可以用以一序列的数据库操作。
注意该方式对于值不在In中的查询是不会有返回的结果,那么就造成一定程度上不清楚In中的值对应的是select返回结果中的哪一条。此时,可以通过该记录的带有主键字段,对结果进行标识的。
代码:

        int reqsize=500;
        Json::Value rspTemp;
        boost::unordered_map<uint32_t, uint32_t> idMap;//记录krcid和对应的位置
        string krcids="(";
        for(int i=0; i<reqsize; i++)
        {
            //批量请求,in操作
            uint32_t temp = JsonReq[i].asInt();//即,In中的所有候选值
            idMap[temp] = i;
            rspTemp[i]["krcid"]=temp;
            rspTemp[i]["fmt"] ="";
            krcids +=Uint32ToString(temp);
            if(i != reqsize-1)
            {
                 krcids.append(",");
            }

        }
        krcids.append(")");
        string sql = "select ID, UC from table1 where ID IN ";
        sql = sql + krcids;
        DataTable dbTable;
        db.ExecSQL(sql, dbTable);
        int rowCount = dbTable.GetRowCount();

        for (int i = 0; i < rowCount; ++i)
        {
            int krcid =  dbTable[i][0].AsInt();//对krcid对应的fmt进行赋值
            int fmt =  dbTable[i][1].AsInt();//返回结果用json格式
            uint32_t rowId =idMap[krcid];
            rspTemp[rowId]["krcid"]=krcid;
            rspTemp[rowId]["fmt"]=fmt;
        }

情景2:涉及到联合查询

批量插入

采用方法1:Value方式合并数据

最简单的就是批量的语句,一次次执行insert操作,最慢这种。使用START TRANSACTION和COMMIT的事务型方式进行批量插入,效率会比一条条插入要高得多。这是因为进行一个INSERT操作时,MySQL**内部会建立一个事务**,在事务内才进行真正插入处理操作。通过使用事务可以减少创建事务的消耗,所有插入都在执行后才进行提交操作。采用value的方法可以合并后日志量(MySQL的binlog和innodb的事务让日志)减少了,降低日志刷盘的数据量和频率,从而提高效率。通过合并SQL语句,同时也能减少SQL语句解析的次数,减少网络传输的IO。同时,也
代码:

    int linecount=0;
    time_t start,stop;
    start = time(NULL);
    string finalstr;
    vector<uint32_t> vecorigina;
    vector<uint32_t> vectrans;
    vector<uint32_t> vecsound;
    vector<uint32_t> veckrci;
    int tasknum =5000;
    for(int i=0; i<rowCount; i++)
    {
        linecount++;
        uint32_t krcid = dbTable[i][0].AsInt();
        uint32_t origiuid = VecUids[i];//构造出来的数据
        uint32_t transuid = VecUids[i+rowCount];
        uint32_t sounduid = VecUids[i+2*rowCount];

        string sql_insert = "insert into tbl1 (id1,id2,id3,id4,modifytime) VALUES";
        string condition = "(?,?,?,?,now())";
        if(linecount%tasknum != 0 && linecount<rowCount)
        {
            finalstr = finalstr +condition+",";
            veckrci.push_back(krcid);
            vecorigina.push_back(origiuid);
            vectrans.push_back(transuid);
            vecsound.push_back(sounduid);
        }
        else
        {
            finalstr = finalstr +condition;
            finalstr = sql_insert+ finalstr;
            veckrci.push_back(krcid);
            vecorigina.push_back(origiuid);
            vectrans.push_back(transuid);
            vecsound.push_back(sounduid);
            dblyric_Debug.PrepareStatment(finalstr);
            int z=1;
            if(linecount==rowCount)//尾数处理
            {
                for(int j=0;j<veckrci.size();j++)
                {
                    dblyric_Debug.SetInt(z++, veckrci[j]);
                    dblyric_Debug.SetInt(z++, vecorigina[j]);
                    dblyric_Debug.SetInt(z++, vectrans[j]);
                    dblyric_Debug.SetInt(z++, vecsound[j]);
                }
                if (!dblyric_Debug.ExecuteUpdate())
                {

                    std::cout<<"update 失败! "<<linecount<<std::endl;
                }
                break;
            }
            if(veckrci.size()!=tasknum || vecorigina.size() !=tasknum || vectrans.size() !=tasknum||vecsound.size()!=tasknum)
            {
                std::cout<<"信息缺失!"<<linecount<<std::endl;
            }
            int j=0;
            for(;j<veckrci.size();j++)
            {
                dblyric_Debug.SetInt(z++, veckrci[j]);
                dblyric_Debug.SetInt(z++, vecorigina[j]);
                dblyric_Debug.SetInt(z++, vectrans[j]);
                dblyric_Debug.SetInt(z++, vecsound[j]);
            }
            if (!dblyric_Debug.ExecuteUpdate())
            {
                std::cout<<"update1 失败! "<<linecount<<std::endl;
            }

            finalstr = "";
            veckrci.clear();
            vecorigina.clear();
            vectrans.clear();
            vecsound.clear();
            stop =time(NULL);           
            std::cout<<"5K耗时:"<<double(stop-start)<<std::endl;
            start = stop;
        }

    }   

采用方法2:数据有序插入

数据有序插入可以提高插入效率。数据有序的插入是指插入记录在主键上是有序排列,例如datetime是记录的主键:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('1', 'userid_1', 'content_1', 1);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('0', 'userid_0', 'content_0', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('2', 'userid_2', 'content_2',2);

修改成下面语句,效率更高:

INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('0', 'userid_0', 'content_0', 0);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('1', 'userid_1', 'content_1', 1);
INSERT INTO `insert_table` (`datetime`, `uid`, `content`, `type`) 
    VALUES ('2', 'userid_2', 'content_2',2);

这是由于数据库插入时,需要维护索引数据,无序的记录会增大维护索引的成本。可以参照innodb使用的B+tree索引,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引调整较小;如果插入的记录在索引中间,需要B+tree进行分裂合并等处理,会消耗比较多计算资源,并且插入记录的索引定位效率会下降,数据量较大时会有频繁的磁盘操作。

合并数据+事务的方法在较小数据量时,性能提高是很明显的,数据量较大时(1千万以上),性能会急剧下降,这是由于此时数据量超过了innodb_buffer的容量,每次定位索引涉及较多的磁盘读写操作,性能下降较快。而使用合并数据+事务+有序数据的方式在数据量达到千万级以上表现依旧是良好,在数据量较大时,有序数据索引定位较为方便,不需要频繁对磁盘进行读写操作,所以可以维持较高的性能。
注意事项:
1. SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_packet配置可以修改,默认是1M,测试时修改为8M。
2. 事务需要控制大小,事务太大可能会影响执行的效率。MySQL有innodb_log_buffer_size配置项,超过这个值会把innodb的数据刷到磁盘中,这时,效率会有所下降。所以比较好的做法是,在数据达到这个这个值前进行事务提交。

采用方法3:通过文件(LOAD DATA LOCAL INFILE)

该方法测试过程中发现,数据量越大优势体现越是明显。按照网络上的说法是,20倍的提升:
According to the MySQL documentation, LOAD DATA INFILE is “roughly 20 times faster” than a MySQL INSERT statement。
SQL语句如下:

LOAD DATA LOCAL INFILE 'D:\\Users\\jason\\Desktop\\1009.txt'
INTO TABLE tbl1(Hash,Song,Id);

注意,可能需要使用到的SELECT … INTO OUTFILE,进行数据的构造。

http://dev.mysql.com/doc/refman/5.7/en/select-into.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值