一、MySQL架构介绍
1、环境准备
1、安装MySQL,自己采用的Docker镜像安装MySQL
# 1、拉取镜像
docker pull mysql:5.7
# 2、创建实例并启动
docker run -p 3306:3306 --name mysql \
-v /root/mysql/log:/var/log/mysql \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
2、新的容器当中不存在vim编辑器,需要安装
执行命令:apt-get update
执行命令:apt-get install vim -y
#说明:执行apt-get install vim命令若出现E: Unable to locate package vim,不要忘了执行apt-get update
3、核心配置文件,修改默认字符集
root@8d87e9c530cf:/etc/mysql# pwd
/etc/mysql
root@8d87e9c530cf:/etc/mysql# ls
conf.d my.cnf my.cnf.fallback mysql.cnf mysql.conf.d
#进入容器找到 /etc/mysql/mysql.conf.d 目录,然后对其中mysqld.cnf进行配置
root@8d87e9c530cf:/# cd /etc/mysql/mysql.conf.d/
root@8d87e9c530cf:/etc/mysql/mysql.conf.d# ls
mysqld.cnf
#添加如下 :
[mysqld]
character_set_server=utf8
注意:安装MySQL完毕之后,第一件事就是修改字符集编码。
4、MySQL无法输入中文问题
#退出容器,重新容器,使用 C.UTF-8 编码
docker exec -it 【容器ID/名称】 env LANG=C.UTF-8 bash
至此,进入容器后,登录MySQL,即可输入中文。
5、设置完成后进入MySQL查看其字符集编码
mysql> show variables like \'%char%\';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
进行测试
mysql> create database db02 ;
Query OK, 1 row affected (0.00 sec)
mysql> use db02 ;
Database changed
mysql> create table user(id int not null , name varchar(255));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into user values(1,\'张三\');
Query OK, 1 row affected (0.00 sec)
mysql> select * from user ;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
+----+--------+
1 row in set (0.00 sec)
环境搭建完成!
2、安装位置
我们的MySQL安装后,我们可以通过可视化界面去操作,但是MySQL在硬盘上的哪个位置呢 ?
2.1、Windows环境目录结构
D:\MySQL\mysql-5.7.33
data目录就是存放我们的具体的数据库!
2.2、Linux环境目录结构
#进入容器,查看mysql配置文件位置!
root@8d87e9c530cf:/# whereis mysql
mysql: /usr/bin/mysql /usr/lib/mysql /etc/mysql /usr/share/mysql
#/etc :配置文件,mysql的配置文件会放在这里面
#/var/lib/mysql: mysql数据存放位置
可以看到我们创建的数据库!
Linux环境下MySQL的安装目录
| 路径 | 解释 |
|---|---|
| /var/lib/mysql | MySQL数据库文件存放位置 |
| /usr/share/mysql | 错误消息和字符集文件配置 |
| /usr/bin | 客户端程序和脚本 mysqladmin mysqldump |
| /etc/init.d/mysql | 启停脚本相关 |
3、MySQL配置文件
1、二进制日志log-bin:主从复制。
# my.cnf
# 开启mysql binlog功能
log-bin=mysql-bin
2、错误日志log-error:默认是关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息等。
# my,cnf
# 数据库错误日志文件
log-error = error.log
3、查询日志log:默认关闭,记录查询的sql语句,如果开启会降低MySQL整体的性能,因为记录日志需要消耗系统资源。
# my,cnf
# 慢查询sql日志设置
slow_query_log = 1
slow_query_log_file = slow.log
4、数据文件
-
frm文件:存放表结构
-
myd文件:存放表数据
-
myi文件:存放表索引
# mysql5.7 使用.frm文件来存储表结构
# 使用 .ibd文件来存储表索引和表数据
-rw-r----- 1 mysql mysql 8988 Jun 25 09:31 pms_category.frm
-rw-r----- 1 mysql mysql 245760 Jul 21 10:01 pms_category.ibd
4、MySQL逻辑架构
逻辑架构示意图
-
Connectors:指的是不同语言中与SQL的交互。 -
Connection Pool:管理缓冲用户连接,线程处理等需要缓存的需求。MySQL数据库的连接层。 -
Management Serveices & Utilities:系统管理和控制工具。备份、安全、复制、集群等等。。 -
SQL Interface:接受用户的SQL命令,并且返回用户需要查询的结果。 -
Parser:SQL语句解析器。 -
Optimizer:查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求query,根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果。For Example:select uid,name from user where gender = 1;这个select查询先根据where语句进行选取,而不是先将表全部查询出来以后再进行gender过滤;然后根据uid和name进行属性投影,而不是将属性全部取出以后再进行过滤。最后将这两个查询条件联接起来生成最终查询结果。 -
Caches & Buffers:查询缓存。 -
Pluggable Storage Engines:存储引擎接口。MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎(注意:存储引擎是基于表的,而不是数据库)。 -
File System:数据落地到磁盘上,就是文件的存储。
和其他数据库相比,MySQL有点与众不同,主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其他的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需求选择合适的存储引擎。
逻辑架构分层
-
连接层:最上层是一些客户端和连接服务,包含本地socket通信和大多数基于客户端/服务端工具实现的类似于
tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。 -
服务层:MySQL的核心服务功能层,该层是MySQL的核心,包括查询缓存,解析器,解析树,预处理器,查询优化器。主要进行查询解析、分析、查询缓存、内置函数、存储过程、触发器、视图等,select操作会先检查是否命中查询缓存,命中则直接返回缓存数据,否则解析查询并创建对应的解析树。
-
引擎层:存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
-
存储层:数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
5、MySQL存储引擎
通过上述我们知道MySQL的存储引擎有十余,最常用的还是InnoDB和MyISAM
mysql> show engines; #通过命令查询当前MySQL所支持的存储引擎
show variables like \'default_storage_engine%\'; #查看当前数据库正在使用的存储引擎,以及默认存储引擎
InnoDB和MyISAM对比
| 对比项 | MyISAM | InnoDB |
|---|---|---|
| 外键 | 不支持 | 支持 |
| 事务 | 不支持 | 支持 |
| 行表锁 | 表锁,即使操作一条记录也会锁住整张表,不适合高并发操作 | 行锁,操作时只锁某一行,不对其他行有影响,适合高并发操作 |
| 缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,対内存要求较高,而且内存大小対性能有决定性影响 |
| 表空间 | 小 | 大 |
| 关注点 | 性能 | 事务 |
| 默认安装 | Y | Y |
6、SQL性能下降的原因
-
查询语句写的差
-
索引失效:索引建了,但是没有用上
-
关联 查询太多
join(设计缺陷或者不得已的需求) -
服务器调优以及各个参数的设置(缓冲、线程数等)
7、SQL执行顺序
select # 7
from # 1
join # 3
on # 2
where # 4
group by # 5
having # 6
distinct # 8
order by # 9
limit # 10
SQL执行顺序分析
8、七种JOIN理论
实战七种join
/*AB共有*/
select * from tbl_dept a join tbl_emp b on a.id = b.deptid ;
/*A全部和AB共有*/
select * from tbl_dept a left join tbl_emp b on a.id = b.deptid ;
/*B全部和AB共有*/
select * from tbl_dept a right join tbl_emp b on a.id = b.deptid ;
/*A独占*/
select * from tbl_dept a left join tbl_emp b on a.id = b.deptid where b.id is null;
/*B独占*/
select * from tbl_dept a right join tbl_emp b on a.id = b.deptid where a.id is null;
/*A独占+B独占+AB共有*/
mysql> select * from tbl_dept a right join tbl_emp b on a.id = b.deptid
-> union
-> select * from tbl_dept a left join tbl_emp b on a.id = b.deptid ;
/*A独占+B独占*/
mysql> select * from tbl_dept a left join tbl_emp b on a.id = b.deptid where b.deptid is null
-> union
-> select * from tbl_dept a right join tbl_emp b on a.id = b.deptid where a.id is null ;
注意:其中union会进行去重!
二、索引优化分析
1、索引
1、什么是索引
MySQL官方对索引的定义为:索引(INDEX)是帮助MySQL高效获取数据的数据结果。
从而可以获得索引的本质:索引是排好序的快速查找数据结构
索引的目的在于提高查询效率,可以类比字典的目录。如果要查mysql这个这个单词,我们肯定要先定位到m字母,然后从上往下找y字母,再找剩下的sql。如果没有索引,那么可能需要a---z,这样全字典扫描,如果我想找Java开头的单词呢?如果我想找Oracle开头的单词呢???
重点:索引会影响到MySQL==查找(WHERE的查询条件)和排序(ORDER BY)==两大功能!
除了数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
# Linux下查看磁盘空间命令 df -h
[root@Ringo ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 16G 23G 41% /
devtmpfs 911M 0 911M 0% /dev
tmpfs 920M 0 920M 0% /dev/shm
tmpfs 920M 480K 920M 1% /run
tmpfs 920M 0 920M 0% /sys/fs/cgroup
overlay 40G 16G 23G 41%
我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种数据结构的索引之外,还有哈希索引(Hash Index)等。
2、索引的优势和劣势
优势
- 查找:类似大学图书馆的书目索引,提高数据检索的效率,降低数据库的IO成本
- 排序:通过索引対数据进行排序,降低数据排序的成本,降低了CPU的消耗
劣势
-
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
-
虽然索引大大提高了查询速度,但是同时会降低表的更新速度,例如对表频繁的进行
INSERT、UPDATE和DELETE。因为更新表的时候,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。 -
索引只是提高效率的一个因素,如果MySQL有大数据量的表,就需要花时间研究建立最优秀的索引。
3、MySQL索引分类和创建
索引分类
-InnoDB
- 主键索引:设为主键的字段会自动创建索引(无序手动创建),innodb为聚簇索引
- 单值索引:一个索引只包含单个列,一个表可以有多个单列索引。
- 唯一索引:索引列的值必须唯一,但是允许空值。(但是只能有一个null)
- 复合索引:一个索引包含多个字段。
-MyISAM
- Full text 全文索引(mysql5.7之前版本,只能用于myISAM存储引擎)
建议:一张表建的索引最好不要超过5个!
索引基本语法
1、建表之后创建索引
/* 1、创建索引 [UNIQUE]可以省略*/
/* 如果只写一个字段就是单值索引,写多个字段就是复合索引 */
CREATE [UNIQUE] INDEX indexName ON tabName(columnName(length));
ALTER TABLE tableName ADD [UNIQUE] INDEX indexName ON (columnName(length));
/* 2、删除索引 */
DROP INDEX [indexName] ON tabName;
/* 3、查看索引 */
/* 加上\G就可以以列的形式查看了 不加\G就是以表的形式查看 */
SHOW INDEX FROM tabName \G;
2、使用ALTER命令来为数据表添加索引
/* 1、该语句添加一个主键,这意味着索引值必须是唯一的,并且不能为NULL */
ALTER TABLE tabName ADD PRIMARY KEY(column_list);
/* 2、该语句创建索引的键值必须是唯一的(除了NULL之外,NULL可能会出现多次) */
ALTER TABLE tabName ADD UNIQUE indexName(column_list);
/* 3、该语句创建普通索引,索引值可以出现多次 */
ALTER TABLE tabName ADD INDEX indexName(column_list);
/* 4、该语句指定了索引为FULLTEXT,用于全文检索 */
ALTER TABLE tabName ADD FULLTEXT indexName(column_list);
3、建表的时候添加索引
CREATE TABLE tb01(id INT PRIMARY KEY , NAME VARCHAR(20),UNIQUE(NAME)); #添加唯一索引,主键索引
CREATE TABLE tb01(id INT PRIMARY KEY , NAME VARCHAR(20),KEY(NAME)); #添加单值索引,主键索引
CREATE TABLE tb02(id INT PRIMARY KEY , NAME VARCHAR(20),age INT ,KEY(NAME,age)); #添加复合索引
4、MySQL索引底层原理 *
数据准备
1.思考
---建表
create table t_emp(id int primary key,name varchar(20),age int);
--插入数据
insert into t_emp values(5,\'d\',22);
insert into t_emp values(6,\'d\',22);
insert into t_emp values(7,\'e\',21);
insert into t_emp values(1,\'a\',23);
insert into t_emp values(2,\'b\',26);
insert into t_emp values(3,\'c\',27);
insert into t_emp values(4,\'a\',32);
insert into t_emp values(8,\'f\',53);
insert into t_emp values(9,\'v\',13);
--查询
select * from t_emp;
思考: 为什么没有按找顺序插入,输出会排好序呢?
- 原因是:mysql底层为主键自动创建索引,一定创建索引会进行排序 ,也就是mysql底层真正存储是这样的
为什么要排序呢 ?
- 因为排序之后在查询就相对比较快了 如查询 id=3的我只需要按照顺序找到3就行啦(如果没有排序大海捞针,全靠运气