MySQL千万级数据的 select count优化

一、需求

之前公司某个管理后台的用户管理分页功能非常得慢,最后分析才知道其实是慢在select count语句,当存在某些筛选条件的时候,select count的执行时间在10s以上。用户表的数据量大概一千三百多万,单表。产品的要求是:每次点查询后必须实时知道某筛选条件下总用户量和总页数,不可以模糊。(好吧,你说怎样就怎样咯)

二、优化方法

1、思路

其实count慢,就是因为数据量大,那为什么不把数据分段呢,然后并发查询每一段的数据量,再相加就是总数据量了。这就要使用到Guzzle了。Guzzle是一个PHP的HTTP客户端,可以用来轻而易举地发送请求。详情请看官方文档:guzzle文档

2、代码实现

每次并发查询十段,每段的大小为10万,假设用户表的主键id为id,那么第一段只要查询id范围在0-10万之间有多少用户符合筛选条件。假如查询id范围在0-10万之间的时间是查询id范围在0-100万之间的十分之一,因为这十段是并发请求,所以理论上的查询速度可以提高十倍(理论上,实际达不到)。每次并发查询判断是否查完了,还要不要继续,就OK了。最后把每段的count累加起来就行了。使用这种方法,查询出count的时间不会超过3s,这个速度可以接受,比之前的十几秒好多了。下面直接上代码。

$client      = new Client(['base_uri' => 'http://httpbin.org/']);  //此处替换为你自己的url
            $i           = 1;
            $total       = 0;   //总数据量
            $resultsArr  = [];
            do {
                $promises = [];
                for ($j=0; $j < 10; $j++) {  //每次并发查询十段
                    $promises[$j] = $client->getAsync('查询的接口地址', ['query' => ['where'=>$selCondition2, 't' => $i + $j]]); //selCondition2为查询条件,t标记为第几段
                }
                $results = Promise\unwrap($promises);

                for ($j = 0; $j < 10; $j++) {
                    $resultsArr = json_decode($results[$j]->getBody()->getContents(), true)['content'];
                    $total      = bcadd($total, $resultsArr['count']);
                }
                $i = $i + 10;
            } while (1 != $resultsArr['ifStop']);  //是否要停止查询

查询每段数据量的接口,代码为:

        $dvbUser                     = new DvbUser();
        $pageSize                    = 100000;   //10万一段
        $t                           = $this->request->get('t', 'int', 1);
        $where                       = $this->request->get('where');
        $result['ifStop']            = 0;
        $where['AND']['id[>]']       = ($t - 1) * $pageSize;
        $where['AND']['id[<=]']      = $t * $pageSize;
        if (0 == $t % 10) {  //每次查询的最后一段判断是否还要继续
            $maxId    = $dvbUser->max('id');
            $result['ifStop'] = ($t * $pageSize > $maxId) ? 1 : 0;
        }
        $result['count']                         = $dvbUser->count($where);
        $this->apiResultStandard(1000, '查询成功!', $result);

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值