MySQL
# MySQL 概述
数据库概念:
数据库:存储数据的仓库,数据是有组织的进行存储,英文:DataBase (DB)
数据库管理系统:操纵和管理数据库的大型软件,英文:DataBase Management System (DBMS)
SQL:操作关系型数据库的编程语言,定义了一套操作关系型数据库的统一标准,英文:Structured Query Language (SQL)
启动与停止:
1 | net start mysql80 #启动 |
关系型数据库 (RDBMS):
概念:建立在关系模型基础上,由多张相互链接的二维表组成的数据库。(类似于 excel 表格)
特点:
1. 使用表存储数据,格式统一,便于维护
2. 使用 SQL 语言操作,标准统一,使用方便
# SQL 语言
使用下面指令登录用户:
1 | mysql -u userName -p |
# 基础内容
SQL 语句可以单行或多行书写,以分号结尾。
MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
注释:单行使用–或 #(MySQL 特有),多行使用 /**/
# SQL 分类
分类 | 全称 | 说明 |
---|---|---|
DDL | Data Definition Language | 数据定义语言,用来定义数据库对象 (数据库,表,字段) |
DML | Data Manipulation Language | 数据操作语言,用来对数据库表种的数据进行增删改 |
DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
DCL | Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
# DDL (数据定义语言)
# DDL - 数据库操作
1 | #[]中是可写,可不写的内容 |
# 数据类型
数值类型:
类型 | 大小 | 有符号范围 | 无符号范围 | 描述 |
---|---|---|---|---|
TINYINT | 1byte | -2^7,2^7-1 | 0,2^8-1 | 微整型 |
SMALLINT | 2bytes | -2^15,2^15-1 | 0,2^16-1 | 小整型 |
MEDIUMINT | 3bytes | -2^23,2^23-1 | 0,2^24-1 | 中整型 |
INT 或 INTEGER | 4bytes | -2^31,2^31-1 | 0,2^32-1 | 整型 |
BIGINT | 8bytes | -2^63,2^63-1 | 0,2^64-1 | 长整型 |
FLOAT | 4bytes | -3.402823466 E+38,3.402823466351 E+38 | 0 和 1.175494351 E-38, 3.402823466 E+38 | 单精度浮点型 |
DOUBLE | 8bytes | -1.7976931348623157 E+308, 1.7976931348623157 E+308 | 0 和 2.2250738585072014 E-308, 1.7976931348623157 E+308 | 双精度浮点型 |
DECIMAL | 依赖于 M (精度) 和 D (标度) 的值 | 依赖于 M (精度) 和 D (标度) 的值 | 小数值 (精确定点数) |
M:指数值总位数,D:指小数位数
字符串类型:
类型 | 大小 | 描述 |
---|---|---|
CHAR | 0-255 bytes | 定长字符串 |
VARCHAR | 0-65535 bytes | 变长字符串 |
TINYBLOB | 0-255 bytes | 不超过 255 个字符的二进制数据 |
TINYTEXT | 0-255 bytes | 短文本字符串 |
BLOB | 0-65535 bytes | 二进制形势的长文本数据 |
TEXT | 0-65535 bytes | 长文本数据 |
MEDIUMBLOB | 0-2^24 - 1 bytes | 二进制形势的中等长度文本数据 |
MEDIUMTEXT | 0-2^24 - 1 bytes | 中等长度文本数据 |
LONGBLOB | 0-2^32 - 1 bytes | 二进制形势的极大文本数据 |
LONGTEXT | 0-2^32 - 1 bytes | 极大文本数据 |
日期类型:
类型 | 大小 | 范围 | 格式 | 描述 |
---|---|---|---|---|
DATE | 3 | 1000-01-01 至 9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | -838:59:59 至 838:59:59 | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901 至 2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:01 至 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
# DDL - 表查询
1 | #查询当前数据库所有表 |
# DDL - 表创建
1 | create table 表名( |
# DDL - 表修改
1 | #添加字段 |
# DML (数据操作语言)
DML (Data Manipulation Language),数据操作语言,用来对数据库中表的数据记录进行增删改操作
添加数据 (INSERT)
修改数据 (UPDATE)
删除数据 (DELETE)
# DML - 添加数据
1 | #给指定字段添加数据 |
# DML - 修改数据
1 | UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ...[WHERE 条件] |
# DML - 删除数据
1 | DELETE FROM 表名 [WHERE 条件] |
# DQL (数据查询语言)
DQL (Data Query Language) 数据查询语言,用来查询数据库中表的记录。
查询关键字 (SELECT)
1 | #基础查询 |
# DQL - 基础查询
1 | #查询多个字段 |
# DQL - 条件查询
1 | #语法 |
# DQL - 聚合函数
聚合函数:将一列数据作为一个整体,进行纵向计算
常见聚合函数:
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
1 | #注意:NULL不参与聚合函数的计算 |
# DQL - 分组查询
1 | #语法 |
# DQL - 排序查询
1 | #语法 |
# DQL - 分页查询
1 | #语法 |
# DCL (数据控制语言)
DCL (Data Control Language) 数据控制语言,用来管理数据库用户、控制数据库的访问权限
# DCL - 管理用户
1 | #查询用户 |
# DCL - 权限控制
权限 | 说明 |
---|---|
ALL, ALL PRIVILEGES | 所有权限 |
SELECT | 查询数据 |
INSERT | 插入数据 |
UPDATE | 修改数据 |
DELETE | 删除数据 |
ALTER | 修改表 |
DROP | 删除数据库 / 表 / 视图 |
CREATE | 创建数据库 / 表 |
其他权限描述及含义,可以直接参考官方文档
1 | #查询权限 |
# MySQL 内置函数
# 字符串函数
MySQL 中内置了许多字符串函数,常用的几个如下:
函数 | 作用 |
---|---|
CONCAT(S1, S2, …, Sn) | 字符串拼接,将 S1,S2,…,Sn 拼接成一个字符串 |
LOWER(str) | 将字符串 str 全部转为小写 |
UPPER(str) | 将字符串 str 全部转为大写 |
LPAD(str, n, pad) | 左填充,用字符串 pad 对 str 的左边进行填充,直到字符串长度为 n |
RPAD(str, n, pad) | 右填充,用字符串 pad 对 str 的右边进行填充,直到字符串长度为 n |
TRIM(str) | 去掉字符串头部和尾部的空格 |
SUBSTRING(str, start, len) | 返回字符串 str 从 start 位置开始的 len 个长度的字符串 (索引从 1 开始) |
# 数值函数
MySQL 中几个常用的数值函数:
函数 | 作用 |
---|---|
CEIL(x) | 向上取整 |
FLOOR(x) | 向下取整 |
MOD(x, y) | 返回 x/y 的模 |
RAND() | 返回 0~1 内的随机数 |
ROUND(x, y) | 求参数 x 的四舍五入的值,保留 y 位小数 |
# 日期函数
MySQL 中几个常用的日期函数:
函数 | 作用 |
---|---|
CURDATE() | 返回当前日期 |
CURTIME() | 返回当前时间 |
NOW() | 返回当前日期和时间 |
YEAR(date) | 获取指定 date 的年份 |
MONTH(date) | 获取指定 date 的月份 |
DAY(date) | 获取指定 date 的日期 |
DATE_ADD(data, INTERVAL expr type) | 返回一个日期 / 时间值加上一个时间间隔 expr 后的时间值 |
DATEDIFF(date1, date2) | 返回起始时间 date1 和结束时间 date2 之间的天数 |
# 流程函数
MySQL 中几个常用的流程函数:
函数 | 作用 |
---|---|
IF(value, t, f) | 如果 value 为 true,则返回 t,否则返回 f |
IFNULL(value1, value2) | 如果 value1 不为空,返回 value1,否则返回 value2 |
CASE WHEN [val1] THEN [res1] … ELSE [default] END | 如果 val1 为 true,返回 res1,… 否则返回 default 默认值 |
CASE [expr] WHEN [val1] THEN [res1] … ELSE [default] END | 如果 expr 的值等于 val1,返回 res1,… 否则返回 default 默认值 |
# 约束
- 概念:约束时作用域表中字段上的规则,用于限制存储在表中的数据
- 目的:保证数据库中数据的正确性、有效性和完整性
- 分类:
约束 | 描述 | 关键字 |
---|---|---|
非空约束 | 限制该字段的数据不能为 null | NOT NULL |
唯一约束 | 保证该字段的所有数据都是唯一、不重复的 | UNIQUE |
主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | PRIMARY KEY |
默认约束 | 保存数据时,如果未指定该字段的值,则采用默认值 | DEFAULT |
检查约束 | 保证字段值满足某一个条件 | CHECK |
外键约束 | 用来让两张表的数据之间建立连接,保证数据的一致性和完整性 | FOREIGN KEY |
使用案例:
根据需求,完成如下表结构的创建
字段名 | 字段含义 | 字段类型 | 约束条件 |
---|---|---|---|
id | ID 唯一标识 | int | 主键,并且自动增长 |
name | 姓名 | varchar(10) | 不为空,并且唯一 |
age | 年龄 | int | 大于 0,并且小于等于 120 |
status | 状态 | char(1) | 如果没有指定该值,则默认为 1 |
gender | 性别 | char(1) | 无 |
1 | create table user( |
# 外键约束
使用外键关联其他表时,具有外键的表被称为子表,被关联的表被称为父表。也称从表与主表。
语法:
1 | #1.创建表时直接添加外键 |
删除 / 更新行为:
行为 | 说明 |
---|---|
NO ACTION | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除 / 更新。(与 RESTRICT 一致) |
RESTRICT | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除 / 更新。(与 NO ACTION 一致) |
CASCADE | 当在父表中删除 / 更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除 / 更新外键在子表中的记录。 |
SET NULL | 当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键的值为 null。(这就要求该外键允许取 null) |
SET DEFAULT | 父表有变更时,子表将外键列设置成一个默认的值 (innodb 不支持) |
1 | #在创建外键时,指定外键的行为 |
# 多表查询
多表关系:
一对多,多对多,一对一
一对多实现:在多的一方建立外键,指向一的一方的主键
多对多实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
一对一实现:在任意一方加入外键,关联另外一方的主键,并设置外键为唯一的 (UNIQUE)
# 多表查询概述
# 语法
1 | select 字段名/通配符 from 表一, 表二 where 条件; |
# 多表查询分类
连接查询:
-
内连接:查询 A 表与 B 表的交集部分
-
外连接:分为左外连接与右外连接
- 左外连接:查询左表所有数据,以及两张表交集部分数据
- 右外连接:查询右表所有数据,以及两张表交集部分数据
- 自连接:当前表与自身的连接查询,自连接必须使用表别名
子查询
# 连接查询
# 内连接
内连接查询语法:
1 | #隐式内连接 |
# 外连接
左外连接语法:
1 | select 字段列表 from 表一 left [outer] join 表二 on 条件 ...; |
右外连接语法:
1 | select 字段列表 from 表二 right [outer] join 表二 on 条件 ...; |
# 自连接
自连接查询语法:
1 | select 字段列表 from 表A 别名A join 表A 别名B on 条件 ...; |
# 联合查询
关键字:union、union all
联合查询:把多次查询的结果合并起来,形成一个新的查询结果集。
语法:
1 | select 字段列表 表A ... |
# 子查询 (嵌套查询)
语法:
1 | select * from t1 where column1 = (select column1 from t2); |
其中括号内部分被称为子查询,根据子查询返回结果的不同,可以分为:
- 标量子查询 (子查询结果为单个值)
- 列子查询 (子查询结果为一列)
- 行子查询 (子查询结果为一行)
- 表子查询 (子查询结果为多行多列)
根据子查询的位置,可以分为:where 之后、from 之后、select 之后
列子查询常用操作符:
操作符 | 描述 |
---|---|
IN | 在指定的集合范围之内,多选一 |
NOT IN | 不在指定的集合范围之内 |
ANY | 子查询返回列表中,有任意一个满足即可 |
SOME | 与 ANY 等同,使用 SOME 的地方都可以使用 ANY |
ALL | 子查询返回列表的所有值都必须满足 |
# 事务
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
对于 mysql 来说,默认每一条一句都是一个事务,执行后都会默认提交。
# 事务控制
1 | select @@autocommit;#检查自动提交的状态,1为开启,0为关闭 |
设置自动提交的状态为 0 后,每次都需要手动提交,未免过于麻烦,所以还有其他方式使用事务
1 | #开启事务 |
事务控制用于操作一些必须同时完成的指令,比如银行转账时的 A 账户扣款与 B 账户加款。
# 事务的四大特性
原子性 (Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性 (Consistency):事务完成时,必须使所有的数据都保持一直状态。
隔离性 (Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性 (Durability):事务一旦提交或回滚,它对数据库中的数据的改变是永久的。
被缩写为:ACID
# 并发事务问题
常见并发事务问题:
问题 | 描述 |
---|---|
脏读 | 一个事务读到另外一个事务还没有提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同,称为不可重复读 |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但插入时,又发现这行数据已经存在 |
解决方式:事务的隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | X | √ | √ |
Repeatable Read (MySQL 默认) | X | X | √ |
Serializable | X | X | X |
从上到下隔离级别逐渐提高,但运行效率逐渐下降。
1 | #查看事务隔离级别 |
# ------ 分割线 ------(基础内容到此结束)
# 存储引擎
存储引擎就是存储数据、建立索引、更新 / 查询数据等技术的实现方式。
存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。
MySQL 默认的存储引擎是 InnoDB。
创建表时,可在括号后面写上 ENGINE = INNODB 来指定存储引擎,例如:
1 | create table table_name( |
# 存储引擎特点
# InnoDB
InnoDB 是一种兼顾高可靠性和高性能的通过用存储引擎,在 MySQL5.5 之后,InnoDB 是默认的 MySQL 存储引擎。
特点:
- DML 操作遵循 ACID 模型,支持事务
- 行级锁,提高并发访问性能
- 支持外键 FOREIGN KEY 约束,保证数据的完整性和正确性
文件:
xxx.ibd:xxx 是表名,innoDB 引擎的每张表都会对应这样一个表空间文件,存储该表的表结构 (frm、sdi)、数据和索引。参数:innodb_file_per_table
InnoDB 中一个页 / 块,大小为 16kb
# MyISAM
MyISAM 是 MySQL 早期的默认存储引擎。
特点:
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
文件:
- xxx.sdi:存储表结构信息
- xxx.MYD:存储数据
- xxx.MYI:存储索引
# Memory
Memory 引擎的表数据是存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或者缓存使用
特点:
- 内存释放
- hash 索引 (默认)
文件:
xxx.sdi:存储表结构信息
# 特点对比
特点 | InnoDB | MyISAM | Memory |
---|---|---|---|
存储限制 | 64TB | 有 | 有 |
事务安全 | 支持 | - | - |
锁机制 | 行锁 | 表锁 | 表锁 |
B+tree 索引 | 支持 | 支持 | 支持 |
Hash 索引 | - | - | 支持 |
全文索引 | 支持 (5.6 版本后) | 支持 | - |
空间使用 | 高 | 低 | N/A |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
支持外键 | 支持 | - | - |
# 存储引擎选则
InnoDB:MySQL 的默认存储引擎。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么 InnoDB 引擎是比较合适的选择。
MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个引擎是非常合适的。
Memory:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。Memory 的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
# 索引
# 概述
索引 (index) 是帮助 MySQL 高效获取数据的数据结构 (有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用 (指向) 数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优点:
- 提高数据检索的效率,降低数据库的 IO 成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低 CPU 的消耗
缺点:
- 索引列也需要占用空间
- 索引大大的提高了查询效率,但同时也降低了增删改 (INSERT、DELETE、UPDATE) 的效率
# 索引结构
MySQL 的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
索引结构 | 描述 | ||
---|---|---|---|
B+Tree 索引 | 最常见的索引类型,大部分引擎都支持 | ||
Hash 索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 | ||
R-tree (空间索引) | 空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 | ||
Full-text (全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于 Lucene,Solr,ES | ||
索引 | InnoDB | MyISAM | Memory |
:--------: | :-------------: | :----: | :----: |
B+tree 索引 | 支持 | 支持 | 支持 |
Hash 索引 | 不支持 | 不支持 | 支持 |
R-tree 索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6 版本之后支持 | 支持 | 不支持 |
B 树:
对于 B 树,假设要存储下面一组数据:
234、345、23、899、1200、1234、1500、1000、123、245、12、1567、1800、1980、2000、1888、2456
若树的度为 5,则一个节点可以存储 4 份数据、5 个指针。
存储结果如下:
当一个节点的数据达到 5 时,会将其从中间数据分裂,然后向上存储,且存储时数据分布会排序好。
B + 树:
对于 B + 树,向上裂变的同时,叶子节点中仍保存数据,并在叶子节点处建立指向其下一个兄弟节点的索引,以形成一条链表,便于区域查找。
假设要插入下面一组数据:
1000、567、234、232、890、1234、2345
若树的度为 5,则存储结果如下:
B + 树的所有数据都会出现在叶子节点,非叶子节点仅仅起到索引数据的作用。
MySQL 索引数据结构对经典的 B + 树进行了优化,将原 B + 树叶子节点的单链表结构修改为了双向循环链表,示意图如下:
这样的修改提高了区间访问的性能。
# 索引分类
在 InnoDB 存储引擎中,根据索引的存储形式,可以分为以下两种:
分类 | 含义 | 特点 |
---|---|---|
聚集索引 (Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引 (Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
剧集索引选取规则:
- 如果存在主键,那么主键索引就是聚集索引。
- 如果不存在主键,将使用第一个唯一 (UNIQUE) 索引作为聚集索引。
- 如果表没有主键,或没有合适的唯一索引,则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引。
回表查询:先通过二级索引进行查询,查询到对应的主键,然后再通过聚集索引查询到行数据。
# 索引语法
1 | #创建索引: |
创建索引时,通常使用如下命名方式:
idx_tableName_column
其中 idx 为 index 缩写,tableName 为表名,column 为索引字段。
# SQL 性能分析
# SQL 执行频率
MySQL 客户端连接成功后,通过
1 | show [session|global] status |
命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的增、删、改、查的访问频次:
1 | SHOW GLOBAL STATUS LIKE 'Com_______';#共7个下划线 |
通过该指令,可以知道该数据库使用哪些指令最频繁,可以对其进行优化。
# 慢查询日志
慢查询日志记录了所有执行时间超过指定参数 (long_query_time, 单位:秒,默认 10 秒) 的所有 SQL 语句的日志
MySQL 的慢查询日志默认没有开启,需要在 MySQL 的配置文件 (/etc/my.cnf) 中配置如下信息:
1 | #开启MySQL慢查询日志开关: |
配置完成之后,通过指令重启 MySQL 服务器进行测试,查看慢日志文件中记录的信息,
路径:/var/lib/mysql/ 本机名 - slow.log
也可以通过指令开启:
1 | #开启MySQL慢查询日志开关 |
在数据库中可以使用指令查看慢查询日志是否开启:
1 | SHOW VARIABLES LIKE 'slow_query_log'; |
# profile 详情
show profile 能够在做 SQL 优化时帮助了解时间消耗在哪里。
通过 have_profiling 参数,能够看到当前 M 有 SQL 是否支持 profile 操作:
1 | #查看是否支持profile操作 |
# explain 执行计划
EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序
语法:
1 | #直接在select语句之前加上关键字explain/desc |
查询结果:
其中各个字段所表示的含义:
id:select 查询的序列号,表示查询中执行 select 子句或者是操作表的顺序 (id 相同,执行顺序从上到下;id 不同,值越大,越先执行)
select_type:表示 select 的类型,常见的取值有 SIMPLE (简单表,即不适用表连接或者子查询)、PRIMARY (主查询,即外层的查询)、UNION (UNION 中的第二个或者后面的查询语句)、SUBQUERY (SELECT/WHERE 之后包含了子查询) 等
type:表示连接类型,性能由好到差的连接类型为:NULL、system、const、eq_ref、ref、range、index、all
possible_key:显示可能应用到这张表上的索引,一个或多个
key:实际使用的索引,如果为 NULL,则没有使用索引
Key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
rows:MySQL 认为必须要执行查询的行数,在 innoDB 引擎中,是一个估计值,可能并不总是准确的
filtered:表示返回结果的行数占需读取行数的百分比,filtered 的值越大越好
# 索引使用
- 最左前缀法则:
如果索引了多列 (联合索引),要遵守最左前缀法则。即查询从索引的最左列开始,并且不跳过索引中的列。
如果跳跃某一列,索引将部分失效 (后面的字段索引失效)。
- 范围查询:
联合索引中,出现范围查询 (>, <),范围查询右侧的列索引失效。
如果业务允许,使用范围查询时尽量使用 (>=, <=),这样不会使范围查询右侧的列索引失效。
- 索引列运算:
如果在索引列上进行运算操作,索引将失效。
- 字符串类型:
字符串类型字段使用时,不加引号,索引将失效。
- 模糊查询:
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
- or 连接的条件:
用 or 分割开的条件,如果仅其中一个有索引,则索引会失效,必须在 or 连接的所有条件都有索引时,才会使用索引。
- 数据分布影响:
如果 MySQL 评估使用索引比全表更慢,则不使用索引。
- SQL 提示:
SQL 提示是优化数据库的一个重要手段,简单来说,就是在 SQL 语句中加入一些人为的提示来达到优化操作的目的。
- use index (index_name)# 建议使用指定索引 (MySQL 会自己判断是否使用)
- ignore index (index_name)# 忽略指定索引
- force index (index_name)# 强制使用指定索引
将其写在表名后面
- 覆盖索引:
尽量使用覆盖索引 (查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少 select *
- 前缀索引:
当字段类型为字符串 (varchar、text 等) 时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘 IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法:
1 | create index index_name on table_name(column(n));#n表示前缀长度 |
前缀长度:
可以根据索引的选择性来决定,而选择性是指不重复的索引值 (基数) 和数据表的记录总数的比值,索引选择性越高则查询效率越高。
唯一索引的选择性是 1,这是最好的索引选择性,性能也是最好的。
1 | select count(distinct column) from table_name;#统计字段去重后的数量 |
- 单列索引与联合索引
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引
# 索引设计原则
- 针对于数据量较大,且查询比较频繁的表建立索引。
- 针对于常作为查询条件 (where) 、排序 (orderby)、分组 (group by) 操作的字段建立索引。
- 尽量选择区分度高的列作为索引, 尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引, 减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
- 如果 索引列不能存储 NULL 值,请在创建表时使用 NOT NULL 约束它。当优化器知道每列是否包含 NULL 值时,它可以更好地确定哪个索引最有效地用于查询。
# SQL 优化
# insert 优化
- 推荐使用批量插入
- 手动提交事务,避免因 mysql 自动提交造成的多次提交
- 主键顺序插入
# 大批量插入数据 (load)
- 如果一次性需要插入大批量数据,使用 insert 语句插入性能较低,此时可以使用 MySQL 数据库提供的 load 指令进行插入。
1 | #客户端连接服务端时,加上参数 --local-infile |
# 主键优化
- 数据组织方式
- 页分裂
页可以为空,也可以填充一半,也可以填充 100%。
每个页包含了 2-N 行数据 (如果一行数据过大,会行溢出),根据主键排列。
主键顺序插入示意图:
主键乱序插入导致页分裂示意图:
- 页合并
- 主键设计原则:
满足业务需求的情况下,尽量降低主键的长度。
插入数据时,尽量选择顺序插入,选择使用 AUTO_INCREMENT 自增主键。
尽量不要使用 UUID 作为主键或者其他自然主键,如身份证号 (因为它们不具备顺序性、且长度较长)。
业务操作时,避免对主键的修改。
# order by 优化
- Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区 sort buffer 中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
- Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
- 尽量使用覆盖索引
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则 (ASC/DESC)
- 如果不可避免的出现 filesort,大量数据排序时,可以适当增大排序缓冲区大小 sort_buffer_size (默认 256k)
# group by 优化
与 order by 类似
# limit 优化
# count 优化
count 的几种用法
-
count () 是一个聚合函数,对于返回的结果集,一行行的判断,如果 count 函数的参数不是 null,累计值就加 1,否则不加,最后返回累计值。
-
用法:count (*)、count (主键)、count (字段)、count (1)
-
按效率排序:count (字段)<count (主键)<count (1) 约等于 count (*)
# update 优化
InnoDB 拥有行锁,通过事务对表进行更新时,可以仅锁定行。
但只有通过索引的修改才会使用行锁,如果未建立相应索引,则会导致表锁,从而降低并发性能。
因此尽量使用有索引的字段进行 update。
# 视图
# 基础
介绍:视图 (View) 是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的 SQL 逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条 SQL 查询语句上。
语法:
1 | #创建 |
# 视图检查选项
当使用 WITH CHECK OPTION 子句创建视图时,MySQL 会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定义。
MySQL 允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。
为了确定检查的范围,MySQL 提供了两个选项:CASCADED 和 LOCAL,默认值为 CASCADED。
CASCADED 会向上添加 CASCADED 进行检查。
LOCAL 仅会在该视图进行检查,并不会向上添加。
# 更新及作用
视图的更新:
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。
如果视图包含以下任何一项,即该视图不可更新:
- 聚合函数或窗口函数 (SUM ()、MIN ()、MAX ()、COUNT () 等)
- DISTINCT
- GROUP BY
- HAVING
- UNION 或者 UNION ALL
- 其他导致视图行与基础表行不能一对一的操作
作用:
- 简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件。
- 安全:数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据。
- 数据独立:视图可以帮助用户屏蔽真实表结构变化带来的影响。
# 存储过程 (类似于函数)
- 介绍
存储过程是事先经过编译并存储再数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
- 特点
封装、复用
可以接收参数,也可以返回数据
减少网络交互,效率提升
# 语法
1 | #创建 |
在命令行中创建存储过程时,因为识别分号即语句结束,而 SQL 语句中也包含分号。
因此可能会导致代码产生错误,即不能读取到 END; 部分便结束了。
需要重新指定语句结束的符号,使用 delimiter 重新定义结束符号。例如:
1 | delimiter $$ #重新指定结束符为$$ |
# 变量
- 系统变量:
系统变量是 MySQL 服务器提供,不是用户定义的,属于服务器层面。
分为全局变量 (GLOBAL)、会话变量 (SESSION)。
1 | #查看系统变量 |
注意:mysql 服务重新启动之后,所设置的全局参数会失效,如果不想失效,需要在 /etc/my.cnf 中配置。
- 用户自定义变量:
用户自定义变量是用户根据需要自己定义的变量,用户变量不用提前声明。
在使用的时候可以直接用 "@变量名" 使用就可以。其作用域为当前会话。
1 | #赋值 |
- 局部变量:
局部变量是根据需要定义的在局部生效的变量,访问之前,需要 DECLARE 声明。可用作存储过程中的局部变量和输入参数,局部变量的范围是在其内声明的 BEGIN…END 块。
1 | #声明 |
# if 语句
语法:
1 | IF 条件1 THEN |
# 参数
类型 | 含义 |
---|---|
IN | 该类参数作为输入,也就是需要调用时传入值 (默认为该类型) |
OUT | 该类型作为输出,也就是该参数可以作为返回值 |
INOUT | 既可以作为输入参数,也可以作为输出参数 |
示例:
1 | create procedure p_name(in score int, out result varchar(10)) |
# case 语句
1 | #方式一: |
示例:
1 | create procedure p_name(in month int, out result varchar(20)) |
# 循环 (while/repeat/loop)
while:
1 | WHILE condition DO |
repeat:
1 | REPEAT |
loop:
1 | #loop是一个仅由LEAVE(break)和ITERATE(continue)关键字控制的死循环 |
# 游标
游标 (CURSOR) 是用来存储查询结果集的数据类型,在存储过程和函数中可以使用游标对结果集进行循环和处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE,其语法分别如下。
1 | #声明游标 |
使用案例:
1 | #根据传入的参数uage,查询用户表tb_user中,所有年龄小于uage的用户姓名和专业 |
# 条件处理程序
条件处理程序 (Handler) 可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤,语法为:
1 | DECLARE handler_action HANDLER FOR condition_value [, condition_value]... statement; |
示例:当游标为空时,退出循环
1 | declare exit handler for SQLSTATE '02000' close u_cursor; |
# 存储函数
存储函数是有返回值的存储过程,存储函数的参数只能是 IN 类型。语法如下:
1 | CREATE FUNCTION 存储函数名称([参数列表]) |
使用示例:
1 | #累加1-n |
# 触发器
触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的 SQL 语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。
现在触发器还只支持行级触发,不支持语句级触发。
触发器类型 | NEW 和 OLD |
---|---|
INSERT 型触发器 | NEW 表示将要或者已经新增的数据 |
UPDATE 型触发器 | OLD 表示修改之前的数据,NEW 表示将要或已经修改后的数据 |
DELETE 型触发器 | OLD 表示将要或者已经删除的数据 |
语法:
1 | #创建 |
使用示例:
1 | #查看触发器 |
# 锁
- 介绍
锁是计算机协调多个进程或线程并发访问某一资源的机制。
在数据库中,除传统的计算资源 (CPU、RAM、I/O) 的争用以外,数据也是一种供许多用户共享的资源。
如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。
从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
- 分类
MySQL 中的锁,按照锁的粒度划分,可以分为以下三类:
- 全局锁:锁定数据库中的所有表
- 表级锁:每次操作锁住整张表
- 行级锁:每次操作锁住对应的行数据
# 全局锁
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的 DML 的写语句,DDL 语句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
语法:
1 | #添加全局锁 |
特点:
数据库中加全局锁,是一个比较重要的操作,存在以下问题:
- 如果在主库上备份,那么备份期间都不能执行更新,业务基本上就要停摆
- 如果在从库上备份,那么备份期间从库就不能执行主库同步过来的二进制日志 (binlog),会导致主从延迟
在 InnoDB 引擎中,我们可以在备份时加上参数–single-transaction 参数来完成不加锁的一致性数据备份。
# 表级锁
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。
应用在 MyISAM、InnoDB、BDB 等存储引擎中。
对于表级锁,主要分为以下三类:
- 表锁
- 元数据锁 (meta data lock MDL)
- 意向锁
对于表锁,分为以下两类:
- 表共享读锁 (read lock),读锁只阻塞其他客户端的写入操作,不阻塞读取操作
- 表独占写锁 (write lock),阻塞其他客户端的读写操作
语法:
1 | #加锁 |
元数据锁 (meta data lock):
MDL 加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动添加。
MDL 锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。
为了避免 DML 与 DDL 冲突,保证读写的正确性。
在 MySQL5.5 中引入了 MDL,当对一张表进行增删改查的时候,添加 MDL 读锁 (共享)。
当对表结构进行变更操作时,添加 MDL 写锁 (排他)
对应 SQL | 锁类型 | 说明 |
---|---|---|
lock tables xxx read/write | SHARED_READ_ONLY / SHARED_NO_READ_WRITE | |
select、select … lock in share mode | SHARED_READ | 与 SHARED_READ、SHARED_WRITE 兼容,与 EXCLUSIVE 互斥 |
insert、update、delete、select … for update | SHARED_WRITE | 与 SHARED_READ、SHARED_WRITE 兼容,与 EXCLUSIVE 互斥 |
alter table … | EXCLUSIVE | 与其他 MDL 都互斥 |
意向锁:
为了避免 DML 在执行时,加的行锁与表锁冲突,在 InnoDB 中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
- 意向共享锁 (IS):由语句 select … lock in share mode 添加,与表锁共享锁 (read) 兼容,与表锁排它锁 (write) 互斥。
- 意向排他锁 (IX):由 insert、update、delete、select … for update 添加,与表锁共享、排它锁都互斥。
1 | #通过以下语句,查看意向锁及行锁的加锁情况: |
# 行级锁
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生冲突的概率最低,并发度最高。应用在 InnoDB 存储引擎中。
InnoDB 的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加锁。
对于行级锁,主要分为以下三类:
- 行锁 (Recored Lock):锁定单个行记录的锁,防止其他事务对此行进行 update 和 delete。在 RC、RR 隔离级别下都支持。
- 间隙锁 (Gap Lock):锁定索引记录间隙 (不包含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行 insert,产生幻读。在 RR 隔离级别下都支持。
- 临键锁 (Next-Key Lock):行锁和间隙锁的组合,同时锁住数据和间隙。在 RR 隔离级别下支持。
行锁
InnoDB 实现了以下两种类型的行锁:
- 共享锁 (S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁
- 排它锁 (X):允许获取排它锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排它锁
SQL | 行锁类型 | 说明 |
---|---|---|
INSERT | 排它锁 | 自动加锁 |
UPDATE | 排它锁 | 自动加锁 |
DELETE | 排它锁 | 自动加锁 |
SELECT (正常) | 不加锁 | |
SELECT … LOCK IN SHARE MODE | 共享锁 | 需要手动在 SELECT 之后加 LOCK IN SHARE MODE |
SELECT … FOR UPDATE | 排它锁 | 需要手动在 SELECT 之后加 FOR UPDATE |
默认情况下,InnoDB 在 REPEATABLE READ 事务隔离级别运行,InnoDB 使用临键锁进行搜索和索引扫描,以防止幻读。
- 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁
- InnoDB 的行锁是针对于索引加的锁,不通过索引条件检索数据,那么 InnoDB 将对表中的所有记录加锁,此时就会升级为表锁
间隙锁 / 临键锁
默认情况下,InnoDB 在 REPEATABLE READ 事务隔离级别运行,InnoDB 使用临键锁进行搜索和索引扫描,以防止幻读。
- 索引上的等值查询 (唯一索引),给不存在的记录加锁时,优化为间隙锁
- 索引上的等值查询 (普通索引),向右遍历时最后一个值不满足查询需求时,临键锁退化为间隙锁
- 索引上的范围查询 (唯一索引)–会访问到不满足条件的第一个值为止
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
# InnoDB 引擎
# 逻辑存储结构
- 表空间 (ibd 文件):一个 mysql 实例可以对应多个表空间,用于存储记录、索引等数据。
Linux 系统下,mysql 的文件存放在 /var/lib/mysql 中
-
段:分为数据段 (Leaf node segment)、索引段 (Non-leaf node segment)、回滚段 (Rollback segment),InnoDB 是索引组织表,数据段就是 B + 树的叶子节点,索引段即为 B + 树的非叶子节点。段用来管理多个 Extent (区)。
-
区:表空间的单元结构,每个区的大小为 1M。默认情况下,InnoDB 存储引擎页大小为 16K,即一个区中一共有 64 个连续的页。
-
页:是 InnoDB 存储引擎磁盘管理的最小单元,每个页的默认大小为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。
-
行:InnoDB 存储引擎数据是按行进行存放的
# 架构
MySQL5.5 版本开始,默认使用 InnoDB 存储引擎,它擅长事务处理,具有崩溃恢复特性,在日常开发中使用非常广泛。下面是 InnoDB 架构图,左侧为内存结构,右侧为磁盘结构。
# 内存结构:
缓冲池:
更改缓冲区 (MySQL5.+ 时为插入缓冲区)
自适应哈希索引:
日志缓冲区:
# 磁盘结构:
系统表空间,独立表空间:
通用表空间,撤销表空间,临时表空间:
双写缓冲区,重做日志:
# 后台线程
# 事务原理
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务四大特性:
原子性 (Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性 (Consistency):事务完成时,必须使所有的数据都保持一直状态。
隔离性 (Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
持久性 (Durability):事务一旦提交或回滚,它对数据库中的数据的改变是永久的。
被缩写为:ACID
# redo log
重做日志,记录的是事务提交时数据页的物理修改,用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲 (redo log buffer) 以及重做日志文件 (redo log file),前者是在内存中,后者在磁盘中。当事务提交之后,会把所有修改信息都存储到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
# undo log
回滚日志,用于记录数据被修改钱的信息,作用包含两个:提供回滚和 MVCC (多版本并发控制)。
undo log 和 redo log 记录物理日志不一样,它是逻辑日志。
可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录。当执行 rollback 时,就可以从 undo log 中的逻辑记录读取到相应的内容并进行回滚。
Undo log 销毁:undo log 在事务执行时产生,事务提交时,并不会立即删除,因为这些日志还可能用于 MVCC。
Undo log 存储:undo log 采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含 1024 个 undo log segment。
# MVCC(Multi-Version Concurrency Control)
# 基本概念
当前读:
读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
对于日常的操作,如:
select … lock in share mode (共享锁),select … for update、update、insert、delete (排它锁)
都是一种当前读。
快照读:
简单的 select (不加锁) 就是快照读,读取的时记录数据的可见版本。有可能是历史数据,不加锁,是非阻塞读
- Read Committed:每次 select,都生成一个快照读
- Repeatable Read:开启事务后第一个 select 语句才是快照读的地方
- Serializable:快照读会退化为当前读
MVCC
全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为 MySQL 实现 MVCC 提供了一个非阻塞读功能。
MVCC 的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log 日志、readView
# MVCC - 隐藏字段
在 InnoDB 引擎中,创建一张表时,除了显示出来的字段外,还会自动创建两个 (三个) 隐藏字段:
隐藏字段 | 含义 |
---|---|
DB_TRX_ID | 最近修改事务 ID,记录插入这条记录或最后一次修改该记录的事务 ID |
DB_ROLL_PTR | 回滚指针,指向这条记录的上一个版本,用于配合 undo log,指向上一个版本 |
DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段 |
# undo log
回滚日志,在 insert、update、delete 的时候产生的便于数据回滚的日志。
当 insert 的时候,产生的 undo log 只在回滚时需要,在事务提交后,可被立即删除。
而 update、delete 的时候,产生的 undo log 不仅在回滚时需要,在快照读时也需要,不会被立即删除。
undo log 版本链
# readView
ReadView (读视图) 是快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护系统当前活跃的事务 id。
ReadView 中包含了四个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务 ID 集合 |
min_trx_id | 最小活跃事务 ID |
max_trx_id | 预分配事务 ID,当前最大事务 ID+1 (因为事务 ID 是自增的) |
creator_trx_id | ReadView 创建者的事务 ID |
# MySQL 管理
# 系统数据库
MySQL 数据库安装完成后,自带了以下四个数据库,具体作用如下:
数据库 | 作用 |
---|---|
mysql | 存储 MySQL 服务器正常运行所需要的各种信息 (时区、主从、用户、权限等) |
information_schema | 提供了访问数据库元数据的各种表和视图,包含数据库、表、字段类型及访问权限等 |
performance_schema | 为 MySQL 服务器运行时状态提供了一个底层监控功能,主要用于收集数据库服务器性能参数 |
sys | 包含一系列方便 DBA 和开发人员利用的 performance_schema 性能数据库进行性能调优和诊断的视图 |
# 常用工具
# mysql
该 mysql 不是指 mysql 服务,而是指 mysql 的客户端工具
语法:
1 | mysql [option] [database] |
# mysqladmin
mysqladmin 是一个执行管理操作的客户端程序。
可以用它来检查服务器的配置和当前状态、创建并删除数据库等。
1 | #通过帮助文档查看选项: |
# mysqlbinlog
由于服务器生成的二进制日志文件以二进制格式保存,所以如果想要检查这些文本的文本格式,就会使用到 mysqlbinlog 日志管理工具。
语法:
1 | mysqlbinlog [option] log-files1 log-files2 ... |
# mysqlshow
mysqlshow 客户端对象查找工具,用来很快的查找存在哪些数据库、数据库中的表、表中的列或者索引。
1 | #语法: |
# mysqldump
mysqldump 客户端工具用来备份数据库或在不同数据库之间进行数据迁移。
备份内容包含建表,及插入表的 SQL 语句
1 | #语法: |
# mysqlimport/source
mysqlimport 是客户端数据导入工具,用来导入 mysqldump 加 - T 参数后到处的文本 (txt) 文件
1 | #语法: |
如果需要导入 sql 文件,可以使用 mysql 中的 source 指令:
1 | source /root/xxxx.sql |
# ------ 分割线 ------(进阶内容到此结束)
# 日志
# 错误日志
错误日志是 MySQL 中最重要的日志之一,它记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。
当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。
该日志是默认开启的,默认存放目录 /var/log/,默认的日志文件名为 mysqld.log,查看日志位置:
1 | show variables like '%log_error%'; |
# 二进制日志
二进制日志 (BINLOG) 记录了所有的 DDL (数据定义语言) 语句和 DML (数据操纵语言) 语句,但不包含数据查询 (SELECT、SHOW) 语句。
作用:
- 灾难时的数据恢复;
- MySQL 的主从复制。
在 MySQL8 版本中,默认二进制日志是开启着的,涉及到的参数如下:
1 | show variables like '%log_bin%'; |
MySQL 服务器中提供了多种格式来记录二进制日志,具体格式及特点如下:
日志格式 | 含义 |
---|---|
STATEMENT | 基于 SQL 语句的日志记录,记录的是 SQL 语句,对数据进行修改的 SQL 都会记录在日志文件中 |
ROW | 基于行的日志记录,记录的是每一行的数据变更。(默认) |
MIXED | 混合了 STATEMENT 和 ROW 两种格式,默认采用 STATEMENT,在某些特殊情况下会自动切换为 ROW 进行记录。 |
1 | show variables like '%binlog_format%'; |
日志查看:
由于日志是以二进制方式存储的,不能直接读取,需要通过二进制日志查询工具 mysqlbinlog 来查看。
具体语法:
1 | mysqlbinlog [option] logfilename |
日志删除:
对于比较繁忙的业务系统,每天生成的 binlog 数据巨大,如果长时间不清除,将会占用大量的磁盘空间。
可以通过以下几种方式清理日志:
指令 | 作用 |
---|---|
reset master | 删除全部 binlog 日志,删除之后,日志编号从 binlog.000001 重新开始 |
purge master logs to ‘binlog.******’ | 删除 ****** 编号之前的所有日志 |
purge master logs before ‘yyyy-mm-dd hh:min:ss’ | 删除日志为 "yyyy-mm-dd hh:min:ss" 之前产生的所有日志,其中 hh 为 24 小时制 |
也可以在 mysql 的配置文件中配置二进制日志的过期时间,设置了之后,二进制日志过期会自动删除。
1 | show variables like '%binlog_expire_logs_seconds%'; |
# 查询日志
查询日志记录了客户端的所有操作语句,而二进制日志不包含查询数据的 SQL 语句。
默认情况下,查询日志是关闭的。通过下面这个语句查询其是否关闭,及其配置文件路径:
1 | show variables like '%general%'; |
修改 MySQL 的配置文件 /etc/my.cnf 文件,添加如下内容:
1 | #该选项用来开启查询日志: |
# 慢查询日志
慢查询日志记录了所有执行时间超过参数 long_query_time 设置值并且扫描记录不小于 min_examined_row_limit 的所有的 SQL 语句的日志,默认关闭。
long_query_time 默认为 10 秒,最小为 0,精度可以到微妙。
1 | #慢查询日志 |
默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。
可以使用 log_slow_admin_statements 和 log_queries_not_using_indexes 更改此行为:
1 | #记录执行较慢的管理语句 |
# 主从复制
# 概述
主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行 (也叫重做),从而使得从库和主库的数据保持同步。
MySQL 支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。
MySQL 复制的要点主要包含以下三个方面:
- 主库出现问题,可以快速切换到从库提供服务
- 实现读写分离,降低主库的访问压力
- 可以在从库中执行备份,以避免备份期间影响主库服务
# 原理
从上图来看,复制分成三步:
- Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中
- 从库读取主库的二进制日志文件 Binlog,写入到从库的中继日志 RelayLog
- 从库重做中继日志中的事件,将改变反应到它自己的数据
# 搭建
服务器准备:
首先需要准备主库与从库两台服务器,并且都安装 mysql 服务,然后开放其 3306 端口 (mysql 服务提供端口)。
主库配置:
1. 修改主库配置文件 /etc/my.cnf
1 | #mysql服务ID,保证整个集群环境中唯一,取值范围:1~2^32-1,默认为1 |
2. 重启 mysql 服务
3. 登录 mysql,创建远程连接的账号,并授予主从复制权限
1 | #创建itcast用户,并设置密码,该用户可在任意主机连接该MySQL服务 |
4. 通过指令,查看二进制日志坐标
1 | show master status; |
字段含义说明:
file:从哪个日志文件开始推送日志文件
position:从哪个位置开始推送日志
binlog_ignore_db:指定不需要同步的数据库
从库配置:
1. 修改配置文件 /etc/my.cnf
1 | #mysql服务ID,保证整个集群环境中唯一,取值范围1~2^32-1,和主库不一样即可 |
2. 重启 mysql 服务
3. 登录 mysql,设置主库配置
1 | CHANGE REPLICATION SOURCE TO SOURCE_HOST='XXX.XXX', SOURCE_USER='XXX', SOURCE_PASSWORD='XXX', SOURCE_LOG_FILE='XXX', SOURCE_LOG_POS=XXX; |
4. 开启同步操作
1 | start replica;#8.0.22之后 |
5. 查看主从同步状态
1 | show replica status;#8.0.22之后 |
# 分库分表
分库分表的中心思想是将数据分散存储,使得单一数据库 / 表的数据量变小。
以此来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。
# 拆分策略
有两种拆分策略:垂直拆分、水平拆分
垂直分库:以表为依据,根据业务将不同表拆分到不同库中
- 每个库的表结构都不一样
- 每个库的数据也不一样
- 所有库的并集是全量数据
垂直分表:以字段为依据,根据字段属性将不同字段拆分到不同表中
- 每个表的结构都不一样
- 每个表的数据也不一样,一般通过一列 (主键 / 外键) 关联
- 所有表的并集是全量数据
水平分库:以字段为依据,按照一定策略,将一个库的数据拆分到多个库中
- 每个库的表结构都一样
- 每个库的数据都不一样
- 所有库的并集是全量数据
水平分表:以字段为依据,按照一定策略,将一个表的数据拆分到多个表中
- 每个表的表结构都一样
- 每个表的数据都不一样
- 所有表的并集是全量数据
实现技术:
- shardingJDBC:基于 AOP 原理,在应用程序中堆本地执行的 SQL 进行拦截、解析、改写、路由处理。需要自行编码配置实现,只支持 java 语言,性能较高。
- MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不及前者。
# MyCat
# Mycat 基础及部署
MyCat 是开源的、活跃的、基于 java 语言编写的 MySQL 数据库中间件。
可以向使用 mysql 一样来使用 mycat,对于开发人员来说根本感觉不到 mycat 的存在。
优势:
- 性能可靠稳定
- 强大的技术团队
- 体系完善
- 社区活跃
安装
MyCat 是采用 java 语言开发的开源的数据库中间件,支持 Windows 和 Linux 运行环境。
在 Linux 中安装 MyCat 需要在服务器中提前安装如下软件:
- MySQL
- JDK
- MyCat
安装 JDK:
下载并解压 JDK 压缩包,将其置于指定目录 /usr/local
1 | tar -zxvf jdk...... -C /usr/local |
配置环境变量:
使用 vim 命令修改 /etc/profile 文件,在文件末尾加入如下配置:
1 | JAVA_HOME=/usr/local/jdk.... |
安装 MyCat:
下载并解压 MyCat 压缩包,将其置于指定目录 /usr/local
1 | tar -zxvf Mycat-server-1.6.7.3-release-20210913163959-linux.tar.gz -C /usr/local |
MyCat 目录结构:
- bin:存放可执行文件,用于启动或停止 mycat
- conf:存放 mycat 的配置文件
- lib:存放 mycat 的项目依赖包 (jar)
- logs:存放 mycat 的日志文件
# Mycat 概述
Mycat 并不存储数据,它仅作逻辑分片,数据仍存储在底层的 MySQL 上
# Mycat 入门
分片配置 (schema.xml):
配置示例:
配置 server.xml
启动服务
1 | 切换到Mycat的安装目录,执行如下指令,启动Mycat: |
Mycat 启动之后,占用端口号 8066
# Mycat 配置
schema.xml:配置逻辑库,逻辑表等相关信息
server.xml:配置 mycat 运行的服务相关的信息
rule.xml:配置分片规则的相关信息
# schema.xml
schema.xml 作为 Mycat 中最重要的配置文件之一,涵盖了 Mycat 的逻辑库、逻辑表、分片规则、分片节点及数据源的配置。
主要包含以下三组标签:
- schema 标签
- datanode 标签
- datahost 标签
schema 标签:
1 | <schema name="DB01" checkSQLschema="true" sqlMaxLimit="100" > |
schema 标签用于定义 Mycat 实例中的逻辑库,一个 Mycat 实例中,可以有多个逻辑库,可以通过 schema 标签来划分不同的逻辑库。
Mycat 中的逻辑库的概念,等同于 MySQL 中的 database 概念,需要操作某个逻辑库下的表时,也需要切换逻辑库 (use xxx)。
核心属性:
- name:指定自定义的逻辑库库名
- checkSQLschema:在 SQL 语句操作时指定了数据库名称,执行时是否自动去除;true 自动去除,false 不自动去除。
- sqlMaxLimit:如果未指定 limit 进行查询,列表查询模式查询多少条记录
dataNode 标签:
1 | <dataNode name="dn1" dataHost="dhost1" database="db01" /> |
dataNode 标签中定义了 Mycat 中的数据节点,也就是我们通常说的数据分片。
一个 dataNode 标签就是一个独立的数据分片。
核心属性:
- name:定义数据节点名称
- dataHost:数据库实例主机名称,引用自 dataHost 标签中 name 属性
- database:定义分片所属数据库
dataHost 标签:
1 | <dataHost name="dhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc"> |
该标签在 Mycat 逻辑库中作为底层标签存在,直接定义了具体的数据库实例、读写分离、心跳语句。
核心属性:
- name:唯一表示,供上层标签使用
- maxCon/minCon:最大连接数 / 最小连接数
- balance:负载均衡策略,取值 0,1,2,3
- writeType:写操作分发方式 (0:写操作转发到第一个 writeHost,第一个挂了,切换到第二个;1:写操作随机分发到配置的 writeHost)
- dbDriver:数据库驱动,支持 native、jdbc
# rule.xml
rule.xml 中定义所有拆分表的规则,在使用过程中可以灵活的使用分片算法,或者对同一个分片算法使用不同的参数,它让分片过程可配置化。
主要包含两类标签:
- tableRule
- Function
# server.xml
server.xml 配置文件包含了 Mycat 的系统配置信息,主要有两个重要的标签:system、user。
system 标签:
1 | <system> |
user 标签:
若 privileges check 不做更改,则表示允许所有权限。
# Mycat 分片规则
- 分片规则 - 范围:
根据指定的字段及配置的范围与数据节点的对应情况,来决定该数据属于哪一个分片。
- 分片规则 - 取模
根据指定的字段值与节点数量进行求模运算,根据运算结果,决定该数据属于哪一个分片。
- 分片规则 - 一致性 hash
所谓一致性 hash,指相同的 hash 因子计算值总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置。
- 分片规则 - 枚举
通过再配置文件中配置可能的枚举值,指定数据分布到不同的数据节点上,本规则适用于按照省份、性别、状态拆分数据等业务。
- 分片规则 - 应用指定
运行阶段由应用自主决定路由到哪个分片,直接根据字符子串 (必须是数字) 计算分片号。
- 分片规则 - 固定分片 hash 算法
该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制第 10 位与 1111111111 进行位 & 运算。(计算出来的结果必在 0-1023 之间)
特点:
- 如果是求模,连续的值,分别分配到各个不同的分片。但是此算法会将连续的值可能分配到相同的分片,降低事务处理的难度。
- 可以均匀分配,也可以非均匀分配。
- 分片字段必须是数字类型。
- 分片规则 - 字符串 hash 解析
截取字符串中的指定位置的子字符串,进行 hash 算法,算出分片。
- 分片规则 - 按 (天) 日期分片
- 分片规则 - 按月分片
# Mycat 的管理及监控
# Mycat 管理:
Mycat 默认开通 2 个端口,可以再 server.xml 中进行修改。
- 8066 数据访问端口,即进行 DML 和 DDL 操作
- 9066 数据库管理端口,即 mycat 服务管理控制功能,用于管理 mycat 的整个集群状态
1 | mysql -h ip -P port -u user -p password; |
部分基础指令:
命令 | 含义 |
---|---|
show @@help | 查看 Mycat 管理工具帮助文档 |
show @@version | 查看 Mycat 的版本 |
reload @@config | 重新加载 Mycat 的配置文件 |
show @@datasource | 查看 Mycat 的数据源信息 |
show @@datanode | 查看 Mycat 现有的分片节点信息 |
show @@threadpool | 查看 Mycat 的线程池信息 |
show @@sql | 查看执行的 SQL |
show @@sql.sum | 查看执行的 SQL 统计 |
# Mycat-eye
Mycat-web (Mycat-eye) 是对 mycat-server 提供的监控服务,功能不局限于对 mycat-server 使用。
它通过 JDBC 连接对 Mycat、Mysql 监控,监控远程服务器 (目前仅限于 linux 系统) 的 CPU、内存、网络、磁盘。
Mycat-eye 运行过程中需要依赖 zookeeper,因此需要先安装 zookeeper。
# 读写分离
读写分离,简单的说是把对数据库的读和写操作分开,以应对不同的数据库服务器。
主数据库提供写操作,从数据库提供读操作,这样能有效的减轻单台数据库的压力。
通过 Mycat 可以轻易实现上述功能,不仅支持 Mysql,也支持 Oracle 和 Sql Server。
# 一主一从
Mycat 配置:
关于 balance:
# 双主双从
Mycat 配置: