跳至主要內容

mysql 优化

OrangBus大约 11 分钟

配置文件

[mysqld]
binlog_format=mixed

OPTIMIZE 优化表

OPTIMIZE TABLE table_name;

会重组表数据和索引的物理存储,减少对存储空间使用和提升访问表时io效率。

索引说明

system>const>eq_ref>ref>range>index>ALL (越左边,查询速度越快)

1、system级别 A、只有一条数据的系统表 B、或衍生表只能有一条数据的主查询

很明显 实际开发当中 我们是不会只有一条数据的

2、const级别 仅仅能查出一条的SQL语句并且用于Primary key 或 unique索引; SELECT * from shop s where s.id=? 主键索引、唯一索引和unique索引达到这个级别,我们写sql要根据公司的业务去写,这个情况也很难达到的。

3、eq_ref级别 唯一性索引:对于每个索引键的查询,返回匹配唯一行数据(有且只有1个,不能多,不能0); 比如你select …from 一张表 where 比方说有一个字段 name = 一个东西,也就是我们以name作为索引,假设我之前给name加了一个索引值,我现在根据name去查,查完后有20条数据,我就必须保证这二十条数据每行都是唯一的,不能重复不能为空!

只要满足以上条件,你就能达到eq_ref,当然前提是你要给name建索引,如果name连索引都没,那你肯定达不到eq_ref;

此种情况常见于唯一索引和主键索引;

比如我根据name去查,但是一个公司里面或一个学校里面叫name的可能不止一个,一般你想用这个的时候,就要确保你这个字段是唯一的,id就可以,你可以重复两个张三,但是你身份证肯定不会重复;

添加唯一键语法:alter table 表名 add constraint 索引名 unique index(列名)

检查字段是否唯一键:show index form 表名;被展示出来的皆是有唯一约束的;

4、ref级别 ref级别的问题不大。

非唯一性索引:对于每个索引键的查询,返回匹配的所有行(可以是0,或多个)。

假设有俩张三,我建立了非唯一索引,那么查出来就是这个级别拉,是不是很简单。

5、range级别 检索指定范围的行,查找一个范围内的数据,where后面是一个范围查询 (between,in,> < >=); in有时有可能会失效,导致为ALL;

6、index级别 把索引的数据全查出来 就是这个级别了

7、ALL级别 不做索引 就是all级别咯

and 查询

select * from `movies` where (`api_id` = 36 and (`type_id` in (1, 6, 7, 8, 9, 20))) order by `updated_at` desc limit 24 offset 24;

优化方案

# 显示索引
SHOW INDEX api_id_type_id from movies;
# 创建索引
CREATE INDEX api_id_tyoe_id on movies(api_id,type_id)

#删除索引
DROP index api_id_type_id on movies;

索引的使用

mysql最左原则

create index idx_name_phone on user(name,phone);

当我们查询数据库的时候,以下查询会走索引name 查询在前,phone 在后

select * from user where name like 'oranggbus%' and phone like '1830001%'

不会走索引,因为 创建的索引 name 在前,phone 在后

select * from user where phone like '1830001%' and name like 'oranggbus%' 

2、范围查询右侧的查询会失效

select * from user name="" and age > 10 and status=1; // status 会失效

3、索引进行运算会失效

select * from user where substring(phone,10,2) = "15"; // phone 索引进行了运算

4、字符串索引查询 不加 单引号,索引失效

select * from user where phone = 1830000001; 

5、模糊查询:头部匹配,索引失效,尾部匹配,不会失效

select * from user where  name like 'oranggbus%'  // 不会失效
select * from user where  name like '%oranggbus%' // 失效

6、or查询:

有索引 or 没有索引 = 涉及到的索引不会用到

有索引 or 有索引 = 涉及到的索引会用到

7、分布影响

覆盖索引

使用索引,并且在返回的列,在该索引中已经全部找到,减少 select *

using index condition 使用索引,需要回表

using where susing index 使用索引,返回数据在索引列中能够找到,不需要会表查询

image-20231025215654472
image-20231025215654472

回表查询:使用覆盖索引查询的时候,返回的结果中有额外的字段,需要回表二次查询

前缀索引

长文本字段查询,减少索引体积,减少io,提高查询效率

提取一个长文本的前几个字符建立索引

create index idx_xx on table_name(colume(n))
create index idx_title_26 on articles(title(26));

如果选择长度

select count(distinct substring(title,1,10))/count(*) from article;

设计原则

1、针对于数据量较大,且查询比较频繁的表建立索引。 2、针对于常作为查询条件 (where)、排序(order by)、分组 (group by)操作的字段建立索引。 3、尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。 4、如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。 5、尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

6、要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。 7、如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个7索引最有效地用于查询。

mysql索引使用

参考网站:https://www.begtut.com/mysql/mysql-index.html

添加索引

CREATE INDEX idx_name on tablename(filed);
CREATE INDEX idx_name_age on user(name,age);

alert table user add index index_email (email);

# 唯一索引(字段值可以为null)
CREATE UNIQUE INDEX index_phone on user (phone);
alert table user add unique (phone);

# 逐渐索引(字段值不能为null)
CREATE PRIMARY KEY INDEX index_phone ON user (phone);
alert table user add primary key (phone);

演示

SELECT count(1) from tableName;


select * from tableName where user.phone='123456789'

查看索引

show index from user;

删除索引

drop index idx_name on jokes;

联合索引

索引选择

  • 经常使用到的列在最左边
  • 选择性高的列优先
  • 宽度小的列

排序优化

  • 要求where子句使用的所有字段,都必须建立索引;
  • 不要在索引列上面做任何操作(计算、函数),否则会导致索引失效而转向全表扫描
  • 索引的列顺序和 order by 字句的顺序完全一致
  • 索引中多有列的方向(升序,降序)和 order by 子句完全一致
  • order by 中的字段全部在关联表中的第一张表中
select * from table where colume > 10 order by u

注意事项

  1. like %keyword 索引失效,使用全表扫描。但可以通过翻转函数+like前模糊查询+建立翻转函数索引=走翻转函数索引,不走全表扫描。
  2. like keyword% 索引有效
  3. like %keyword% 索引失效,也无法使用反向索引。
  4. not in<> 不能使用索引

可参考:https://www.cnblogs.com/yizhiamumu/p/9055531.html

https://blog.csdn.net/weixin_28973649/article/details/116112874

CREATE INDEX idx_id_title on jokes(id,title);

CREATE INDEX idx_id on joke_cates(id);

SELECT count(1) FROM jokes;

SELECT * FROM jokes WHERE id < 1000;

select * from jokes where title like '搞笑%'

EXPLAIN select title from jokes where title like '%搞笑%'

添加演示数据

https://downloads.mysql.com/docs/sakila-db.zip

优化案例

order by 查询太慢

  • 索引的列顺序和order by 子句的顺序完全一致

  • 索引中所有的列的方向(升序,降序)和 order by 子句完全一致

  • order by 中的字段全部在关联表的第一张表

    order by 的字段是主表(个人理解)

1、配置,当内存比较大的时候可以适当放大这个值

sort_buffer_site

2、建立索引

加入我们需要需要进行id倒叙查询

select * from jokes where cate_id=1 order by id limit 15

索引创建: 创建一个以 where 条件进行查询的联合索引

create index idx_cate_id_id on jokes(cate_id,id);

3、回表生成结果集

当行小于 max_length_for_sort_data 会生成全字段中间结果集

用户查询

# 用户统计
select count(1) from million_users; # 2 s 823 ms
# 查看寻一个用户
select * from million_users where id=5600; # 89 ms
select * from million_users order by id desc limit 20; # 89 ms
select * from million_users where money > 1000 order by id desc limit 20; # 79 ms
# 下面这两个也是耗时的查询
select * from million_users where name="橙留香" and password="caVLvYBY8HfRjGSOaM99jHSyM8o7Gv"; # 5 s 606 ms
select * from million_users where name like "hlubo%" order by id desc limit 20; # 5 s 827 ms

# 查看所有
show index from million_users;
# 创建索引
create index idx_name_password on million_users(name,password);
# 删除索引
drop index idx_name_password on million_users;

# 有索引的情况下查询
select * from million_users where name="橙留香" and password="caVLvYBY8HfRjGSOaM99jHSyM8o7Gv"; # 74 ms
select * from million_users where name like "hlubo%" order by id desc limit 20; # 107 ms

关联统计优化

通过【文章分类】统计该分类下面的【文章】数

jokejoke_cate
id,cate_idid

优化建议: 在 joke 中创建一个 cate_id 字段的索引

create index idx_cate_id on joke(cate_id);

Mysql占用空间统计

「所有库」的容量大小

SELECT 
table_schema as '数据库',
sum(table_rows) as '记录数',
sum(truncate(data_length/1024/1024, 2)) as '数据容量(MB)',
sum(truncate(index_length/1024/1024, 2)) as '索引容量(MB)',
sum(truncate(DATA_FREE/1024/1024, 2)) as '碎片占用(MB)'
from information_schema.tables
group by table_schema
order by sum(data_length) desc, sum(index_length) desc;

MySQL 数据库中容量排名前 10 的表

USE information_schema;
SELECT 
  TABLE_SCHEMA as '数据库',
  table_name as '表名',
  table_rows as '记录数',
  ENGINE as '存储引擎',
  truncate(data_length/1024/1024, 2) as '数据容量(MB)',
  truncate(index_length/1024/1024, 2) as '索引容量(MB)',
  truncate(DATA_FREE/1024/1024, 2) as '碎片占用(MB)'
from  tables 
order by table_rows desc limit 10;

「指定库」中,容量排名前 10 的表

USE information_schema;
SELECT 
  TABLE_SCHEMA as '数据库',
  table_name as '表名',
  table_rows as '记录数',
  ENGINE as '存储引擎',
  truncate(data_length/1024/1024, 2) as '数据容量(MB)',
  truncate(index_length/1024/1024, 2) as '索引容量(MB)',
  truncate(DATA_FREE/1024/1024, 2) as '碎片占用(MB)'
from  tables 
where 
  table_schema='laravel_web'  # 表名
order by table_rows desc limit 10;

「指定库」中「指定表」的容量大小

SELECT
  table_schema as '数据库',
  table_name as '表名',
  table_rows as '记录数',
  truncate(data_length/1024/1024, 2) as '数据容量(MB)',
  truncate(index_length/1024/1024, 2) as '索引容量(MB)',
  truncate(DATA_FREE/1024/1024, 2) as '碎片占用(MB)'
from 
  information_schema.tables
where 
  table_schema='laravel_web'and table_name='movies'
order by 
  data_length desc, index_length desc;

查询技巧

经纬度范围查询

SELECT qymc,lng,lat,
(st_distance(point(lat,lng),point(116.452404,39.947689))*111195) AS distance
FROM fxxf_tsgs 
HAVING distance<100
ORDER BY distance 
limit 100

慢查询统计

根据慢查询总数倒序统计

mysqldumpslow -s c movie-slow.log > movie_total_desc.txt

根据慢查询时长倒序统计

mysqldumpslow -s t movie-slow.log > movie_time_desc.txt

基本命令

用户操作

# 创建用户
create user demo@'%' identified by '123456';

# 查看用户信息
select user,host,plugin from mysql.user;

# 修改用户密码
alter user demo@'%' identified by 'new password';


# 修改用户连接方式
update mysql.user set Host='localhost' where user='demo';

# 删除用户
drop user demo@'%';

权限操作

# 查看当前用户权限
show grants ;

# 查看制定用户权限
show grants for orangbus@'%';

# 授权
# grant: 关键字
# test.*: 作用域,test库所有表
# 'tom'@'192.168.150.%': 哪个用户
grant select,update,insert on test.* to demo@'%';

# 授予所有权限
grant all privileges on *.* to demo@'%';

# 授予用户授权其他用户权限的权限
grant select,insert,update,delete on test.* to demo@'%' with grant option;

# 删除用户权限
revoke insert on test.* from demo@'%';

# 删除授予权限
revoke grant option on test.* from demo@'%';

# 删除所有权限
# revoke仅删除权限, 不删除用户
# revoke all 后还是会有一个USAGE 权限
revoke all on test.* from demo@'%';

数据库操作

# 创建
CREATE DATABASE 数据库名;

# 删除
drop database 数据库名;

------
create database user;
drop database user;

表操作

# 创建表
CREATE TABLE table_name (
	id INT UNSIGNED AUTO_INCREMENT,
    name char(11) not null,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 添加表字段
alert table tabme_name add phone char(11) first|after filed_name;

# 修改表字段
 alert table table_name change phone phone varchrt(11);

# 删除表字段
alert table table_name drop phone;

# 删除表
drop table table_name;

导入sql

mysql -u username -p database_name < file.sql

导出sql

mysqldump -u username -p database_name > dump.sql

mysqldump -u username -p database_name table_name > table_dump.sql

mysqldump -u username -p --no-data database_name > structure_dump.sql

1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u dbuser -p dbname > dbname.sql

2.导出一个表 mysqldump -u 用户名 -p 数据库名 表名> 导出的文件名 mysqldump -u dbuser -p dbname users> dbname_users.sql

3.导出一个数据库结构 mysqldump -u dbuser -p -d --add-drop-table dbname >d:/dbname_db.sql -d 没有数据 --add-drop-table 在每个create语句之前增加一个drop table

4.导入数据库 常用source 命令 进入mysql数据库控制台,如 mysql -u root -p mysql>use 数据库 然后使用source命令,后面参数为脚本文件(如这里用到的.sql) mysql>source d:/dbname.sql

mysql导入大文件

修改该目录下my.ini文件中max_allowed_packet 因为默认max_allowed_packet为1k,如果导入的文件过大。可能会报错。 我们将该值改大一点,我这里设为1G。

[mysqld]

max_allowed_packet = 1024M

查看设置是否生效

show VARIABLES like '%max_allowed_packet%';

本地终端连接数据库导入

mysql -h 192.168.3.1 -uroot -p

use mydb;

set names utf8;

source /path/to/insertTable.sql

====
# 开启事务
start transaciton;

# 引入SQL文件
source xxx.sql

# 成功后提交事务
commit;

日志