关于MySQL的一些基础知识回顾(持续更新...)

关于MySQL的基础知识

ps:一些图片来自网络,若有冒犯,请多包涵

1. MySQL基本架构

1.1 MySQL的应用架构

用户<= = = = =>客户端<= = = = = =>服务端
在这里插入图片描述

1.2 MySQL的逻辑架构

在这里插入图片描述

1.3 SQL查询语句的执行逻辑

在这里插入图片描述

1.4 SQL修改语句的执行逻辑

在这里插入图片描述

  1. 获取客户端更新语句的请求,查询引擎缓存池中是否有数据
  2. 没有则从磁盘文件加载,对此行记录加锁
  3. 将更新的旧值写入undolog,便于回滚
  4. 更新buffer pool中的数据(成脏数据)
  5. 将操作结果写入redo log buffer中,buffer中可能存储多个修改事务的结果
  6. 准备提交事务时,将redo log刷到redo log文件(磁盘)中
  7. 执行器生成本次更新的bin log,刷到bin log文件(磁盘)中
  8. 执行器调用引擎的提交事务接口,完成提交(此时,将本次事务的bin log文件名,文件位置写入redo log,并做一个commit标记)
  9. 修改后的数据写入磁盘

1.5 关于查看和配置查询缓存的一些操作

  1. show variables like ‘have_query_cache’:查看当前MySQL是否支持查询缓存
  2. show variables like ‘query_cache_type’:查看当前MySQL是否开启了查询缓存
  3. show variables like ‘query_cache_size’:查看查询缓存占用的大小
  4. query_cache_type=1:配置文件(my.ini)中开启查询缓存
  5. net stop(start) mysql:重启mysql
  6. show varaiables like ‘profiling’:查看profile状态(系统分析)
  7. set profiling=1:开启profile
  8. show profiles:查看当前会话的所有profile信息
  9. show profile for query 查询id:查看具体查询的profile信息

2. MySQL的存储引擎

2.1 查看数据库支持哪些引擎的语句

  1. select version();—— 查看数据库版本
  2. show engines——查看存储引擎

2.2 InnoDB和MyISAM存储引擎

InnoDB优点:支持事务、外键、行锁。这几个特点能满足高并发下的一致性需求

创建表时指定引擎:

CREATE TABLE xxx
(
  region_id int primary key auto_increment,
  region_name VARCHAR(25)
) engine=innodb default character set utf8;

修改表的引擎:

alter table t1 engine=innodb;
2.2.1 对比
InnoDBMyISAM
支持事务,对每条SQL默认封装为事务,自动提交不支持事务
支持外键,基本不用,需要关联表可以用table a = table b on a.id = b.id不支持外键
主索引是聚集索引,B+tree结构,索引和数据绑定主索引是非聚集索引,B+tree结构,索引和数据分离,索引保存数据地址
必须有主键,不指定则默认6字节int类型可以没有主键
主键索引和辅助索引存在层级关系,使用辅助索引查询需要回表(1.获得结果为主键id;2.通过id查询得到数据)主键索引和辅助索引是平级关系
不保存表的具体行数,所以不指定count时,需要全表扫描用变量保存表的行数。(但是不能用where条件)
不支持全文索引(5.7以后也支持)支持全文索引
支持表锁、行锁。默认行锁(实现在索引上的)只支持表锁
存储文件类型有——.frm(存表结构定义描述文件);.ibd(存数据+索引))【8.0.frm也没了】存储文件类型有——.frm(表结构定义描述文件);.MYD(数据内容);.MYI(存索引内容)

2.3 InnoDB为什么推荐使用自增ID作为主键

自增id可以保证每次插入数据B+索引是从右侧扩展的,避免B+数频繁合并分裂。使用别的主键数据插入随机,效率差

2.4 InnoDB四大特性(简单概述

2.4.1 插入缓冲(insert buffer/change buffer)

将多个插入操作合并为一次操作——只对非聚簇索引的插入和更新有效。先判断插入的非聚集索引是否在缓冲池中,不再则先放在insert buffer中,按照一定频率合并。

2.4.2 二次写(double write)

是一个缓存区域。2M。
位置:在buffer pool和数据文件之间。
运作方式:防止系统在写磁盘过程中崩溃。可以从double write区域中找到备份恢复。
特点:块写,IO消耗低

2.4.3 自适应哈希索引(用innodb_adapitve_hash_index开启)

针对被频繁访问的二级索引,建立哈希索引提升速度。使用B+数构造,建立的速度也快
innodb会监控对表上索引页的查询,发现可以使用,则自动建立
使用范围:对页的连续访问必须一样

2.4.4 预读(read ahead)

线性预读:将下一个extent预读到buffer pool中
随机预读:废弃

3. MySQL日志系统

3.1 有哪些日志

  1. 重做日志redo log
  2. 回滚日志undo log
  3. 二进制日志binary log
  4. 错误日志error log
  5. 慢查询日志slow query log

3.2 WAL预写日志

MySQL 的写操作并不是立刻更新到磁盘上,而是先记录在日志上,然后在合适的时间再更新到磁盘上。原子性和持久性

3.3 重做日志

  • 记录数据库数据变化前后的值,不管事务是否提交,都记录。
  • 两部分组成:redo log buffer和redo log file
  • 当数据页从磁盘读到buffer pool中进行修改时,修改后的数据与数据页上的不一样,称之为脏页。
  • 数据还没有刷到磁盘中,服务重启,数据就会丢失
  • redo log会记录数据页发生的改变,防止服务重启导致数据丢失

3.4 重做日志的刷盘机制

  • 用innodb_flush_log_at_trx_commit配置
  • 0:主线程周期刷新
  • 1:事务提交时,日志缓冲区写入磁盘
  • 2:事务提交时,将redo log buffer写入文件系统缓存,由系统内部来fsync磁盘文件

3.5 回滚日志

  • 逻辑记录。delete一条数据,undo log中会insert
  • 执行rollback,可以读取内容进行回滚
  • 记录事务开始前的状态

3.6 回滚日志存在形式

  • 段管理。默认128个rollback segment回滚段。每个回滚段有1024个redo log segment
  • 回滚段分配:
    1. slot 0:预留给系统表空间
    2. slot 1-32:预留给临时表空间,每次重启数据库,重建临时表空间
    3. slot 33-127:有独立表空间,预留给undo独立表空间;没有则预留给系统表空间

3.7 两阶段提交

即redo log和binlog的一致性。事务完成且bin log写入完成,从prepare状态转变为commit 状态;若发生故障,从prepare状态恢复事务前的状态

3.8 日志特性比较

redo logundo logbin log
作用保持事务持久性事务回滚,原子性主从赋值环境的建立
产生主体InnoDBInnoDBMySQL
类型物理日志逻辑日志逻辑日志
内容每个页的修改修改前的数据执行的SQL语句
每个事务的日志数量修改的行数据量修改的行数据量事务提交后的一条SQL语句
写入方式循环写循环写追加写

4. 数据库表设计

4.1 常用数据类型

  • 字符串类型:char varchar text
  • 日期时间类型:date time datetime timestamp
  • 数值类型:tinyint int bigint decimal
  • 二进制类型:blob mediumblob longblob
  • 其他:enum set json

4.2 数据类型应用有哪些原则

  • 简单(能用int不用varchar)
  • 使用最小数据类型(能用tinyint不用int)
  • 要存储小数可以考虑使用decimal类型
  • 避免使用text、blob等大字段类型

4.3 常用字段约束

  • 非空约束(not null):字段的值不允许为空
  • 主键约束(primary key):字段值不允许为空并且唯一
  • 唯一约束(unique key):字段值必须唯一
  • 检查约束(check):字段值需要在指定范围
  • 外键约束(foreign key):字段值需要参考引用表中的字段值
  • 字段默认值(Default Value): 允许为空的字段给一个默认值

4.4 三大范式

三大范式的提出已经过于久远,现如今实际开发中,已经被逐渐淡化。简单了解即可。

  1. 第一范式(1NF):字段不可再分(原子性)。例如姓名可再分为姓和名,这属于可再分。
  2. 第二范式(2NF):首先要满足1NF,保证数据库表中每一列都与主键相关,而不是与主键的某一部分相关。
  3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

4.5 数据库表设计要考虑什么?

  • 命名规范(可读性-见名知意)
  • 合适的数据类型以及注释
  • 主键设计要合理(最好没有业务含义)
  • 表中要有标识逻辑删除的字段(is_deleted)
  • 表中建议有created_time,modified_time字段
  • 表中不建议有太多字段,例如超过50个是不推荐的
  • 尽量使用not null对字段进行标识
  • 评估哪些字段添加什么约束,是否使用索引.
  • 为提高查询效率可以适当添加字段冗余
  • 不推荐做物理外键,可以有逻辑外键(业务上有关系)
  • 表的存储引擎推荐使用InnoDB
  • 大字段建议放在一张独立的表中
  • 分库分表

4.6 SQL语句语法的执行顺序

  1. from
  2. on
  3. join
  4. where
  5. group by
  6. having
  7. select
  8. order by
  9. limit

5. MySQL中的锁和MVCC

5.1 ACID

  1. 原子性:通过undolog实现-执行回滚;
  2. 一致性:通过undolog,redolog,binlog;
  3. 隔离性:通过锁,MVCC-多版本并发控制;
  4. 持久性:通过redolog日志实现;

5.2 隔离性

5.2.1 四种隔离级别
  1. 读未提交
  2. 读已提交
  3. 可重复读
  4. 序列化
5.2.2 可能出现的问题
  1. 脏读:A事务修改数据没有提交,B也读取了数据还进行了操作,A撤销回滚,恢复数据。B事务读到的数据和数据库中不一致。排它锁解决

  2. 不可重复读:A事务多次读取表中某一行数据,B事务在此期间对数据进行修改,导致A的读取结果不同。行级锁、共享锁解决

  3. 虚读:A事务读取到了B事务新增或删除的数据,导致前后两次读取结果不同。表级锁解决

  4. 记忆表格:

    隔离级别脏读不可重复读虚读(幻读)
    读未提交可能可能可能
    读已提交不可能可能可能
    可重复读不可能不可能可能
    串行化(序列化)不可能不可能不肯能

5.3 MySQL的锁分类

  1. 性能:

    乐观锁:认为没有其他事务对该数据进行操作,仅在提交前进行比对,被修改则重新操作

    悲观锁:认为会有其他事务对其进行操作,而直接加锁

  2. 操作类型:共享锁(读锁)、排它锁(写锁)

  3. 粒度:全局所、表锁、行锁、间隙锁

5.4 锁的应用

5.4.1 全局锁的应用
  • 所定所有表,用于逻辑备份
  • fiush tables with read lock 加锁,只能读
  • unlock tables
5.4.2 表锁的应用
  • 针对表。有表读锁、表写锁、元数据锁
  • 表读锁:lock rable XXX read。只读。其他线程阻塞
  • 表写锁:lock table XXX write。可读写。其他线程阻塞
5.4.3 行锁的应用
  • 针对某一行。可以在某一行添加共享锁和排它锁
  • 共享锁:(select * from XX where xxx=xxx lock in share mode)针对同一份数据,多个读操作可以同时进行而不会互相影响。
  • 排它锁:(…for update) 当前写操作没有完成前,它会阻断其他写锁和读锁
5.4.4 死锁

事务A持有资源1,事务B持有资源2,事务A完成业务需要资源2且事务B完成业务需要资源1。此时出现资源竞争,两个事务都阻塞。

5.5 MVCC

5.5.1 用处以及理解

Multi Version Concurrent Control。用来解决事务隔离性的一种方式,针对于读已提交和可重复读。

5.5.2 底层逻辑

undo log日志、ReadView快照读、三个隐藏字段(DB_TRX_ID/DB_ROLL_PTR/DB_ROW_ID)

5.5.2.1 三个隐藏字段
  • DB_ROW_ID隐藏ID:数据表没有主键,也没有非空唯一字段。自动生成
  • DB_TRX_ID当前事务ID:记录创建这条记录或者最后一次修改该记录的事务id
  • DB_ROLL_PTR上版本指针:指向本记录上一个版本,配合undo log实现回滚
5.5.2.2 undo日志版本链

在这里插入图片描述

当前事务,对age数据进行修改并更新,产生一条undolog日志,多个事务同时操作此数据,有多条日志。其中DB_ROLL_PTR(上版本指针)会指向上一版本

5.5.2.3 快照读和当前读
  1. 快照读

    • 也叫普通读。读取记录中的可见版本数据,不加锁,是普通select
    • 小提示——如果隔离级别是串行,会变成当前读
  2. 当前读

    • 也叫锁定读。读取最新版本数据,读之前要获得对应记录的锁。举例:

      select * from xxx where id = 1 lock in share mode;
      select * from xxx where id = 1 for update;
      
5.5.2.4 ReadView
  • 理解:一个视图。记录的是事务数据。这些事务数据是当前事务能读取的

  • 组成:

    1. create_trx_id: 保存创建ReadView的当前事务id。

    2. m_ids: 截止到当前事务id之前,所有活跃的事务id(还没有commit的事务)

    3. min_trx_id: 记录活跃事务id中(m_ids)的最小值

    4. max_trx_id: 记录当前事务结束后应分配的下一个事务id值

5.5.2.5 重复读底层原理

启动事务时生成一个ReadView,事务期间使用ReadView保证读到的数据都是事务启动前的记录。

举例说明:

5.5.2.6 读已提交底层原理

A事务每次select都会生成一个新的ReadView,那么事务期间,B事务多次读取同一数据,前后两此的结果可能不同。

6. MySQL中的索引

6.1 啥是索引?为啥要用?有啥优缺点?

  • 索引是一种数据结构。表就像是书,索引就像是目录
  • 方便查找数据(查字典要翻目录)
  • 缺点是占用额外空间(目录要占好几页),更新数据时要更新索引(内容更新,目录要变动)

6.2 索引的分类

  • 逻辑应用角度:主键、普通、唯一、联合、空间索引等
  • 物理存储角度:聚集、非聚集索引
  • 数据结构角度:hash索引、B+数索引

查看索引的语句是:

show index from 表名

6.3 添加、删除索引的sql语句

添加索引:

普通索引:
	创建表时添加:create table 表名 (.... , index 索引名 字段名)
	创建表后添加:create index 索引名 on 表名(字段名)
	alter语句添加:alter table 表名 add index 索引名(字段名)
唯一索引:
	create unique index 索引名 on 表名(字段名)
	alter table 表名 add unique index 索引名(字段名)
创建联合索引:
	create index 索引名 on (字段名,字段名,...)
	alter table 表名 add 索引名(字段名,字段名,...)

删除索引:

drop index 索引名 on 表名
drop index index_first_last on student;

6.4 B-树

  • 特点:每个非叶子节点允许有多个分叉,每个非叶子节点存储指针(子节点地址)、索引、数据。叶子节点存索引和数据

  • 劣势:

    非叶子节点存数据,则存的索引就少了,分叉就少了,树就高了,查询效率就低了

    叶子节点不支持范围查询,查询需要从根节点开始

6.5 B+树

  • 特点:非叶子节点存索引和指针,叶子节点存索引和数据。
  • 优势:叶子节点有序,使用双向链表连接,支持范围查询

6.6 聚集索引和非聚集索引

  • 聚集索引:索引和数据不分离
  • 非聚集索引:索引和数据分开存储。查找时,先找到索引,获得索引下的数据地址,根据地址找到数据。

7. SQL调优

7.1 调优的一些原则

  1. 减少表数据量——分表
  2. 减少数据访问量——建立索引
  3. 减少数据计算——数据库中的计算拿到程序中

7.2 优化的基本逻辑

  • 规范编写语句
  • 语句逻辑正确——小表驱动大表
  • 定位慢SQL——耗时长的语句进行优化
  • 优化策略——sql结构调整,索引应用
  • 结合业务分库分表——减少单表数据量

7.3 常见案例

  1. 查询避免使用select *
  2. where语句中不适用or
  3. where语句中不出现与null比较
  4. 避免查询中存在隐式转换
  5. 避免where中使用 != 或<>
  6. 避免like后前缀使用“%”
  7. 查询时采用最左匹配
  8. 避免查询中使用SQL函数
  9. 避免in后面的数据太多,避免使用in
  10. 多个查询条件、分组条件、排序条件时,使用联合索引
  11. 表连接优先用inner join ,小表驱动大表
  12. 表关联的字段使用相同编码
  13. 批量插入代替循环插入
  14. 清空数据使用truncate
  15. limit限制返回条数,list 、query接口必须分页。有效避免内存溢出
  16. 同步数据,使用update_at检索出增量数据。例如ES索引构建的定时任务,只会去跑最近时间往前2分钟的数据
  17. 分页查询使用limit,当pageNum增大时,影响效率。此时可以借助上次查询的最大Id,也可以是Orderby的最大值最小值做条件的过滤。
  18. 缩小数据集的条件可以放在前面

7.4 慢SQL的一些操作演示

1.查看慢查询日志是否打开:
show variables like '%slow_query_log%';
2.开启慢查询日志:
set global slow_query_log = ON;
3.查看默认的慢查询阈值(默认10秒):
show variables like '%long_query_time';
4.设置慢查询时间:
set slow_query_time = 1;
5.慢查询日志的路径:
show global variables like 'datadir';
6.慢查询日志文件名:
show global variables like 'slow_query_log_file';

7.5 分析慢SQL——Explain执行计划

  • 语句前加explain即可
  • 可以查看关联查询的执行顺序
  • 查看查询操作的具体类型
  • 哪些索引会命中
  • 表中有几条记录参与
7.5.1 常见字段
  1. id

    • id相同:执行顺序由上至下
      这里写图片描述

    • id不同:如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
      img

    • id相同又不同(两种情况同时存在):id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行
      img

  2. select_type:查询的类型,主要是用于区分普通查询、联合查询、子查询等复杂的查询

    • SIMPLE:简单的select查询,查询中不包含子查询或者union
    • PRIMARY:查询中包含任何复杂的子部分,最外层查询则被标记为primary
    • SUBQUERY:在select 或 where列表中包含了子查询
    • DERIVED:在from列表中包含的子查询被标记为derived(衍生),mysql或递归执行这些子查询,把结果放在零时表里
    • UNION:若第二个select出现在union之后,则被标记为union;若union包含在from子句的子查询中,外层select将被标记为derived
    • UNION RESULT:从union表获取结果的select
  3. type:访问类型。最少是range,最好能是ref

    • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,可以忽略不计

    • const:表示通过索引一次就找到了,const用于比较primary key 或者 unique索引。因为只需匹配一行数据,所有很快。如果将主键置于where列表中,mysql就能将该查询转换为一个const
      这里写图片描述

    • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键 或 唯一索引扫描

      注意:ALL全表扫描的表记录最少的表如t1表

    • ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质是也是一种索引访问,它返回所有匹配某个单独值的行,然而他可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体

    • range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了那个索引。一般就是在where语句中出现了bettween、<、>、in等的查询。这种索引列上的范围扫描比全索引扫描要好。只需要开始于某个点,结束于另一个点,不用扫描全部索引
      img

    • index:index与ALL区别为index类型只遍历索引树。index是从索引中读取,而ALL是从硬盘读取

    • ALL:遍历全表以找到匹配的行

  4. possible_keys:查询涉及到的字段上存在索引,则该索引将被列出,但不一定被查询实际使用

  5. key:实际使用的索引,如果为NULL,则没有使用索引。

  6. key_len:查询中使用的索引的长度

  7. ref:索引的那一列被使用了

  8. rows:找到所需的记录所需要读取的行数

  9. Extra:额外信息

    1. Using filesort :mysql无法利用索引完成的排序操作
    2. Using temporary:mysql在对查询结果排序时使用了临时表,常见于order by 和 group by
    3. Using index:select操作中使用了覆盖索引,出现Using where,说明索引用来查找,没有则用来读取
    4. Using where
    5. Using join buffer:使用了链接缓存
    6. Impossible WHERE:where子句的值总是false

7.6 分析慢SQL——Profile工具

Profile的演示:
1.检查是否支持
select @@have_profiling
2.检查是否关闭
select @@profiling
3.开启和查看
set progiling = 1
4.执行一个sql语句
select * from employees where salary > 5000;
5.查看sql的query id
show profiles
6.查看具体sql的执行情况
show profile for query 1;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值