聊聊数据库~4.SQL优化篇
1.5.查询的艺术
上期回顾:https://www.cnblogs.com/dotnetcrazy/p/10399838.html
本节脚本:https://github.com/lotapp/BaseCode/blob/master/database/SQL/02.索引、查询优化.sql
文章有点小长,但认真阅读肯定会有所感触和收获的。PS:我把我能想到的都列下来了,如果有新的会追加,欢迎补充和纠错~
1.5.1.索引
大方向:减少冗余索引,避免重复(无用)索引
1.概念
大一统分类:
- 聚簇索引、非聚簇索引:看看数据是否与索引存储在一起(一起是聚簇索引)
- 主键索引、辅助索引
- 稠密索引、稀疏索引
- 是否索引了每一个数据项(是则为稠密索引)
B+ Tree
索引、hash
索引(键值索引,只有Memory存储引擎支持
)、R Tree
索引(空间索引,MyISAM存储引擎支持
)、Fulltext
索引(全文索引)- 简单索引、组合索引
PS:索引通常做查询条件的字段(索引是在存储引擎级别实现的)
常用分类:
- 语法分类:
- 普通索引:一列一索引
- 唯一索引:设置unique之后产生(可空)
- 可以这么理解:唯一+非空=主键
- 复合索引:多列一索引
- 物理存储:(Innodb和MyISAM存储引擎)
- 聚簇索引:一般都是主键
- 数据和索引存储在一起的存储方式
- Innodb文件后缀:frm、ibd(数据+索引)
- 非聚簇索引:不是聚集索引的索引
- 数据和索引分开存放
- MyISAM文件后缀:frm、myd(数据)、myi(索引)
- PS:它俩都是b树索引,frm(表结构)和存储引擎无关
- 聚簇索引:一般都是主键
2. 语法基础
- 查看索引:
show index from tb_name;
- show index from worktemp.userinfo\G;
- show index from worktemp.userinfo;
- 创建索引:
create [unique] index index_name on tb_name(列名,...)
alter table tb_name add [unique] index [index_name] on (列名,...)
- 删除索引:
drop index index_name on tb_name
alter table tb_name drop index index_name
1.5.2.执行计划
1.往期回顾
先回顾下上节课内容:
手写SQL的语法顺序:
select distinct
<select_list>
from <tb_name>
<join_type> join <right_table> on <join_condition>
where
<where_condition>
group by
<group_by_list>
having
<having_condition>
order by
<order_by_list>
limit <limit_number>
SQL执行顺序:
from <tb_name>
on <join_condition>
<join_type> join <right_table>
where <where_condition>
group by <group_by_list>
having <having_condition>
select [distinct] <select_list>
order by <order_by_list>
limit <limit_number>
2.基础
语法:explain + SQL语句
执行计划:使用explain
关键词可以模拟优化器执行SQL查询语句,一般用来分析查询语句或者表结构的性能瓶颈
执行计划一般用来干这些事情:
- 查看表的读取顺序
- 查看数据读取操作的操作类型
- 查看哪些索引可以使用
- 查看哪些索引被实际使用
- 查看表之间的引用
- 查看每张表有多少行被优化器读取
主要参数
主要是看这几个参数:
- id:当前查询语句中,每个select语句的编号
- 主要是针对子查询、union查询
select_type
:查询类型- 简单查询:simple(一般的查询语句)
- 复杂查询:(详解见附录1)
subquery
:用于where中的子查询(简单子查询)derived
:用于from中的子查询union
:union语句的第一个之后的select语句union result
:匿名临时表
type
:访问类型(MySQL查询表中行的方式)- all:全表扫描
- index:根据索引的次序进行全表扫描(覆盖索引效率更高)
- range:根据索引做指定范围扫描
- ref:返回表中所有匹配某单个值的所有行
- eq_ref:等同于ref,与某个值做比较且仅返回一行
- const:根据具有唯一性索引查找时,且返回单个行(性能最优)
- eg:主键、唯一键
- PS:1~6 ==> 数字越大效率越高(性能递增),(详解见附录2)
possible_keys
:查询可能会用到的索引key
:查询中使用了的索引key_len
:索引使用的字节数(详解见附录3)- 根据这个值,可以判断索引使用情况
- eg:使用组合索引时,判断所有索引字段是否都被查询到
ref
:显示key列索引用到了哪些列、常量值- 在索引列上查找数据时,用到了哪些列或者常量
rows
:估算大概需要扫描多少行Extra
:额外信息(性能递减)- using index:使用了覆盖索引
using where
:在存储引擎检索后,再进行一次过滤- using temporary:对结果排序时会使用临时表
- using filesort:对结果使用一个外部索引排序
- 没有有索引顺序,使用了自己的排序算法
- 可能出现的情况:(出现这个情况基本上都是需要优化的)
- where后面的索引列和
order by|group by
后面的索引列不一致(只能用到一个索引) - eg:
explain select * from users where id<10 order by email;
(只用到了id)
- where后面的索引列和
附录
1.select_type
select_type
:查询类型
-- `subquery`:用于where中的子查询(简单子查询)
explain
select name, age
from students
where age > (select avg(age) from students);
-- `union`:union语句的第一个之后的select语句
-- `union result`:匿名临时表
explain
select name, age, work
from students
where name = '小张'
union
select name, age, work
from students
where name = '小明';
-- `derived`:用于from中的子查询
explain
select *
from (select name, age, work from students where name = '小张'
union
select name, age, work from students where name = '小明') as tmp;
图示输出:
2.type
type
:访问类型(MySQL查询表中行的方式)
-- all:全表扫描(效率极低)
explain
select *
from students
where name like '%小%';
-- index:根据索引的次序进行全表扫描(效率低)
explain
select name, age, work
from students
where name like '%小%'; -- 其实就是上面全表扫描的改进版
-- range:根据索引做指定范围扫描
explain
select name, age, work
from students
where id > 5;
-- ref:返回表中所有匹配某单个值的所有行
explain
select name, age, work
from students
where name = '小明';
-- eq_ref:等同于ref,与某个值做比较且仅返回一行
explain
select *
from userinfo
inner join (select id from userinfo limit 10000000,10) as tmp
on userinfo.id = tmp.id; -- 1s
-- const:根据具有唯一性索引查找时,且返回单个行(**性能最优**)
explain
select name, age, work
from students
where id = 3; -- 一般都是主键或者唯一键
图示输出:
3.key-len
- 是否为空:
- not null 不需要额外的字节
- null 需要1字节用来标记
- PS:索引最好不要为null,这样需要额外的存储空间而且统计也变得更复杂
- 字符类型(char、varchar)的索引长度计算:
- 字符编码:(PS:不同字符编码占用的存储空间不同)
latin1
|ISO8859
占1个字节,gbk
占2个字节,utf8
占3个字节
- 变长字段(varchar)需要额外的2个字节
- 1字节用来保存需要的字符数
- 1字节用来记录长度(PS:如果列定义的长度超过255则需要2个字节【总共3字节】)
- 定长字段(char)不需要额外的字节
- 字符编码:(PS:不同字符编码占用的存储空间不同)
- 数值类型、日期类型的索引长度计算:
- 一般都是其本身长度,如果可空则+1
- 标记是否为空需要占1个字节
- PS:datetime在5.6中字段长度是5,在5.5中字段长度是8
- 一般都是其本身长度,如果可空则+1
- 复合索引有最左前缀的特性。如果复合索引能全部用上,则为复合索引字段的索引长度之和
- PS:可以用来判断复合索引是否全部使用到
- 举个栗子:
- eg:
char(20) index 可空
key-len=20*3(utf8)+1(可空)=61
- eg:
varchar(20) index 可空
key-len=20*3(utf8)+2(可变长度)+1(是否可空的标记)=63
- eg:
建表语句
create table if not exists `students`
(
id int unsigned auto_increment primary key,
name varchar(25) not null default '' comment '姓名',
age tinyint unsigned not null default 0 comment '年龄',
work varchar(20) not null default '普通学生' comment '职位',
create_time datetime not null comment '入学时间',
datastatus tinyint not null default 0 comment '数据状态'
) charset utf8 comment '学生表';
-- select current_timestamp(), now(), unix_timestamp();
insert into students(name, age, work, create_time, datastatus)
values ('111', 22, 'test', now(), 99),
('小张', 23, '英语课代表', now(), 1),
('小李', 25, '数学课代表', now(), 1),
('小明', 21, '普通学生', now(), 1),
('小潘', 27, '物理课代表', now(), 1),
('张小华', 22, '生物课代表', now(), 1),
('张小周', 22, '体育课代表', now(), 1),
('小罗', 22, '美术课代表', now(), 1);
-- 创建一个组合索引
create index ix_students_name_age_work on students (name, age, work);
说了这么多题外话,现在进入正题:
1.5.3.建表优化
- 定长和变长分离(具体得看业务)
- eg:varchar、text、blob等变长字段单独出一张表和主表关联起来即可
- 常用字段和不常用字段分离
- 根据业务来分析,不常用的字段拎出来
- 在1对多需要关联统计的字段上添加点冗余字段
- 分表分库时,扩表跨库查询的情景(注意数据一致性)
- eg:在分类表中添加一个数量字段,统计每天新增商品数量
- 添加商品时,选完分类就update一下count值(第二天清零)
- 字段类型一般都是按照这个优先级:(尽量使用优先级高的类型)
数值 > 日期 > char > varchar > text、blob
- PS:总体原则就是够用即可,然后尽量避免null(不利于索引,浪费空间)
- eg:varchar(10)和varchar(300),在表连接查询时,需要的内存是不一样的
- 伪hash法:比如商品url是一个varchar的列
- 这时候再建一个hash(url)之后的列,把索引设置到该列
- 推荐使用
crc32
(用bigint存储)索引空间就会小很多而且可以避免全表扫描 - eg:
select crc32('http://www.baidu.com/shop/1.html');
- 推荐使用
- PS:如果DBA配置了crc64,则使用;如果没有,可以加个条件(
CRC32碰撞后的解决方案
)- 对于少部分碰撞的记录,只需要多扫描几行就行了,不会出现全表扫描的情况
- eg:
select xxx from urls where crc_url=563216577 and url='url地址'
- 这时候再建一个hash(url)之后的列,把索引设置到该列
PS:需要关注的技术点:crc32
1.5.4.组合索引专题
项目里面使用最多的是组合索引,这边先以组合索引为例:
1.尽可能多的使用索引列,尽可能使用覆盖索引
-- 如果我查询的时候,索引的三列都用到了,那么速度无疑是最快的
-- Extra:using where
explain
select id, name, age, work, create_time
from students
where name = '小张'
and age = 23
and work = '英语课代表';
-- PS:★尽量使用覆盖索引★(近乎万能)
-- 覆盖索引:仅仅查找索引就能找到所需要的数据
-- Extra:using where;using index
explain
select name, age, work
from students
where name = '小张'
and age = 23
and work = '英语课代表';
-- PS:一般把经常select出的列设置一个组合索引,一般不超过5个
图示:
2.最左前缀原则
类比火车,火车头自己可以开,车身要是没有了车头就开不了
-- 查询的时候从最左边的列开始,并且不跳过中间的列,一直到最后
explain
select id, name, age, work, create_time
from students
where name = '小张'
and age = 23
and work = '英语课代表';
-- 跳过了中间的age,这时候只用到了name列的索引(work列没用到)
explain
select id, name, age, work, create_time
from students
where name = '小张'
and work = '英语课代表';
图示:
再看两个补充案例:
-- PS:如果跳过了第一列,这时候索引一个也用不到,直接全表扫描了
explain
select id, name, age, work, create_time
from students
where age = 23
and work = '英语课代表';
-- PS:列不一定需要按照指定顺序来写
explain
select id, name, age, work, create_time
from students
where age = 23
and work = '英语课代表'
and name = '小张';
图示:
2.3.范围条件放在最后面(范围条件后面的列索引会失效)
-- name、age、work索引生效时,key_len=140
explain
select id, name, age, work, create_time, datastatus
from students
where name = '小张'
and age = 23
and work = '英语课代表';
-- 现在key_len=78 ==> work列索引就失效了(PS:age索引列未失效,只是age之后的列失效了)
explain
select id, name, age, work, create_time, datastatus
from students
where name = '小张'
and age > 22
and work = '英语课代表';
图示:
补充说明:
-- 加快查询速度可以使用覆盖索引
explain
select name, age, work
from students
where name = '小张'
and age > 22
and work = '英语课代表';
-- PS:多个主键列也一样
explain
select id, name, age, work
from students
where name = '小张'
and age > 22
and work = '英语课代表';
-- PS:调换顺序是没法解决范围后面索引失效的(本来对顺序就不在意)
explain
select id, name, age, work, create_time, datastatus
from students
where name = '小张'
and work = '英语课代表'
and age > 22;
图示:
2.4.不在索引列上做其他操作
容易导致全表扫描,这时候利用覆盖索引可以简单优化下
1.!=
、is not null
、is null
、not in
、in
、like
慎用
!=
、is not null
、is null
的案例
-- 1.不等于案例
-- 索引失效(key,key_len ==> null)
explain
select id, name, age, work, create_time, datastatus
from students
where name != '小明'; -- <> 等同于 !=
-- 项目里面很多使用都要使用,那怎么办呢?==> 使用覆盖索引
-- key=ix_students_name_age_work,key_len=140
explain
select name, age, work
from students
where name != '小明'; -- <> 等同于 !=
-- 2.is null、is not null案例
-- 索引失效(key,key_len ==> null)
explain
select id, name, age, work, create_time, datastatus
from students
where name is not null;
-- 解决:覆盖索引 key=ix_students_name_age_work,key_len=140
explain
select name, age, work
from students
where name is not null;
图示:
not in
、in
的案例
-- 3.not in、in案例
-- 索引失效(key,key_len ==> null)
explain
select id, name, age, work, create_time, datastatus
from students
where name in ('小明', '小潘', '小李');
explain
select id, name, age, work, create_time, datastatus
from students
where name not in ('小明', '小潘', '小李');
-- 解决:覆盖索引 key=ix_students_name_age_work,key_len=140
explain
select name, age, work
from students
where name in ('小明', '小潘', '小李');
explain
select name, age, work
from students
where name not in ('小明', '小潘', '小李');
图示:
like
案例:尽量使用xxx%
的方式来全文搜索,能和覆盖索引联合使用更好
-- 4.like案例
-- 索引不失效 key=ix_students_name_age_work,key_len=77(尽量这么用like)
explain
select id, name, age, work, create_time, datastatus
from students
where name like '张%';
-- 索引失效
explain
select id, name, age, work, create_time, datastatus
from students
where name like '%张';
-- 索引失效
explain
select id, name, age, work, create_time, datastatus
from students
where name like '%张%';
-- 解决:覆盖索引 key=ix_students_name_age_work,key_len=140(尽量避免)
explain
select name, age, work
from students
where name like '%张%';
2.计算、函数、类型转换(自动 or 手动)【尽量避免】
-- 4.2.计算、函数、类型转换(自动 or 手动)【尽量避免】
-- 这时候索引直接失效了,并全表扫描了
-- 解决虽然可以使用覆盖索引,但是尽量避免下面的情况:
-- 1.计算
explain
select id, name, age, work, create_time, datastatus
from students
where age = (10 + 13);
-- 2.隐式类型转换(111==>'111')
explain
select id, name, age, work, create_time, datastatus
from students
where name = 111;
-- PS:字符类型不加引号索引就直接失效了
-- 虽然覆盖索引可以解决,但是不要这样做(严格意义上讲,这个算个错误)
-- 3.函数
explain
select id, name, age, work, create_time, datastatus
from students
where right(name, 1) = '明';
图示:
光看没意思,再举个简单的业务案例:
eg:用户一般都是根据商品的大分类=>小分类=>品牌来查找,有时候到不看品牌,直接小分类后就自己找了。那么组合索引可以这么建:
index(分类id,商品价格)
,index(分类id,品牌id,商品价格)
(一般都需要根据查询日记来确定)
PS:有些条例是流传甚广的,有些是工作中的经验,至少都是我踩过坑的,可以相对放心(业务不同优化角度不同)
1.5.5.写法上的优化
5.1.or改成union
-- 5.1.or改成union
-- 现在高版本对只有一个or的sql语句有了优化
explain
select id, name, age, work, create_time, datastatus
from students
where name = '小明'
or name = '小张'
or name = '小潘';
-- PS:等同上面or的语句
explain
select id, name, age, work, create_time, datastatus
from students
where name in ('小明', '小张', '小潘');
-- 高效
explain
select id, name, age, work, create_time, datastatus
from students
where name = '小明'
union all
select id, name, age, work, create_time, datastatus
from students
where name = '小张'
union all
select id, name, age, work, create_time, datastatus
from students
where name = '小潘';
PS:union总是产生临时表,优化起来比较棘手
一般来说union子句尽量查询最少的行,union子句在内存中合并结果集需要去重(浪费资源),所以使用union的时候尽量加上all(在程序级别去重即可)
5.2.count优化
一般都是count(主键|索引)
,但现在count(*)
基本上数据库内部都优化过了(根据公司要求使用即可)
PS:记得当时踩了次坑,等复现的时候补上案例(记得好像跟null相关)
看下就知道为什么说无所谓了(PS,你count(非索引)
就有所谓了)
explain
select count(id) -- 常用
from userinfo;
explain
select count(*)
from userinfo;
-- 你`count(非索引)`就有所谓了
explain
select count(password)
from userinfo;
我想说的优化是下面这个count优化案例:(有时候拆分查询会更快)
-- 需要统计id>10000的数据总量(实际中可能会根据时间来统计)
explain
select count(*) as count
from userinfo
where id > 10000; -- 2s
-- 分解成用总数-小数据统计 ==> 1s
explain
select (select count(*) from userinfo) - (select count(*) from userinfo where id <= 10000) as count;
执行图示:
分析图示:
5.3.group by和order by
group by
和order by
的列尽量相同,这样可以避免filesort
-- 5.3.group by和order by的列尽量相同,这样可以避免filesort
explain
select *
from students
group by name
order by work;
explain
select *
from students
group by name
order by name;
-- 加where条件也一样
explain
select *
from students
where name like '小%'
group by age
order by work;
-- PS:一般group by和order by的列都和where索引列相同(不一致也只会使用一个索引)
explain
select *
from students
where name like '小%' and age>20
group by name
order by name;
-- where后面的索引列和`order by|group by`后面的索引列不一致
-- id和email都是索引,但只用了一个索引
explain
select *
from users
where id < 10
order by email;
图示:
PS:不一致也只会使用一个索引(在索引误区有详细说明)
5.4.用连接查询来代替子查询
一般来说都是用连接查询来代替子查询,有些时候子查询更方便(具体看业务吧)
-- 用exists代替in?MySQL查询优化器针对in做了优化(改成了exists,当users表越大查询速度越慢)
explain
select *
from students
where name in (select username from users where id < 7);
-- ==> 等同于:
explain
select *
from students
where exists(select username from users where username = students.name and users.id < 7);
-- 真正改进==>用连接查询代替子查询
explain
select students.*
from students
inner join users on users.username = students.name and users.id < 7;
-- 等效写法:这个tmp是临时表,是没有索引的,如果需要排序可以在()里面先排完序
explain
select students.*
from students
inner join (select username from users where id < 7) as tmp on students.name = tmp.username;
图示:(内部已经把in转换成exists了,所以改不改写无所谓了)
5.5.★limit优化★
limit offset,N
:mysql并不是跳过offset
行,然后取N
行,而是取offset+N
行,然后放弃前offset
行,返回N
行
- PS:
offset越大效率越低
(你去翻贴的时候,页码越大一般越慢)
知识点
为了更加的直观,我们引入一下profiling
-- 查看profiling系统变量
show variables like '%profil%';
-- profiling:开启SQL语句剖析功能(开启之后应为ON)
-- 来查看是否已经启用profile
select @@profiling;
-- 启动profile(当前会话启动)
set profiling = 1; -- 0:未启动,1:启动
show profiles; -- 显示查询的列表
show profile for query 5; -- 查看指定编号查询的详细信息
输出:
MariaDB [dotnetcrazy]> show variables like '%profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| have_profiling | YES |
| profiling | OFF |
| profiling_history_size | 15 |
+------------------------+-------+
3 rows in set (0.002 sec)
MariaDB [dotnetcrazy]> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 0 |
+-------------+
1 row in set (0.000 sec)
MariaDB [dotnetcrazy]> set profiling = 1;
Query OK, 0 rows affected (0.000 sec)
正文
上面设置完后,分别执行下面SQL:
select * from userinfo limit 10,10;
select * from userinfo limit 1000,10;
select * from userinfo limit 100000,10;
select * from userinfo limit 1000000,10;
select * from userinfo limit 10000000,10;
输出:
+----------+------------+------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+------------------------------------------+
| 1 | 0.00060250 | select * from userinfo limit 10,10 |
| 2 | 0.00075870 | select * from userinfo limit 1000,10 |
| 3 | 0.03121300 | select * from userinfo limit 100000,10 |
| 4 | 0.30530230 | select * from userinfo limit 1000000,10 |
| 5 | 3.03068020 | select * from userinfo limit 10000000,10 |
+----------+------------+------------------------------------------+
图示:
解决方法
- 业务上解决,eg:不许翻页超过100(一般都是通过搜索来查找数据)
- PS:百度搜索页面也只是最多翻到76
- 使用where而不使用offset
- id完整的情况:eg:
limit 5,3 ==> where id > 5 limit 3;
- PS:项目里面一般都是逻辑删除,id基本上算是比较完整的
- id完整的情况:eg:
覆盖索引+延迟关联
:通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据- 使用场景:比如
主键为uuid
或id不连续
(eg:部分数据物理删除了等等)
- 使用场景:比如
说太空洞,演示下就清楚了:
-- 全表扫描
explain
select *
from userinfo
limit 10000000,10; -- 3s
-- 先range过滤了一部分
explain
select *
from userinfo
where id > 10000000
limit 10; -- 20ms
-- 内部查询使用了索引覆盖
explain
select *
from userinfo
inner join (select id from userinfo limit 10000000,10) as tmp
on userinfo.id = tmp.id; -- 2s
分析图示:
查询图示:
扩展:索引误区和冗余索引
1.索引误区
很多人喜欢把where条件的常用列上都加上索引,但是遗憾的事情是:独立的索引只能同时用上一个
- PS:在实际应用中往往选择
组合索引
别不信,来验证一下就知道了:
-- id和email都是索引,但是只能使用一个索引(独立的索引只能同时用上一个)
-- id的key-len=4(int4个字节)
-- email的key-len=152(50*3(utf8下每个字符占3位)+2(varchar需要额外两个字节存放)==>152)
-- 1.唯一索引和主键:优先使用主键
explain
select * from users where id = 4 and email = 'xiaoming@qq.com';
-- 2.组合索引和主键:优先使用主键
explain
select * from users where id=4 and createtime='2019-02-16 17:10:29';
-- 3.唯一索引和组合索引:优先使用唯一索引
explain
select * from users where createtime='2019-02-16 17:10:29' and email='xiaoming@qq.com';
-- 4.组合索引和一般索引:优先使用组合索引
-- create index ix_users_datastatus on users(datastatus);
-- create index ix_users_username_password on users(username,password);
explain
select * from users where datastatus=1 and username='小明';
-- 删除临时添加的索引
-- drop index ix_users_datastatus on users;
-- drop index ix_users_username_password on users;
图示:
PS:根据测试得知,一次只能使用1个索引。索引优先级:主键 > 唯一 > 组合 > 普通
2.冗余索引
举个标签表的例子:
create table tags
(
id int unsigned auto_increment primary key,
aid int unsigned not null,
tag varchar(25) not null,
datastatus tinyint not null default 0
);
insert into tags(aid,tag,datastatus) values (1,'Linux',1),(1,'MySQL',1),(1,'SQL',1),(2,'Linux',1),(2,'Python',1);
select id, aid, tag, datastatus from tags;
输出:
+----+-----+--------+------------+
| id | aid | tag | datastatus |
+----+-----+--------+------------+
| 1 | 1 | MySQL | 1 |
| 2 | 1 | SQL | 1 |
| 3 | 2 | Linux | 1 |
| 4 | 2 | Python | 1 |
+----+-----+--------+------------+
实际应用中可能会根据tag查找文章列表
,也可能通过文章id查找对应的tag列表
项目里面一般是这么建立索引(冗余索引):index(文章id,tag),index(tag,文章id),这样在上面两种情况下可以直接用到覆盖索引
create index ix_tags_aid_tag on tags(aid,tag);
create index ix_tags_tag_aid on tags(tag,aid);
select tag from tags where aid=1;
select aid from tags where tag='Linux';
3.修复碎片
这边简单说下,下一章应该还会继续说运维相关的知识
数据库表使用时间长了会出现碎片,可以定期修复一下(不影响数据):optimize table users;
修复表的数据以及索引碎片会把数据文件整理一下,这个过程相对耗费时间(数据量大的情况下)一般根据情况选择按周|月|年修复一下
PS:可以配合crontab
(定时任务)使用:
- 使用命令:
crontab -e
:***** 命令 [ > /dev/null 2>&1 ]
5个*的含义
:分
、时
、日
、月
、周
- 从定向知识:
>> /xx/日志文件
:输出重定向到日记文件(不包含错误信息)>> /xx/日志文件 2>&1
:输出信息包括错误信息> /dev/null 2>&1
:出错信息重定向到垃圾桶(黑洞)
- 举几个栗子:
21*** xxx
==> 每天 1:02 执行 xxx命令5921*** xxx
==> 每天 21::59 执行 xxx命令*/*1*** xxx
==> 每1小时 执行一次xxx命令- 定时任务以
*/
开头
- 定时任务以
课后拓展:
【推荐】一步步分析为什么B+树适合作为索引的结构
https://blog.csdn.net/weixin_30531261/article/details/79312676
善用mysql中的FROM_UNIXTIME()函数和UNIX_TIMESTAMP()函数
https://www.cnblogs.com/haorenergou/p/7927591.html
【推荐】MySQL crc32 & crc64函数 提高字符串查询效率
https://www.jianshu.com/p/af6cc7b72dac
MySQL优化之profile
https://www.cnblogs.com/lizhanwu/p/4191765.html
聊聊数据库~4.SQL优化篇的更多相关文章
- 聊聊数据库~3.SQL基础篇
上篇回顾:聊聊数据库~SQL环境篇 扩展:为用户添加新数据库的权限 PS:先使用root创建数据库,然后再授权grant all privileges on 数据库.* to 用户名@"%& ...
- 聊聊数据库~2.SQL环境篇
传统数据库 上篇文章:聊聊数据库~开篇 https://www.cnblogs.com/dotnetcrazy/p/9690466.html 本来准备直接开讲NoSQL的(当时开篇就是说的NoSQL) ...
- 聊聊数据库~5.SQL运维上篇
1.6.SQL运维篇 运维这块逆天只能说够用,并不能说擅长,所以这篇就当抛砖之用,欢迎补充和纠错 PS:再说明下CentOS优化策略这部分的内容来源:首先这块逆天不是很擅长,所以主要是参考网上的DBA ...
- 简单聊聊TiDB中sql优化的一个规则---左连接消除(Left Out Join Elimination)
我们看看 TiDB 一段代码的实现 --- 左外连接(Left Out Join)的消除; select 的优化一般是这样的过程: 在逻辑执行计划的优化阶段, 会有很多关系代数的规则, 需要将逻辑执行 ...
- 面试问题之数据库:SQL优化的具体操作
转载于:https://www.cnblogs.com/wangzhengyu/p/10412499.html SQL优化的具体操作: 1.尽量避免使用select *,返回无用的字段会降低查询效率. ...
- 聊聊数据库~6.SQL运维中篇
上篇回顾:https://www.cnblogs.com/dotnetcrazy/p/10810798.html#top 1.6.5.MySQL日志相关 本文的测试环境:MySQL5.7.26.Mar ...
- 【数据库】SQL优化方法汇总
最近在研究SQL语句的优化问题. 下面是从网上搜集的,有的地方有点老了,可是还是有很多可以借鉴的地方的. 如何加快查询速度? 1.升级硬件. 2.根据查询条件,建立索引,优化索引.优化访问方式,限制结 ...
- 数据库及SQL优化
一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...
- 数据库的SQL优化
1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. (因为在条件查询条件添加索引,会直接被检索到列,会非常的快速) 2.应尽量避免在 wher ...
随机推荐
- BZOJ_2134_单选错位——期望DP
BZOJ_2134_单选错位——期望DP 题意: 分析:设A为Ai ∈ [1,ai+1] 的概率,B为Ai = A(imodn+1)的概率显然P(A|B) = 1,那么根据贝叶斯定理P(B) = P( ...
- (2)STM32使用HAL库操作外部中断——理论讲解
1.中断触发过程 对主程序压栈--把中断服务函数的地址写入到程序计数器(PC)--执行中断服务函数 2.中断向量表 中断服务函数的地址在STM32的手册上的中断向量表中(如下是一部分): 如上表所示, ...
- monkey----log分析要求
对monkey测试过程中生成的XXX.log文件中进行关键字的查找.主要查找讯息如下: (1) anr项:即无响应,一般形式为ANR in org.codeaurora.bluetooth: (2)c ...
- nginx用户认证与htpasswd命令
最近在搭建ELK,然后ELK的kibana界面想添加一个访问限制,看到kibana有个插件x-pack,本来想用用,发现是收费的,就放弃了,然后就想着想配置下nginx的认证访问来实现简单的访问登陆. ...
- 从壹开始 [Admin] 之四 || NetCore + SignalR 实现日志消息推送
缘起 哈喽大家周一好呀,感觉好久没有写文章了,上周出差了一次,感觉还是比坐办公室好的多,平时在读一本书<时生>,感兴趣的可以看看
- 网络协议 终章 - GTP 协议:复杂的移动网络
前面都是讲电脑上网的情景,今天我们就来认识下使用最多的移动网络上网场景. 移动网络的发展历程 你一定知道手机上网有 2G.3G.4G 的说法,究竟这都是什么意思呢?有一个通俗的说法就是 ...
- DateUtil日期工具类
目录 (1)需要导入的包 (2)DateUtil类 (1)需要导入的包 主要用于Strings.isNullOrEmpty(date) <!--string处理类--> <depen ...
- GDAL读取的坐标起点在像素左上角还是像素中心?
目录 1. 问题 2. 结论 3. 例外 1. 问题 笔者在处理地理栅格数据的时候,总是会发生偏差半个像素的问题. 比如说通过ArcMap打开一张.tif,查看其地理信息:同时用记事本打开.tfw,比 ...
- Android 切换横竖屏
一个项目一般会自己先定义项目是横屏还是竖屏但是也有可以横屏和竖屏之间切换的activty. 切换横竖屏的方法: //判断当前屏幕方向if(getRequestedOrientation() == Ac ...
- oppo8.0系统怎么无需Root激活Xposed框架的经验
在较多部门的引流,或业务操作中,基本都需要使用安卓的神一般的xposed框架,近期我们部门购来了一批新的oppo8.0系统,基本都都是基于7.0以上版本,基本都不能够刷入root的su权限,虽说一部分 ...