各位用户为了找寻关于MySQL 百万级数据的4种查询优化方式的资料费劲了很多周折。这里教程网为您整理了关于MySQL 百万级数据的4种查询优化方式的相关资料,仅供查阅,以下为您介绍关于MySQL 百万级数据的4种查询优化方式的详细内容
一.limit越往后越慢的原因
当我们使用limit来对数据进行分页操作的时,会发现:查看前几页的时候,发现速度非常快,比如 limit 200,25,瞬间就出来了。但是越往后,速度就越慢,特别是百万条之后,卡到不行,那这个是什么原理呢。先看一下我们翻页翻到后面时,查询的sql是怎样的:
? 1select
*
from
t_name
where
c_name1=
'xxx'
order
by
c_name2 limit 2000000,25;
这种查询的慢,其实是因为limit后面的偏移量太大导致的。比如像上面的 limit 2000000,25 ,这个等同于数据库要扫描出 2000025条数据,然后再丢弃前面的 20000000条数据,返回剩下25条数据给用户,这种取法明显不合理。
二.百万数据模拟
1、创建员工表和部门表,编写存储过程插数据
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81/*部门表,存在则进行删除 */
drop
table
if EXISTS dep;
create
table
dep(
id
int
unsigned
primary
key
auto_increment,
depno mediumint unsigned
not
null
default
0,
depname
varchar
(20)
not
null
default
""
,
memo
varchar
(200)
not
null
default
""
);
/*员工表,存在则进行删除*/
drop
table
if EXISTS emp;
create
table
emp(
id
int
unsigned
primary
key
auto_increment,
empno mediumint unsigned
not
null
default
0,
empname
varchar
(20)
not
null
default
""
,
job
varchar
(9)
not
null
default
""
,
mgr mediumint unsigned
not
null
default
0,
hiredate datetime
not
null
,
sal
decimal
(7,2)
not
null
,
comn
decimal
(7,2)
not
null
,
depno mediumint unsigned
not
null
default
0
);
/* 产生随机字符串的函数*/
DELIMITER $
drop
FUNCTION
if EXISTS rand_string;
CREATE
FUNCTION
rand_string(n
INT
)
RETURNS
VARCHAR
(255)
BEGIN
DECLARE
chars_str
VARCHAR
(100)
DEFAULT
'abcdefghijklmlopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
;
DECLARE
return_str
VARCHAR
(255)
DEFAULT
''
;
DECLARE
i
INT
DEFAULT
0;
WHILE i < n DO
SET
return_str = CONCAT(return_str,
SUBSTRING
(chars_str,FLOOR(1+RAND()*52),1));
SET
i = i+1;
END
WHILE;
RETURN
return_str;
END
$
DELIMITER;
/*产生随机部门编号的函数*/
DELIMITER $
drop
FUNCTION
if EXISTS rand_num;
CREATE
FUNCTION
rand_num()
RETURNS
INT
(5)
BEGIN
DECLARE
i
INT
DEFAULT
0;
SET
i = FLOOR(100+RAND()*10);
RETURN
i;
END
$
DELIMITER;
/*建立存储过程:往emp表中插入数据*/
DELIMITER $
drop
PROCEDURE
if EXISTS insert_emp;
CREATE
PROCEDURE
insert_emp(
IN
START
INT
(10),
IN
max_num
INT
(10))
BEGIN
DECLARE
i
INT
DEFAULT
0;
/*
set
autocommit =0 把autocommit设置成0,把默认提交关闭*/
SET
autocommit = 0;
REPEAT
SET
i = i + 1;
INSERT
INTO
emp(empno,empname,job,mgr,hiredate,sal,comn,depno)
VALUES
((START+i),rand_string(6),
'SALEMAN'
,0001,now(),2000,400,rand_num());
UNTIL i = max_num
END
REPEAT;
COMMIT
;
END
$
DELIMITER;
/*建立存储过程:往dep表中插入数据*/
DELIMITER $
drop
PROCEDURE
if EXISTS insert_dept;
CREATE
PROCEDURE
insert_dept(
IN
START
INT
(10),
IN
max_num
INT
(10))
BEGIN
DECLARE
i
INT
DEFAULT
0;
SET
autocommit = 0;
REPEAT
SET
i = i+1;
INSERT
INTO
dep( depno,depname,memo)
VALUES
((START+i),rand_string(10),rand_string(8));
UNTIL i = max_num
END
REPEAT;
COMMIT
;
END
$
DELIMITER;
2.执行存储过程
? 1 2 3 4/*插入120条数据*/
call insert_dept(1,120);
/*插入500W条数据*/
call insert_emp(0,5000000);
插入500万条数据可能很慢
三.4种查询方式
1.普通limit分页
? 1 2 3 4 5 6/*偏移量为100,取25*/
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
order
by
a.id
desc
limit 100,25;
/*偏移量为4800000,取25*/
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
order
by
a.id
desc
limit 4800000,25;
执行结果
? 1 2 3 4 5 6 7 8 9 10[SQL]
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
order
by
a.id
desc
limit 100,25;
受影响的行: 0
时间: 0.001s
[SQL]
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
order
by
a.id
desc
limit 4800000,25;
受影响的行: 0
时间: 12.275s
越往后,查询效率越慢
2.使用索引覆盖+子查询优化
因为我们有主键id,并且在上面建了索引,所以可以先在索引树中找到开始位置的 id值,再根据找到的id值查询行数据。
? 1 2 3 4 5 6 7 8 9 10 11/*子查询获取偏移100条的位置的id,在这个位置上往后取25*/
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id >= (
select
id
from
emp
order
by
id limit 100,1)
order
by
a.id limit 25;
/*子查询获取偏移4800000条的位置的id,在这个位置上往后取25*/
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id >= (
select
id
from
emp
order
by
id limit 4800000,1)
order
by
a.id limit 25;
执行结果
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15[SQL]
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id >= (
select
id
from
emp
order
by
id limit 100,1)
order
by
a.id limit 25;
受影响的行: 0
时间: 0.106s
[SQL]
SELECT
a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id >= (
select
id
from
emp
order
by
id limit 4800000,1)
order
by
a.id limit 25;
受影响的行: 0
时间: 1.541s
3.起始位置重定义
适用于主键是自增主键的表
? 1 2 3 4 5 6 7 8 9 10/*记住了上次的分页的最后一条数据的id是100,这边就直接跳过100,从101开始扫描表*/
SELECT
a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id > 100
order
by
a.id limit 25;
/*记住了上次的分页的最后一条数据的id是4800000,这边就直接跳过4800000,从4800001开始扫描表*/
SELECT
a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id > 4800000
order
by
a.id limit 25;
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[SQL]
SELECT
a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id > 100
order
by
a.id limit 25;
受影响的行: 0
时间: 0.001s
[SQL]
SELECT
a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
from
emp a
left
join
dep b
on
a.depno = b.depno
where
a.id > 4800000
order
by
a.id limit 25;
受影响的行: 0
时间: 0.000s
这个效率是最好的,无论怎么分页,耗时基本都是一致的,因为他执行完条件之后,都只扫描了25条数据。
4,降级策略(百度的做法)
这个策略是最简单有效的,因为一般的大数据查询都会有搜索条件,没人会关注100页以后的内容,当用户查询页数过大时,给它返回一个错误就行了,例如百度就只能搜索到76页
以上就是MySQL 百万级数据的4种查询优化方式的详细内容,更多关于MySQL 百万级数据查询优化的资料请关注其它相关文章!
原文链接:https://www.cnblogs.com/xiaodou00/p/14813759.html