mysql的in查询参数限制,多少数据量会造成性能下降?什么时候创建临时表合适?

文章目录

一、前言

如题所示,博主对这个问题一直模模糊糊的,网上也没一个具体的测试,都是零零散散的提一下,缺少系统的测试。前两天看到csdn的论坛,看到两个大版主,有的说超过300性能会下降,有的说超过999性能会下降???行吧,咱们不如自己测测。

arduino 复制代码
论坛链接:https://bbs.csdn.net/topics/392474752

二、本地配置以及生成测试数据

测试前首先是明确博主这边的各种环境,按照环境来测是最准的。

1、本地mysql配置

(1)mysql版本
sql 复制代码
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.5.53    |
+-----------+
1 row in set (0.00 sec)
(2)本地sql长度限制
sql 复制代码
mysql> show variables like '%max_allowed_packet%';
+--------------------------+------------+
| Variable_name            | Value      |
+--------------------------+------------+
| max_allowed_packet       | 52428800   |
| slave_max_allowed_packet | 1073741824 |
+--------------------------+------------+
2 rows in set (0.00 sec)

in查询的数量多少,和sql长度有关的,我们本地sql长度长一些,这样in查询数量多的时候也不至于报错。博主本地sql限制长度是50M,一次插5W条数据没问题,相应的in查询5W也不是问题哈哈。

mysql批量插入数据,一次插入多少行数据效率最高?

2、创建表和测试数据(50W)

(1)创建表语句
sql 复制代码
CREATE TABLE `student` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id主键',
  `sno` int(10) NOT NULL DEFAULT '0' COMMENT '学号',
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(10) NOT NULL DEFAULT '1' COMMENT '年龄',
  `test_id` int(10) NOT NULL DEFAULT '0' COMMENT '和id一样的值,方便测试',
  PRIMARY KEY (`id`),
  UNIQUE KEY `sno` (`sno`),
  KEY `test_id` (`test_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生表(test)'

这里的test_id表的数据和id的值是一样的,方便咱们测试。索引什么的都建好,50W数据也不少了,可以用到索引的。

(2)循环插入50W条数据
sql 复制代码
<?php
$link = mysqli_connect('localhost','root','root');
if(!$link){
  exit('mysql数据链接失败');
}
mysqli_select_db($link,'test');
mysqli_set_charset($link,'utf-8');
// 循环插50W条,每次插入5W条数据
for($i=0;$i<10;$i++){
  $sqls = "";
  //接下来循环拼接sql到下面
  $j = 50000*$i+1;
  $k = $j+50000;
  for($j;$j<$k;$j++){
      $sno = intval('2019'.sprintf("%06d", $j)); //学号是前面补0
      $name = 'name_'.$j;
      $age = 20;
      $test_id = $j;
      $sqls .= "({$sno},'{$name}',{$age},{$test_id})".",";
  }
  $sqls = substr($sqls,0,-1);
  $sql = "INSERT INTO `student` (`sno`,`name`,`age`,`test_id`) VALUES {$sqls}";
  $rs = mysqli_query($link,$sql);
  if(! $rs ) {
    die('无法插入数据: ' . mysqli_error($link));
  }else{
    echo "数据插入成功\n:返回值是:".$rs;
  }
  unset($sqls);
  unset($sql);
  unset($rs);
}

mysqli_close($link);

3、测试in查询是否用到了索引

sql 复制代码
mysql> explain select * from student where 1 and test_id in (100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142);
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | student | range | test_id       | test_id | 4       | NULL |   43 | Using where |
+----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

这里看是能用到索引的,如果大家对这块有疑问可以参考我的其他博客:mysql的in查询是可以用到索引吗?亲测详解

三、开始测试

这里的测试主要分为三部分,主要是有顺序的in查询字符串,和无顺序的查询字符串。以及in查询与创建临时表的性能比较。

代码大致如下:

sql 复制代码
function test_in()
{
  $start = rand(1, 5000) * 100;
  $nums = 100;
//开始测试
  $link = mysqlTest();
//获取要查询的随机数
  $strs_arr = randomStr($nums, $start, true);
  $str = "(" . implode(',', $strs_arr) . ")";
//调用获取毫秒时间的函数,t1
  $sql = "select * from student where 1 and test_id in {$str}";
//var_dump($sql);exit;
  $t1 = msectime();
  $rs = mysqli_query($link, $sql);
  $t2 = msectime();
  $diff = $t2 - $t1;
  if (!$rs) {
    die('查询失败: ' . mysqli_error($link));
  } else {
    $arr = mysqli_fetch_all($rs);
    echo "数据查询成功:查询数据量是:" . count($arr) . "\n";
  }
  echo "查询开始值是:" . $start . "查询数量是" . $nums . "耗费时间是:" . $diff . "毫秒";
  mysqli_close($link);
}

1、查询顺序字符串

这里的结果都是博主查询10次,求的平均值大小,大概是如下:

sql 复制代码
查询开始值是:32800查询数量是100耗费时间是:7毫秒
查询开始值是:44300查询数量是100耗费时间是:5毫秒
查询开始值是:33300查询数量是100耗费时间是:6毫秒
查询开始值是:49300查询数量是100耗费时间是:4毫秒

下面是正式结果:

php 复制代码
100条平均"5ms"
200条平均:10.2ms
299条平均:16.4ms
400条平均:19.4ms
600条平均:21.67ms
800条平均:30.8ms
999条平均:32.8ms
1200条平均:39.2ms
3000条平均:67ms
5000条平均:101.5ms
10000条平均:189ms
20000条平均:336ms

其实这里可以看到,in查询中的数量增加,性能下降并没有想象中那么可怕。这也是mysql5.5之后优化了in查询的结果。

2、打乱查询字符串比较

php 复制代码
100条平均:7.5ms
300条平均:13ms
600条平均:23.8ms
999条平均:31ms
1200条平均:39ms
3000条平均:76ms
10000条平均:199ms

这里对比上面的顺序查询,可以看出来性能是有细微差异的。也是,我们顺序查询的时候,经过索引会进行顺序IO,消耗的性能会少一些。不过我们平时进行In查询的数据基本都是散乱的,所以这个还是比较有参考意义的。

3、创建临时表测试

这块主要是要创建表,写入数据,通过inner join进行连接查询等。下面列出大致步骤和代码。

(1)创建临时表
php 复制代码
function createTem($link,$table_name)
{
  $sql= "create temporary table {$table_name} (
  id int(10) NOT NULL AUTO_INCREMENT,
  test_id int(10) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `test_id` (`test_id`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8";
  $rs = mysqli_query($link,$sql);
  if(!$rs){
    die('创建临时表失败: ' . mysqli_error($link));
  }
}
(2)插入数据,进行测试
php 复制代码
function test_create_tem()
{
  $start = rand(1, 5000) * 100;
  $nums = 20000;
//开始测试
  $link = mysqlTest();
  $table_name = "test_tem";
//获取要查询的随机数
  $strs_arr = randomStr($nums, $start, true);
  //$str = "(" . implode(',', $strs_arr) . ")";
  $sqls = "";
  foreach($strs_arr as $v){
      $sqls .= "({$v})".",";
  }
  $sqls = substr($sqls,0,-1);
//调用获取毫秒时间的函数,t1
  $t1 = msectime();
  //创建临时表
  createTem($link,$table_name);
  //插入数据到临时表
  $insert_sql = "insert into `{$table_name}` (test_id) VALUES {$sqls}";
  $rs1 = mysqli_query($link, $insert_sql);
  if($rs1){
    $sql = "select a.* from student as a join {$table_name} as b on a.test_id=b.test_id";
    $rs = mysqli_query($link, $sql);
  }
  $t2 = msectime();
  $diff = $t2 - $t1;
  if (!$rs) {
    die('查询失败: ' . mysqli_error($link));
  } else {
    $arr = mysqli_fetch_all($rs);
    echo "数据查询成功:查询数据量是:" . count($arr) . "\n";
  }
  echo  "数据查询成功:查询数据量是:" . count($arr)."连表耗费时间是:" . $diff . "毫秒";
  mysqli_close($link);
}

打印结果形如如下:

php 复制代码
数据查询成功:查询数据量是:20000连表耗费时间是:138毫秒
xxxxx
(3)临时表的耗时展示
php 复制代码
300条数据:13ms
600条数据:17ms
999条数据:22.6ms
1500条数据:25ms
3000条数据:34.4ms
5000条数据:55ms
10000条数据:89ms
20000条数据:124ms

这部分我们没有测试较少数据的查询,因为小数据的查询,耗时肯定是比in查询长的。

这里能看出来:

1)当数据量大于300的时候,临时表的查询性能一直是优于in查询的。
2)随着数据量的增加,临时表的性能越来越出色

四、总结

这个测试说实话,对博主来说也是推翻了一些东西,在以前博主总是不愿意建临时表的,总觉得麻烦,创建表还得插入数据,这速度能快嘛,,结果打脸了。

1、in查询数据量多少会性能下降?

答: 根据测试来说,in查询的数据量在1W以内都是可以接受的,这里没看到有性能剧变,猜测是mysql5.5之后的版本优化原因。就像in查询使用索引一样,网上很多资料都说用不到索引,但是实际上5.5之后都是可以用到的。不能说前辈们总结的是错的,只能说mysql一直在发展进步。

2、数据量多少创建临时表比较好

答: 通过测试我们可以看到,数据量大于300建临时表都是不错的选择。这块是仁者见仁智者见智了,如果in查询的条件,其他部分也有用到的话,建临时表是最好的,可以多次使用。如果你只是想一锤子买卖,那么博主觉得1000条以内直接用in查询就好了。

3、提升性能的建议

答: 博主这里只是大致测试下,代码写的勉强能用,测试精度也有限。如果想要继续提升性能的话,建议是创建临时表的时候选用内存表 ,性能还能提升30%左右。其他部分能提升的不多了,这里的in查询已经用到了索引。。。

以上就是关于in查询的总结了,博主耗费了一天周末时间测试,不过结果是好的,最起码搞懂了一些东西,下次碰到类似的情况也知道该如何选择了哈哈,一起加油!

需要源码的,评论留邮箱,博主可以把测试demo发过去。

end

相关推荐
桃园码工1 天前
2-测试bigcache做进程内缓存 --开源项目obtain_data测试
vscode·mysql·go·postman
ZQDesigned2 天前
在 Windows 和 macOS 上配置 Golang 语言环境
后端·go
楽码2 天前
只需一文:了解validator标签以轻松验证
后端·安全·go
煎鱼eddycjy3 天前
新提案:由迭代器启发的 Go 错误函数处理
go
煎鱼eddycjy3 天前
Go 语言十五周年!权力交接、回顾与展望
go
不爱说话郭德纲4 天前
聚焦 Go 语言框架,探索创新实践过程
go·编程语言
0x派大星5 天前
【Golang】——Gin 框架中的 API 请求处理与 JSON 数据绑定
开发语言·后端·golang·go·json·gin
IT书架5 天前
golang高频面试真题
面试·go
郝同学的测开笔记5 天前
云原生探索系列(十四):Go 语言panic、defer以及recover函数
后端·云原生·go
秋落风声6 天前
【滑动窗口入门篇】
java·算法·leetcode·go·哈希表