MySQL/MariaDB数据库

指令

终端指令

  • mysql -uroot -p 回车 输入密码后再回车

  • source 全路径/xxx.sql;//导入*.sql批处理文件

  • show variables like '%collation%';//查询终端和mysql的字符集

  • show variables like '%max_connection%';//查询支持的最大连接数

  • set global max_connections=200;//设置mysql最大连接数

  • show variables like '%query_cache%';查询mysql是否开启了查询缓存

  • set global slow_query_log=0n; //开启慢查询

  • set names gbk;修改MySql的字符集

库指令

  • show databases; //查询所有数据库

  • show create database 数据库名;//查询数据库信息

  • create database 数据库名 charset=utf8/gbk; //创建数据库

  • drop database 数据库名; //删除数据库

  • alter database 数据库名 character set 字符集(utf8/gbk);//修改数据库字符集

  • use 数据库名; //使用数据库

表指令

  • show tables; //查询所有表

  • show create table 表名; //查询表信息

  • desc 表名; //查询表字段信息

  • create table 表名(字段 1 名 类型,字段 2 名 类型)charset=utf8/gbk; //创建表

  • drop table 表名; //删除表

  • rename table 原名 to 新名; //修改表名

  • alter table 表名 add 字段名 类型; //最后面添加表字段

  • alter table 表名 add 字段名 类型 first; //最前面添加表字段

  • alter table 表名 add 字段名 类型 after xxx; //在xxx字段后面添加表字段

  • alter table 表名 drop 字段名; //删除表字段

  • alter table 表名 change 原名 新名 新类型; //修改表字段

  • alter table 表名 default charset 字符集名称;//修改表默认字符集

  • comment//给字段注释,可以在创建表的时候使用

  • alter table 表名 change 字段名 字段名 timestamp not null default current_timestamp//插入数据时候会自动更新时间戳

  • alter table 表名 change 字段名 字段名 timestamp not null default current_timestamp on update current_timestamp//插入数据时候会自动更新时间戳,同时更新数据时候会更新值

数据指令

  • insert into 表名 values(值 1,值 2,值 3); //插入全值数据

  • insert into 表名(字段 1,字段 2) values(值 1,值 2); //插入指定字段数据

  • insert into 表名 values(值 1,值 2,值 3),(值 4,值 5,值 6); //批量插入全值数据

  • insert into 表名(字段 1,字段 2) values(值 1,值 2); //批量插入指定字段数据

  • delete from 表名 where 条件; //删除数据

  • update 表名 set 字段名=值,字段名=值 where 条件; //修改数据

  • select 字段信息 from 表名 where 条件; //查询数据

数据类型

  • 整数: int(m) 和 bigint(m) , bigint等效Java中的long , m代表显示长度 ,zerofill用来补零

  • 浮点数: double(m,d), m代表总长度 d代表小数长度 ,例如23.645 m=5 d=3

  • 浮点数: decimal

  • 字符串

  • char(m): 固定长度, m=5 存 "刘德华" 占5个字符长度, 执行效率略高, 最大值255

  • varchar(m):可变长度, m=5 存 "刘德华" 占3个字符长度,节省空间, 最大值65535但是建议保存255以内的数据,超过255的建议使用text

  • text(m):可变长度, 最大值65535

  • 日期

  • date: 保存年月日

  • time: 保存时分秒

  • datetime: 保存年月日时分秒, 默认值为null, 最大值9999-12-31

  • timestamp:(时间戳:距离1970年1月1日的毫秒数)保存年月日时分秒,默认值为当前系统时间, 最大值2038-1-19

  • 使用原则

  • 尽量选择简单数据类型(例如存储整数用int不用varchar)

  • 尽量使用最小数据类型(例如能用tinyint不用int)

  • 假如要存储小数可以考虑使用decimal类型。

  • 尽量避免使用text、blob等大字段类型(假如需要使用则尽量放到一张表中)

字段约束

  • 非空约束(not null):字段的值不允许为空

  • 主键约束(primary key):字段值不允许为空并且唯一,包含索引

  • 主键自增约束(primary key auto_increment):主键约束+自增,自增规则是从历史最大值基础上+1,包含索引

  • 唯一约束(unique key):字段值必须唯一,包含索引

  • 检查约束(check):字段值需要在指定范围(但是数据库之间的兼容不好)

  • 外键约束(foreign key):字段值需要参考引用表中的字段值。作用是保护数据的完整性,包含索引

嵌套查询

  • 把一条SQL语句嵌套到另外一条SQL语句中,称为子查询

  • 案例

  • select * from emp where sal>(select avg(sal) from emp where dept_id=2);

  • select * from emp where dept_id=(select dept_id from emp where sal=(select min(sal) from emp)) and sal!=(select min(sal) from emp);

数值计算

  • 查询每个人的姓名,工资和年终奖(5个月的工资)

  • select name,sal,5*sal 年终奖 from emp;

  • 给每个2号部门的员工涨薪5块钱

  • update emp set sal=sal+5 where dept_id=2;

关键字

  • 比较运算符 > < >= <= = !=和<>

  • and/or/not

  • 查询多个条件同时满足时使用 and

  • 满足一个条件时使用or

  • 相反时候用not

  • between x and y//在y和x之间,包含x和y

  • 字段名 in(X,Y,Z)//查询某个字段的值为多个值的时候使用in

  • distinct//去重

  • select distinct job from emp where dept_id=1;

  • like//模糊查询

  • _ : 代表1个未知字符

  • %: 代表0或多个未知字符

  • x%//以x开头

  • %x//以x结尾

  • x%y//以x开头y结尾

  • %x%//包含x

  • _x%//第二个字符是x

  • _x%y_//第二个是x倒数第二个是y

  • order by 字段名 asc升序(默认)/desc降序//对根据字段名排序

  • limit 跳过的条数,请求的条数(每页的条数)//分页,跳过的条数=(请求的页数-1)*每页的条数

  • 字段名 as xxx//把字段起个xxx的别名

  • group by 字段名//可以将某个字段相同值的数据划分为一组, 然后以组为单位进行统计查询

  • 聚合函数

  • avg(字段名)//平均值

  • max(字段名)//最大值

  • min(字段名)//最小值

  • sum(字段名)//求和

  • count(*)//计数

  • abs(字段名)//绝对值

  • year(hire_date)//获取字段的年份

  • having

  • where 后面只能写普通字段条件, 不能写聚合函数条件

  • having关键字专门用来写聚合函数条件的, 并且需要和分组group by结合使用, 写在分组的后面

  • case when表达式

select first_name,salary,
      (case when salary>=10000 then '偏高'
            when salary>=8000 then '中等'
            else '偏低' 
            end) level
from employees

select  sum(case when salary>=10000 then 1 else 0 end ) '大于等于10000',
        sum(case when salary<10000 then 1 else 0 end ) '小于10000'
from employees
  • from->on->join->where->select的临时表->group by->having->select->distinct->order by->limi

关联查询

  • 关联关系

  • 一对一:有AB两张表, A表中的一条数据对应B表中的一条数据, 同时B表中的一条数据也对应A表中的一条数据.

  • 一对多:有AB两张表, A表中的一条数据对应B表中的多条数据, 同时B表中的一条数据对应A表中的一条数据.

  • 多对多:有AB两张表, A表中的一条数据对应B表中的多条数据, 同时B表中的一条数据也对应A表中的多条数据.

  • 如何建立关系:

  • 一对一: 在AB两张表中任意一张表里面添加一个建立关系的字段指向另外一张表的主键

  • 一对多: 在AB两张表中,表示多的表中添加建立关系的字段指向另外一张表的主键

  • 多对多: 在单独的关系表中添加两个字段分别指向另外两张表的主键

  • 关联查询分类

  • 等值连接

  • 查询到的是两个表之间的交集数据(有关系的数据)

  • 格式:select 字段信息 from A,B where A.x=B.x(关联关系) and 其它条件;

  • 内连接

  • 查询到的是两个表之间的交集数据(有关系的数据)

  • 格式: select 字段信息 from A join B on A.x=B.x(关联关系) where 其它条件;

MySQL/MariaDB数据库_第1张图片
  • 外连接

  • 外连接查询的是一张表的全部和另外一张表的交集

  • 格式: select 字段信息 from A left/right join B on A.x=B.x where 其它条件;

MySQL/MariaDB数据库_第2张图片
  • 自关联(可以将自身表跟自身表关联)

select m.first_name,m.salary
from employees e join employees m
on e.manager_id=m.employee_id
where e.employee_id=206

索引

  • 所谓的索引(index)其实就是数据目录,通常情况下,索引是为了提高查询效率的

索引分类

  • 主键索引、唯一索引、组合索引、普通索引

  • 聚集索引就是索引和数据存储在一起,也就是索引与数据是不分离。一张表只有一个聚集索引(即主键索引),数据库保存数据的物理顺序依据,默认情况下就是主键id,所以按id查询数据库中的数据效率非常高

MySQL/MariaDB数据库_第3张图片
  • 非聚集索引:索引与数据是分离的,索引和数据是单独存储的.如果想在非主键列上添加索引,就是非聚集索引了.个人认为二级索引也是非聚集

  • 非聚簇索引在创建时,存储的是索引值以及索引对应的记录的地址。基于非聚簇索引查询数据时,可以先基于索引找到数据的一个地址,然后基于地址再去查找数据。单从索引角度来说,非聚集索引查找速度不如聚集索引,非聚集索引找到索引位置后还需要根据索引找到数据对应的位置

MySQL/MariaDB数据库_第4张图片
MySQL/MariaDB数据库_第5张图片

使用场景

  • 例如"张三丰" 这个姓名,创建索引后查询效率就会明显提升,如果没有索引,这样的查询就会引起效率最低的"逐行搜索",就是一行一行的查这个数据的姓名是不是张三丰,效率就会非常低

  • 使用索引需要考虑以下情况

  • 创建的索引会占用硬盘空间

  • 创建索引之后,对该表进行增删改操作时,会引起索引的更新,所以效率会降低

  • 对数据库进行批量新增时,先删除索引,增加完毕之后再创建

  • 不要对数据样本少的列添加索引

  • 模糊查询时,查询条件前模糊的情况,是无法启用索引的

  • 每次从数据表中查询的数据的比例越高,索引的效果越低

  • 当我们执行查询时,where条件后应该先查询有索引的列

  • 使用场景

  • on 子句

  • where 子句

  • group by 子句

  • having 子句

  • order by 子句

使用

  • 查看索引:如何查看表中的索引show index from [表名];

  • 索引的创建

  • 创建表的同时创建索引.(例如 create table tablename(....,index 索引名 (字段名)))

  • 创建表后通过create语句创建索引(例如 create index 索引名 on 表名(字段名))

  • 创建表后通过alter语句创建索引(例如 alter table add index 索引名(字段名))

  • 删除索引:drop index 索引名 on 表名;

存储结构

  • MySQL中InnoDB默认的索引存储结构是B+树

  • B+树有特点

  • 树中的非叶子节点只存储索引和指针

  • 树中的叶子节点存储索引和数据

  • 树中的叶子节点处于相同层,并且之间会使用双向链表连接,可以更好的支持范围查询.

MySQL/MariaDB数据库_第6张图片

B树和B+树区别

  • 一个磁盘中可以存储索引数量会更多.

  • 相同数据量的B+树相对于B树的高度相对会比较低(因为分叉多了)

  • 叶子节点之间B+树有双向链表的连接,可以支持快速的范围查询.

  • B树

MySQL/MariaDB数据库_第7张图片
  • B+树

MySQL/MariaDB数据库_第8张图片

B+树和Hash区别

  • hash索引查询效率比较高,但是不支持范围查询.

二叉树和平衡二叉树区别

  • 二叉树

MySQL/MariaDB数据库_第9张图片
  • 平衡二叉树

MySQL/MariaDB数据库_第10张图片

表设计

  • 宽表和窄表

  • 宽表就是表中字段比较多的表(字段越多维护越困难,甚至会影响查询效率)

  • 窄表就是表中字段比较少的表(维护简单、太少可能会导致大量的表关联)

  • 三大范式:范式是一种设计规范,一种关系模式,可以对表的设计起到一个指导性作用

  • 第一范式(1NF):描述的是字段名不可再分。例如姓名可再分为姓和名,这属于可再分。

//不符合第一范式
create table teacher(
   id int auto_increment,
   name varchar(50) not null comment '姓名',
   primary key (id)
)engine=InnoDB character set utf8mb4;
//符合第一范式
create table teacher
(
   id int auto_increment,
   first_name varchar(50) not null comment '姓',
   last_name varchar(50) not null comment '名',
   primary key (id)
)engine=InnoDB character set utf8mb4;
  • 第二范式(2NF): 不存在非主键字段对主键字段的部分依赖。

//不符合第二范式,因为cname依赖于cid,但不依赖于sid
create table if not exists score(
   sid bigint comment '学生编号',
   cid bigint comment '课程编号',
   cname varchar(50) not null comment '课程名',
   score int not null comment '成绩',
   primary key (sid,cid) comment '复合主键’
)engine=InnoDB character set utf8mb4;
//符合第二范式,同时把cid和cname设计在另外一张表
create table if not exists score
(
   sid bigint comment '学生编号',
   cid bigint comment '课程编号',
   score int not null comment '成绩',
   primary key (sid,cid)
)engine=InnoDB character set utf8mb4;
  • 第三范式(3NF): 不存在非主键字段对主键字段的传递依赖。

//不满足第三范式,因为存在传递依赖,这里的邮编依赖于街道,街道又依赖于部门id,所以这里存在传递依赖
create table if not exists departments
(
  id int auto_increment comment '部门编号',
  name varchar(100) not null comment '部门名称',
  city varchar(20) not null comment '所在城市',
  street_address VARCHAR(40) not null '街道',
  postal_code VARCHAR(12) default '' comment '邮编',
  primary key (id)
)engine=InnoDB character set utf8mb4;
//满足第三范式,可以将部门地址信息写到locations表,然后departments表再与locations建立关系
create table if not exists locations
(
  id int auto_increment comment '地址编号',
  city varchar(20) not null comment '城市',
  street_address VARCHAR(40) not null comment '街道',
  postal_code VARCHAR(12) default '' comment '邮编',
  primary key (id)
)engine=InnoDB character set utf8mb4;

create table if not exists departments
(
id int auto_increment comment '部门编号',
name varchar(100) not null comment '部门名称',
location_id int,
unique key (name),
primary key (id),
foreign key (location_id) references locations(id)
)engine=InnoDB character set utf8mb4;
  • 反范式,范式设计为我们进行表设计提供一些指导性思想,但实际项目中有时为了提高查询效率,可能会在表中适当的添加一些冗余字段。就类似于将课程名添加到成绩表中,这样查询成绩表时可以直接查询出课程名,不需要再去关联课程表进行查询了。但是这种冗余可能会带来更新的复杂读。例如更新课程表的课程名时,还要去更新成绩表中的课程名。

执行流程

  • 查询流程

MySQL/MariaDB数据库_第11张图片
  • 更新流程

MySQL/MariaDB数据库_第12张图片

视图View

  • 数据库中的一个对象,可以将其看成是一张虚拟表,基于表创建,视图中只有结构,不存储数据,可以通过视图查询到表中的数据.

  • 使用视图,可以简化程序对SQL语句的编写,更好保证数据数据的安全(隐藏了对实际表的访问)

  • 创建视图

create view emp_view as
select e.employee_id,d.department_name,l.city
from employees e join departments d on e.department_id = d.department_id
join locations l on d.location_id = l.location_id;
  • 使用视图

select employee_id,department_name,city from emp_view;
  • 删除视图

drop view if exists emp_view;

事务

  • 一个逻辑工作单元,这个工作单元中的所有操作,要么都成功要么都失败.通过事务的四大特性(原子性,一致性,隔离性,持久性)保证数据的正确性(完整,一致)。原子性:要么一起成功要么一起失败,一致性:事务的执行不能破坏数据库的完整和一致,隔离性:一个事务不能被另外一个事务影响,持久性:事务提交以后会被保存下来

  • 查询事务的当前级别:select @@tx_isolation

  • 修改事务的级别

 set session transaction isolation level read uncommitted;
 set session transaction isolation level read committed;
 set session transaction isolation level repeatable read;
 set session transaction isolation level serializable;
  • 事务并发产生的问题通过修改事务的隔离级别可以避免,但要注意一点,隔离级别越高效率越差,一般推荐事务的隔离级别为读已提交(read commoitted)。底层是通过锁和MVCC(多版本并发控制)

  • 脏读 (一个事务读取了其它事务未提交的数据)

  • 不可重复读(一个事务对同样查询条件的数据进行多次查询时,得到结果不一致)

  • 幻读 (一个事务读取到的数据可能是表中不存在数据)

锁的分类

  • 从性能维度进行分类(悲观锁,乐观锁-性能教好),无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想,所以,不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁、表锁、排他锁、共享锁)混为一谈。其实,在DBMS中,悲观锁正是利用数据库本身提供的锁机制来实现的

  • 从操作类型上进行分类(共享锁,排它锁)

  • 从数据锁定粒度上进行分析(全局锁,表锁,行锁,...)

全局锁

  • 全局锁可以对库中所有表上锁,默认是关闭的,使用前可以手动打开.

  • flush tables with read lock;打开全局锁

  • unlock tables;关闭全局锁

表锁

  • 表锁是对整张表进行锁定的一种锁的设计,可以分为表读锁,表写锁.

  • lock table regions read;添加表读锁(共享锁)

  • lock table regions write;添加表写锁(排它锁)

  • unlock tables;关闭表锁

  • 当前线程对表添加了表读锁,当前线程可以执行的操作

  • 不可以写,会出错

  • 当前线程对表添加了表读锁,其它线程可以执行的操作

  • 写会阻塞,直到解锁或超时

  • 当前线程对表添加了写锁,当前线程可以执行的操作

  • 可以读

  • 可以写

  • 当前线程对表添加了写锁,其他线程可以执行的操作

  • 不可以读写,会被阻塞.(直到解锁或超时)

行锁

  • 行锁是mysql中InnoDB存储引擎的一种针对行记录进行加锁的一种实现方式,默认所有的select 操作不加锁,对于insert、update、delete语句,InnoDB会自动给涉及的数据加锁,而且是排他锁.行锁中锁的是索引

  • 如果一条sql 语句操作了主键索引,Mysql 就会锁定这条语句命中的主键索引(或称聚簇索引)。

  • 如果一条语句操作了非主键索引(或称辅助索引),MySQL会先锁定该非主键索引,再锁定相关的主键索引。

  • 如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表级锁一样。

  • 即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同 执行计划的代价来决定的,如果 MySQL 认为全表扫 效率更高,比如对一些很小的表,它 就不会使用索引,这种情况下 InnoDB 将锁住所有行,相当于表锁。因此,在分析锁冲突时, 别忘了检查 SQL 的执行计划,以确认是否真正使用了索引。

  • 行锁上的共享锁与排它锁

  • 共享锁(S锁):允许当前事务读取一行,阻止其它事务对相同记录添加排它锁.针对的是select语句,因为其他语句会自动添加排它锁

  • 排它锁(X锁):允许当前事务更新数据,阻止其它事务获取相同数据集的共享锁,排它锁.

  • 添加共享锁和排它锁

  • 共享锁:select * from regions where id=12 lock in share mode

  • 排它锁:select * from regions where id=12 for update

  • 多个并发事务对同一记录进行操作时数据的一致性,可以对这条记录添加排它锁,但是这样可能会降低系统并发性能

  • 行锁优化

  • 尽可能让所有数据检索都通过索引来完成, 从而避免无索引行锁升级为表锁

  • 合理设计索引,尽量缩小锁的范围

  • 尽可能减少检索条件,避免间隙锁

  • 尽量控制事务大小,减少锁定资源量和时间长度

  • 尽可能低级别事务隔离

MVCC

  • MVCC(Multi Version Concurrent Control)多版本并发控制,它可以通过历史版本保证读数据的一致性,但是这样方式相对于添加排它锁,并发性能要好

  • 事务的四个特性

  • 原子性(通过undolog实现-执行回滚)

  • 隔离性(通过锁,MVCC-版本控制)

  • 一致性(通过undolog,redolog,隔离性)

  • 持久性(通过redolog日志实现)

  • MVCC的实现原理主要依赖于记录中的三个隐藏字段,undolog,ReadView来实现的

  • MVCC中的隐藏字段

  • DB_TRX_ID:记录创建这条记录或者最后一次修改该记录的事务id

  • DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undolog实现数据的回滚.

  • DB_ROW_ID:隐藏的主键,如果数据表没有主键,那么innodb会自动生成一个row_id,

MySQL/MariaDB数据库_第13张图片
  • 对于Read Committed和Repeatable Read的隔离级别,都要读取已经提交的事务数据,也就是说如果版本链中的事务没有提交,该版本的记录是不能被读取的,那哪个版本的事务是可以读取的,此时就引入了ReadView.事务执行操作时,会生成当前事务的ReadView,保存当前事务之前活跃的所有事务id。

MySQL/MariaDB数据库_第14张图片
  • m_ids: 截止到当前事务id之前,所有活跃的事务id。

  • min_trx_id: 记录以上活跃事务id中的最小值。

  • max_trx_id: 保存当前事务结束后应分配的下一个id值。

  • creator_trx_id: 保存创建ReadView的当前事务id。

  • 事务隔离(RC,RR)特性的实现

  • 如果db_trx_id与Readview中的creator_trx_id相等,则说明当前事务在访问自己的操作数据,此时可以访问。

  • 如果db_trx_id小于ReadView中的min_trx_id值,表明生成的该版本的事务在当前事务生成readview之前已经提交,所以可以直接读取.

  • 如果被访问版本的db_trx_id大于ReadView中的max_trx_id值,表明该版本的事务在当前事务生成ReadView后才开启的,所以该版本不可以被当前事务访问.

  • 如果访问的版本的db_trx_id属性值在min_trx_id和max_trx_id之间 ,就需要判断一下db_trx_id的值是不是在m_ids列表中,如果在,说明创建 ReadView时,生成的该版本的事务还是活跃的,该版本不可以访问,如果不存在,则说明创建ReadView时,生成该版本的事务已经提交则可以读取.

SQL优化

优秀的SQL编写逻辑

  • 查询时尽量避免实用select *;

  • 1. 这样减少可以数据扫描以及网络开销(很多查询不需要查询所有列)。

  • 2. 要尽量使用覆盖索引(索引中已经包含你需要的数据)、减少回表查询。

  • 尽量避免在where子句中使用or作为查询条件。

  • or可能会使用索引失效,进而执行全表扫描。

  • 全表查询的效率相对基于所引查询的效率会比较低。

  • where 条件中尽量不要出现与null值的比较

  • 容易引发全表查询

  • 避免在where子句中使用!=或者<>操作符

  • 使用like查询条件时应尽量避免前缀使用"%"

  • 避免在查询条件中使用一些内置的SQL函数。

  • 当有多个查询条件、分组条件、排序条件时,尽量使用联合索引(组合索引)

  • 表连接时优先使用内连接(inner join),使用小表驱动大表。

  • 表设计时字段类型能用简单数据类型不用复杂类型。

  • 清空表中数据可优先使用truncate.

  • 插入多条数据时可考虑使用批量插入。

慢SQL查询分析

  • 查看慢查询日志,确定已经执行完的慢查询。

  • 开启慢查询日志(一般默认是关闭状态),set global slow_query_log = on;开启慢查询

  • 设置慢查询阀值(响应速度是多长时间被定为是慢查询),set global long_query_time = 1;设置阀值,阀值设定的标准

  • 线上业务一般建议把 long_query_time 设置为 1 秒,如果某个业务的 MySQL 要求比较高的 QPS,可设置慢查询为 0.1 秒。发现慢查询及时优化或者提醒开发改写。

  • 一般测试环境建议 long_query_time 设置的阀值比生产环境的小,比如生产环境是 1 秒,则测试环境建议配置成 0.5 秒。便于在测试环境及时发现一些效率低的 SQL。

  • 甚至某些重要业务测试环境 long_query_time 可以设置为 0,以便记录所有语句。并留意慢查询日志的输出,上线前的功能测试完成后,分析慢查询日志每类语句的输出,重点关注 Rows_examined(语句执行期间从存储引擎读取的行数),提前优化。

  • 确定慢查询日志路径(日志文件在哪里),show global variables like 'datadir';查询路径

  • 确定慢查询日志的文件名(具体日志文件是哪个),然后对文件内容进行分析。show global variables like 'slow_query_log_file';查询文件名

  • 常用的选项说明

  • Time:慢查询发生的时间

  • User@Host:客户端用户和IP

  • Query_time:查询时间

  • Lock_time:等待表锁的时间

  • Rows_sent:语句返回的行数

  • Rows_examined:语句执行期间从存储引擎读取的行数

  • show processlist 查看正在执行的慢查询。

  • 使用 show processlist 命令判断正在执行的慢查询。

  • 使用 show full processlist 看慢查询语句的全部内容可以。

执行计划(Explain)

  • 当定位到是哪个sql语句执行比较慢的时候,可以通过Explain来分析这个sql语句慢的原因

  • MySQL 提供了一个Explain命令,它可以对select语句进行分析,并输出select执行时的详细信息,开发人员可以基于这些信息进行有针对性的优化

mysql> explain select * from employees where employee_id<100 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 1
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
  • id:select 的序列号,有几个 select 就有几个 id,id 的顺序是按 select 出现的顺序增长的。即:id 越大执行优先级越高,id相同则从上往下执行,id 为NULL最后执行。

  • select_type:

  • SIMPLE : 表示查询语句不包含子查询或 union

  • PRIMARY:表示此查询是最外层的查询

  • UNION:表示此查询是 union 的第二个或后续的查询

  • DEPENDENT UNION:union 中的第二个或后续的查询语句,使用了外面查询结果

  • UNION RESULT:union 的结果

  • SUBQUERY:SELECT 子查询语句

  • DEPENDENT SUBQUERY:SELECT 子查询语句依赖外层查询的结果。

  • DERIVED: from 子句后的相对比较复杂查询

  • type表示查询数据的方式:

  • ALL:表示全表扫描,性能最差。

  • index:表示基于索引的全表扫描,先扫描索引再扫描全表数据。

  • range:表示使用索引范围查询。使用 >、>=、<、<=、in 等等。

  • ref:表示使用非唯一索引进行单值查询。

  • eq_ref:一般情况下出现在多表 join 查询,表示前面表的每一个记录,都只能匹配后面表的一行结果。

  • const:表示使用主键或唯一索引做等值查询,常量查询。

  • NULL:表示不用访问表,速度最快。

  • Extra

  • Using where 表示查询需要通过where条件查询数据(可能没有用到索引,也可能一部分用到了索引)。

  • Using index 表示查询需要通过索引,索引就可以满足所需数据(不需要再回表查询,这里出现了索引覆盖)。

  • Using filesort 表示查询出来的结果需要额外排序,数据量小在内存,大的话在磁盘,因此有 Using filesort 建议优化。

  • Using temprorary 查询使用到了临时表,一般出现于去重、分组等操作(这里一般也需要优化)。

  • Using index condition 表示查询的记录,在索引中没有完全覆盖(可能要基于where或二级索引对应的主键再次查询)。

性能分析工具(Profile)

  • Profile 是MySQL内置的一个性能分析工具,基于Profile可以更好的了解SQL执行过程中的资源情况。例如你的CPU,内存,IO等

  • 确定你数据库是否支持Profile。select @@have_profiling;查看是否支持

  • 确定Profile是否是关闭的,假如是关闭的需要先开启。

  • select @@profiling;查看是否开启,0代表没有

  • set profiling=1;开启

  • 执行SQL语句

  • 查看执行SQL的query id。 show profiles;查看query id

  • 通过query id查看SQL每个状态的耗时时间。 show profile for query 1;

问题处理

  • 数据中带有中文的时候,MySql提示有16进制错误

  • 执行 set names gbk;然后修改某数据库字符集:alter database 数据库名 character set 字符集(utf8/gbk);

MySQL/MariaDB数据库_第15张图片
  • 删除数据库中的重复数据:MariaDB可以在删除的sql语句中嵌入查询同一个表的子sql语句,而MySQL5.7不支持,除非把查询出来的数据放到一个临时表

  • 查询百万数据的分页优化

//查询效率太慢
select *
from employees
limit 10,10
//优化后
select e2.*
from (
         select employee_id
         from employees
         order by employee_id
         limit 10,10) e1 join employees e2
                         on e1.employee_id=e2.employee_id

分库分表

  • 垂直分表:把一张表中的字段分成多个表,

  • 按照高频率访问的字段和低频率访问的字段拆开成两个表

  • 按照数据字数比较多的字段和数据字数比较少的字段分成两个表。因为数据比较大可能会跨页导致需要加载的页比较多

  • 垂直分库:按照业务将表垂直分表后放到不同的数据库,然后数据库可以分到不同的服务器上,从而减轻的一个服务器的压力

  • 水平分表:水平分表就是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中(对数据的拆分,不影响表结构)

  • 水平分库:水平分库就是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。例如按照id的奇数和偶数来拆分等

  • 分库分表使用的算法。例如映射法,基因法,冗余法,noSQL法

JDBC

  • Java DataBase connectivity: Java数据库连接,Sun公司通过JDBC接口定义好了方法名,让各个数据库的厂商根据此方法名写各自的实现类(驱动)

MySQL/MariaDB数据库_第16张图片
  • JDBC连接MySQL/MariaDb数据库

  • 在pom.xml文件中添加引入MySQL驱动(jar包) 的依赖

MySQL/MariaDB数据库_第17张图片
  • 在程序代码中执行数据库链接和SQL语句

MySQL/MariaDB数据库_第18张图片
  • Statement方法

  • execute("sql"); 可以执行任意SQL语句, 推荐执行数据库相关和表相关的SQL

  • int row = executeUpdate("sql"); 此方法执行增删改相关的SQL语句. row代表生效的行数

  • ResultSet rs = executeQuery("sql"); 此方法专门用于执行查询相关的SQL语句, ResultSet是结果集对象, 里面装着查询回来的所有数据

  • PreparedStatement

  • 预编译指的是在执行SQL语句之前,创建PreparedStatement对象时对SQL语句进行编译, 此时会对SQL语句的语法格式进行校验,并且对SQL语句的语义进行锁死操作, 之后用户输入的内容只能以值的形式添加到SQL语句中, 这样的话不管用户输入的内容是什么都不会影响原有SQL语句的逻辑.从而避免了出现SQL注入的问题.

DBCP

  • DataBaseConnectionPool: 数据库连接池

  • 作用: 是将连接重用,避免了频繁开关连接导致的资源浪费,从而提高执行效率

  • 数据库连接池的使用

  • 在pom.xml里面添加数据库连接池的依赖

  • 在程序代码中执行数据库链接池

MySQL/MariaDB数据库_第19张图片

你可能感兴趣的:(sql,mariadb,mysql)