赞
踩
如题所示,博主对这个问题一直模模糊糊的,网上也没一个具体的测试,都是零零散散的提一下,缺少系统的测试。前两天看到csdn
的论坛,看到两个大版主,有的说超过300
性能会下降,有的说超过999
性能会下降???行吧,咱们不如自己测测。
论坛链接:https://bbs.csdn.net/topics/392474752
测试前首先是明确博主这边的各种环境,按照环境来测是最准的。
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.5.53 |
+-----------+
1 row in set (0.00 sec)
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
也不是问题哈哈。
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
数据也不少了,可以用到索引的。
<?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);
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
查询与创建临时表的性能比较。
代码大致如下:
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); }
这里的结果都是博主查询10次,求的平均值大小,大概是如下:
查询开始值是:32800查询数量是100耗费时间是:7毫秒
查询开始值是:44300查询数量是100耗费时间是:5毫秒
查询开始值是:33300查询数量是100耗费时间是:6毫秒
查询开始值是:49300查询数量是100耗费时间是:4毫秒
下面是正式结果:
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
查询的结果。
100条平均:7.5ms
300条平均:13ms
600条平均:23.8ms
999条平均:31ms
1200条平均:39ms
3000条平均:76ms
10000条平均:199ms
这里对比上面的顺序查询,可以看出来性能是有细微差异的。也是,我们顺序查询的时候,经过索引会进行顺序IO
,消耗的性能会少一些。不过我们平时进行In
查询的数据基本都是散乱的,所以这个还是比较有参考意义的。
这块主要是要创建表,写入数据,通过inner join
进行连接查询等。下面列出大致步骤和代码。
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));
}
}
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); }
打印结果形如如下:
数据查询成功:查询数据量是:20000连表耗费时间是:138毫秒
xxxxx
300条数据:13ms
600条数据:17ms
999条数据:22.6ms
1500条数据:25ms
3000条数据:34.4ms
5000条数据:55ms
10000条数据:89ms
20000条数据:124ms
这部分我们没有测试较少数据的查询,因为小数据的查询,耗时肯定是比in
查询长的。
这里能看出来:
1)当数据量大于300的时候,临时表的查询性能一直是优于in查询的。
2)随着数据量的增加,临时表的性能越来越出色
这个测试说实话,对博主来说也是推翻了一些东西,在以前博主总是不愿意建临时表的,总觉得麻烦,创建表还得插入数据,这速度能快嘛,,结果打脸了。
答: 根据测试来说,in
查询的数据量在1W
以内都是可以接受的,这里没看到有性能剧变,猜测是mysql5.5
之后的版本优化原因。就像in
查询使用索引一样,网上很多资料都说用不到索引,但是实际上5.5
之后都是可以用到的。不能说前辈们总结的是错的,只能说mysql
一直在发展进步。
答: 通过测试我们可以看到,数据量大于300
建临时表都是不错的选择。这块是仁者见仁智者见智了,如果in
查询的条件,其他部分也有用到的话,建临时表是最好的,可以多次使用。如果你只是想一锤子买卖,那么博主觉得1000
条以内直接用in
查询就好了。
答: 博主这里只是大致测试下,代码写的勉强能用,测试精度也有限。如果想要继续提升性能的话,建议是创建临时表的时候选用内存表,性能还能提升30%左右。其他部分能提升的不多了,这里的in
查询已经用到了索引。。。
以上就是关于in
查询的总结了,博主耗费了一天周末时间测试,不过结果是好的,最起码搞懂了一些东西,下次碰到类似的情况也知道该如何选择了哈哈,一起加油!
需要源码的,评论留邮箱,博主可以把测试demo发过去。
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。