0%

MySQL

bing

mysql 基础

mysql 学习心得

mysql面试题

三层逻辑架构

  • 第一层:负责连接管理、授权认证、安全等
  • 第二层:负责解析查询并进行优化
  • 第三层:存储引擎

pic


pic

模式

数据库Schema理解

打个比方

  • 数据库 database:酒店

    1
    CREATE DATABASE database_name;
  • 模式 schema:房间

    1
    CREATE SCHEMA schema_name; -- mysql中模式和数据库是同义的
  • 表 table:床

    1
    2
    3
    4
    5
    6
    CREATE TABLE student (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age DATA NULL,
    PRIMARY KEY('id')
    );
  • 行 row:床上用品

    1
    INSERT INTO student(name,age) VALUES('apathy',NOW());
  • 用户 user:酒店管理员

    1
    2
    3
    CREATE USER user_name IDENTIFIED BY 'password'; -- 新创建的用户没有任何权限
    DROP USER user_name; -- 删除用户
    RENAME user_name TO new_name; -- 重命名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 权限
    SHOW GRANTS FOR user_name;

    -- 账户名 username@host,username@% 表示默认主机
    GRANT USAGE ON *.* TO 'apathy'@'%'; -- apathy用户无权限

    -- 授予权限
    GRANT SELECT ON test.* TO 'apathy'; -- 为apathy用户授予test数据库查看权限

    -- 撤销权限
    REVOKE SELECT ON test.* FROM 'apathy'; -- 撤销apathy用户的test数据库查看权限

数据定义语言(DDL)

  • CREATE

  • ALTER

    1
    2
    ALTER TABLE student ADD COLUMN gender VARCHAR(10); -- 增加列
    ALTER TABLE student DROP COLUMN age; -- 删除列
  • DROP

数据操纵语言(DML)

  • INSERT
  • DELETE
    • DELETE:删除一行数据或删除匹配的数据,并将删除操作记录在二进制文件和重做日志中
    • TRUNCATE:删除表中所有数据,不记录日志,不会触发删除触发器
  • UPDATE
  • SELECT

查询语句书写顺序:

select -> from -> where -> group by -> having -> order by -> limit

查询语句执行顺序:

from:从哪些表进行检索,自右向左解析表

where:过滤表数据,自左向右解析条件

group by :对过滤后的数据进行分组

having :过滤分组后的数据

select:获得查询结果

order by:对查询结果进行排序

limit:限制返回结果数

数据库控制语言(DCL)

  • grant
  • revoke

连接

  • 内连接

    内连接与等值连接相同,从笛卡尔乘积中选择满足筛选条件的数据记录

    1
    2
    3
    4
    5
    6
    7
    SELECT A.value, B.value
    FROM tablea AS A INNER JOIN tableb AS B
    ON A.key = B.key;

    SELECT A.value, B.value
    FROM tablea AS A, tableb AS B
    WHERE A.key = B.key;
    1
    SELECT * FROM tablea AS A, tableb AS b;  -- 返回笛卡尔积
  • 自然连接

    自然连接是把同名列通过等值测试连接起来的,同名列可以有多个

    1
    SELECT * FROM tablea AS A NATURAL JOIN tableb AS B;
  • 外连接

    • 左外连接

      1
      SELECT * FROM tablea AS A LEFT OUTER JOIN tableb AS B ON A.key = B.key;
    • 右外连接

      1
      SELECT * FROM tablea AS A RIGHT OUTER JOIN tableb AS B ON A.key = B.key;
    • 全外连接
      mysql 不支持全外连接,使用UNION ALL 模拟

  • 自连接

    1
    SELECT * FROM tablea AS A AND tablea AS B WHERE A.key = B.key;

视图

视图的定义功能强于基本表,视图的操作功能弱于基本表,视图的控制功能与基本表相当

1
2
3
CREATE VIEW view_name AS SELECT s.id, s.name FROM student AS s; -- as后跟查询
SELECT * FROM view_name ...; -- 视图和表操作一致
SHOW TABLES; -- 查看所有的表和视图

基表是单表:对视图进行DML操作会影响基表

  • INSERT:不一定成功
  • UPDATE:成功
  • DELETE:成功

基表是多表:对视图进行DML操作会影响基表

  • INSERT:不一定成功
  • UPDATE:成功
  • DELETE:不一定成功

优点:

  • 简化复杂 sql 操作,提高 sql 重用性
  • 视图是虚拟表,只使用实际表的部分数据
  • 通过只给用户访问视图的权限,保证数据的安全性
  • 对数据库重构,不影响程序运行
  • 更改数据格式和表示

约束

  • 外键约束

    • 关联两张表,保证表和表之间的数据完整性和准确性,实现一些级联操作
    • InnoDB 引擎支持外键,MyISAM 引擎不支持外键
    • 外键必须建立索引,没有索引会自动创建索引
    • 外键关系的两个表的列必须是数据类型相似的,能够进行转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name, ...)
    [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
    [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
    -- RESTRICT 限制外表中的外键改动
    -- CASCADE 跟随外键改动
    -- SET NULL 设空值
    -- NO ACTION 无动作,默认的
    -- SET DEFAULT 设默认值
  • 主键约束

  • 唯一约束

  • 非空约束

  • 自增

  • 默认值

范式

  • 1NF:属性不可分
  • 2NF:满足1NF,并且非主属性完全依赖主键
  • 3NF:满足2NF,并且非主属性相互独立,没有依赖关系

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 创建函数
CREATE FUNCTION function_name([parameter_name type,...])
RETURNS {INT|STRING|...}
BEGIN
-- function body
DECLARE var1[,var2,...] type [default default_value]; -- 声明局部变量
SET var1 = value1; -- 赋值操作
SELECT COUNT(*) FROM people INTO var2; -- 赋值操作
-- 条件
IF condition_one THEN ...;
ELSE IF condition_two THEN ...;
ELSE ...;
END IF;

-- 选择
CASE case_value
WHEN when_value THEN ...;
[WHEN when_value THEN ...];
[ELSE statement_list];
END CASE;

-- 循环
WHILE search_condition DO
...;
END WHILE;
RETURN (); -- 返回一个值,不能返回集合
END;
1
2
-- 使用函数
SELECT function_name(parameter_name);
1
2
-- 删除函数
DROP FUNCTION function_name;

自带4个数据库

  • information_schema:存储 mysql 数据库所维护的其他所有数据库的信息
  • mysql:主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息
  • performance_schema:性能优化
  • sys:快速了解系统的元数据信息,为性能瓶颈分析,性能优化提供帮助

临时表

临时表只在当前连接可见,当关闭连接时,MySQL会自动删除表并释放所有空间。因此在不同的连接中可以创建同名的临时表,并且操作属于本连接的临时表

临时表主要是为了放一些中间大结果集的一些子集,临时表建立在内存中,数据存储在内存中

mysql 索引

MySQL索引背后的数据结构及算法原理

B+ 树

  • 二叉搜索树

    • 任意节点如果有左子树,左子树的值小于当前节点
    • 任意节点如果有右子树,右子树的值大于当前节点
    • 任意节点的左子树和右子树也是二叉搜索树
  • 平衡二叉树

    • 平衡二叉树是二叉搜索树
    • 任意节点的左子树和右子树的高度差的绝对值小于等于1
    • 任意节点的左子树和右子树也是平衡二叉树
  • 红黑树

    红黑树详细分析

    • 红黑树是一种近似平衡的二叉搜索树
    • 红黑树有红节点和黑节点
    • 根节点一定是黑节点
    • 叶子节点都是黑节点(NULL节点)
    • 红节点的子节点一定是黑节点
    • 根节点到每个叶节点的路径都包含相同数量的黑节点

    红黑树的插入:

    • 如果新节点 N 是根节点,红染黑
    • 如果新节点 N 的父节点是黑节点,不作调整
    • 如果新节点 N 的父节点是红节点
      • 父节点的兄弟节点是红节点,将父节点以及其兄弟节点染黑,祖父节点染红,并向上递归
      • 父节点的兄弟节点是黑节点
        • 新节点是父节点的右孩子,左旋,再右旋
        • 新节点是父节点的左孩子,右旋

    pic

    红黑树的删除:

    • 如果待删节点 X 有两个孩子,找到该节点的前驱或后继,将前驱或者后继的值复制到要删除的节点中,转化为一个孩子节点(以下讨论一个孩子)

    • 如果待删节点 X 是红节点,直接用孩子节点 N 补位

    • 如果待删节点 X 是黑节点,并且孩子节点 N 是红节点,删除节点,用孩子节点 N 补位并染黑

    • 如果待删节点 X 是黑节点,并且孩子节点 N 是黑节点,删除待删节点 X 并用孩子节点 N 补位

      • 如果 N 是新的根节点,不作调整
      • 其他情况见下图

      pic

  • M阶B树(M指的是子节点的最大个数)

    • 根节点的子节点取值范围为$[2,M]$,关键字取值范围为$[1,M-1]$
    • 除根节点之外的非叶节点的子节点取值范围为$[\frac{M}{2},M]$,关键字取值范围为$[\frac{M}{2}-1,M-1]$
    • 叶节点位于同一层,关键字取值范围为$[\frac{M}{2}-1,M-1]$
    • 节点内关键字从小到大排序
    • 一个节点有$k-1$个关键字,则有$k$个子节点,$k-1$个关键字刚好是$k$个子节点的值域划分
  • B+树

    • 有 $k$ 个子树的节点包含 $k$ 个关键字,非叶节点保存索引,叶节点保存关键字
    • 叶节点关键字从小到大排序,并且增加指向下一个叶节点的指针
    • 非页节点存储索引,存储的索引是子节点的最小值或最大值

B+ 树做索引的优点

  • B+ 树的磁盘 IO 操作次数更少 / 查找次数更少

    硬盘基本知识(磁头、磁道、扇区、柱面)

    索引本身也很大,不可能全部存储在内存中,索引往往以索引文件的形式存储在磁盘中,那么索引的查找过程就要产生磁盘 IO 操作。磁盘的读写时间主要由寻道时间和旋转时间决定,而寻道时间和旋转时间相对于内存读写时间要慢很多

    索引的查找次数越少, IO 操作次数越少。索引的查找次数由树的高度决定,相比于其他的搜索树,B+树的高度更小,复杂度为$O(log_dN)$ ,$d$ 通常很大,这也意味着磁盘 IO 操作更少

  • 利用磁盘预读特性

    说说Innodb中LRU怎么做的?

    磁盘的最小读写单位是扇区,操作系统与磁盘的最小读写单位是块/簇,内存与操作系统的最小读写单位是页。通常情况下,扇区和页的大小都是4K字节

    为了减少 IO 操作,磁盘往往不是按需读取,而是每次都会预读。预读过程中磁盘会进行顺序读取,顺序读取不需要磁盘并且只需要很短的旋转时间,所以预读可以提高 IO 效率,速度非常快。预读的长度一般为页的整倍数
    B+ 树的节点大小等于一个页的大小,这样每个节点只需要一次 IO 操作就可以完全载入。每次新建节点,直接申请一个页的空间,可以实现一个节点只需一次 IO 操作。MySQL 默认页的大小是 16Kb,节点的大小也为 16 Kb。通常 B+ 树的高度在 2-4 层,由于 MySql 在运行时,根节点是常驻内存的,因此每次查找只需要大约 2 -3 次 IO

  • B+ 树的查询效率更加稳定

    关键字都在叶节点,并且叶节点都在同一层

  • B+ 树适合范围查找

    B树的关键字存储于整棵树的节点内,查询必须遍历树;而B+树的关键字存储于叶节点,非叶节点存储索引,并且叶节点之间存在指针,适合范围查找和排序等操作

Hash 索引

hash 索引的查询时间复杂度是 $O(1)$,相对于 B+ 树索引更高效。但 Hash 索引存在一系列问题:

  • Hash 索引只支持等值查询,比如 =in
  • Hash 索引不支持范围查找,在进行范围查找时要遍历全表
  • Hash 索引在查询时必须使用全部索引字段,不然计算的 hash 值不一样;不支持联合索引的最左前缀匹配原则
  • Hash 索引无法利用索引排序

Innodb索引类型

  • 聚簇索引 / 聚集索引:叶节点存储完整的数据记录,表数据按照索引顺序进行存储。一张表只能有一个聚簇索引
  • 非聚簇索引:叶节点存储数据存放的地址,不要求数据存储顺序和索引顺序一致
  • 辅助索引:以其他字段作为索引,叶节点存储主键信息
  • BTree+索引:使用B+ 树作为索引的数据结构
  • 哈希索引:Innodb存储引擎支持自适应哈希索引。当某个索引值被频繁使用时,会在 B+树索引之上再创建一个哈希索引
  • 全文索引:Innodb 存储引擎在MySQL 5.6.4版本中开始支持全文索引

索引实现

  • InnoDB 存储引擎:数据文件本身就是索引文件,表文件是按照 B+ 树组织的一个索引结构,data 域保存了完整的数据记录,这种索引叫聚集索引;辅助索引的 data 域存储的是主键
  • MyISAM 存储引擎:使用B+ 树作为索引结构,叶节点包括 key 和 data ,data 域存储数据记录的地址;辅助索引的 data 域也存储数据记录的地址

索引使用和优化

索引按个数划分,可以分为单列索引和联合索引,联合索引要满足最左前缀原则

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE people(
id INT(20) AUTO_INCREMENT,
name VARCHAR(50),
age DATE,
gender VARCHAR(10) NOT NULL,
location VARCHAR(100) DEFAULT 'secret',
PRIMARY KEY(id,name,age)
);

INSERT INTO people(name,age,gender)
VALUES ('apathy','1994-04-16','man'),
('linda','1995-12-01', 'woman'),
('bob','1993-04-01','man');
  • 全列匹配

    1
    SELECT * FROM people WHERE id = 1 and name = 'apathy' and age = '1994-04-16'; -- 会使用索引

    索引对顺序敏感,mysql 查询优化器会自动调整 where 子句的条件查询顺序以使用适合的索引

  • 最左前缀匹配

    1
    SELECT * FROM people WHERE id = 1 and name = 'apathy'; -- 会使用索引
  • 查询条件未提供索引中间列

    1
    SELECT * FROM people WHERE id = 1 and age = '1994-04-16'; -- 会使用索引,只使用id
  • 查询条件未提供最左前缀

    1
    SELECT * FROM people WHERE name = 'apathy' and age = '1994-04-16'; -- 不会使用索引
  • 匹配前缀字符串

    1
    SELECT * FROM people WHERE id = 1 and name like 'a%'; -- 会使用索引

    必须是最左前缀

  • 范围查找

    1
    SELECT * FROM people WHERE id < 3 and name = 'apathy'; -- 会使用索引,只使用id
  • 查找条件包含表达式或函数

    1
    2
    SELECT * FROM people WHERE id + 1 = 2; -- 不会使用索引
    SELECT * FROM people WHERE POW(id,2) = 2; -- 不会使用索引
    1
    SELECT * FROM people WHERE id = 1 and CONCAT(name,',hello') = 'apathy,hello'; -- 会使用索引,只使用id

mysql 存储引擎

InnoDB & MyISAM

  • 存储:InnoDB 聚簇索引和数据存储在一个文件中,MyISAM 索引和数据分开存储
  • 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句
  • 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁
  • 外键:InnoDB 支持外键
  • 备份:InnoDB 支持在线热备份
  • 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢
  • 其它特性:MyISAM 支持压缩表和空间数据索引
  • InnoDB 不保存表的行数,MyISAM 保存表的行数
  • InnoDB 更安全,保证数据一致性,MyISAM的性能更优,存储空间更少

Memory

内存型数据库,数据全部放在内存中

mysql 事务

日志

Mysql日志

  • 重做日志(redo log)

    记录修改后的数据;存放保证事务持久性

  • 回滚日志(undo log)

    记录修改前的数据;保存事务发生之前的数据版本,可以用于回滚,同时可以提供MVCC下的读;回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可;MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。当需要读取的记录已经被其他事务加锁的时候, 当前事务可以通过 undo 读取之前的版本, 以此实现⼀致性⾮锁定读

  • 二进制日志(bin log)

    记录数据库的写操作,在事务提交时会把写操作写入二进制日志,可用于主从复制

  • 错误日志(error log)

    记录数据库启动、运行、停止时发生的问题

  • 慢查询日志(slow query log)

    记录查询缓慢的 SQL 操作,方便找到 SQL 的瓶颈进行优化

  • 一般查询日志(general log)
    记录执行的 SQL 语句

  • 中继日志(relay log)

    来自主数据库的二进制日志文件,IO 线程读取主数据库二进制文件并写入本地的一类日志

ACID

事务是满足ACID特性的一组操作

pic

  • 原子性
    通过 undo log 回滚日志实现
  • 一致性
    事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的
  • 隔离性
  • 持久性
    通过 redo log 重做日志实现

并发一致性问题

  • 脏读

  • 不可重复读

    侧重于读-读操作,两次读取信息不一致

  • 幻读

    mysql 幻读详解

    幻读侧重于某一次的 select 操作得到的结果无法支撑后续的业务操作。举个例子:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在无法插入,此时就发生了幻读

    侧重于读-写操作,第一次读的信息不能支撑写操作

封锁协议

三级封锁协议

  • 一级封锁协议:事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁
  • 二级封锁协议:在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁
  • 三级封锁协议:在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁

两段锁协议

加锁和解锁分为两个阶段进行

隔离级别

  • 读未提交:存在脏读、不可重复读、幻读问题
    • 一个事务可以读取到其他事务未提交的DML数据
  • 读已提交:存在不可重复读、幻读
    • 一个事务可以读取到其他事务已提交的DML数据
  • 可重复读:存在幻读问题
    • 一个事务可以读取到其他事务已提交的新插入的数据
  • 串行
    • 事务串行执行
    • or 事务可以并行执行,但是事务对相同数据的操作是串行执行的,对表和行加锁

快照读和当前读

  • 快照读
    读取某一时刻的快照,RR 级别下的快照读使用 MVCC 和 undo log 来实现

    1
    SELECT * FROM ...
  • 当前读
    读取的是最新的数据,需要加锁。以下第一个语句需要加 S 锁,其它都需要加 X 锁

    1
    2
    3
    4
    5
    select * from table where ? lock in share mode;
    select * from table where ? for update;
    insert;
    update;
    delete;

多版本并发控制(MVCC)

Mysql中MVCC的使用及原理详解

多版本并发控制是 MySQL 的 innodb 存储引擎实现隔离级别的实现方式。MVCC 可以认为是行锁的变种,实现了不加锁读,它在大多数情况下避免了加锁,使系统开销更小

MVCC 在读已提交和可重复读两种隔离级别下工作。未提交读总是读取最新的数据,不需要使用 MVCC;串行需要对读取的表加锁,无法使用 MVCC 实现

在RR级别下,快照读是通过MVVC(多版本控制)和 undo log 来实现的

innodb 行结构

innodb 存储引擎的行中额外存储一些信息:

  • DATA_TRX_ID:6字节,标记了最新更新这条行记录的 transaction id
  • DATA_ROLL_PTR:7字节,回滚指针,指向当前行记录的回滚日志记录
  • DB_ROW_ID:6字节,如果没有主键的情况
  • DELETE BIT:标识该记录是否被删除

快照:表示当前 select 时刻或事务第一次select时刻还有哪些未提交的事务,维护一个 list,存储正在运行的事务版本号

MVCC在可重复读和读已提交隔离级别下的区别:

  • 在可重复读隔离级别下,并不是事务开始时就建立快照,而是事务中第一个查询语句执行时才会建立快照
  • 在读已提交隔离级别下,事务内每次执行查询时都会建立快照,即会产生不可重复读的问题

如何解决脏读和不可重复读问题:

  • 脏读
    脏读是一个事务读取到了其他事务未提交的更新的数据。MVCC是通过保存数据在某个时间点的快照来实的,而数据快照都是当前时刻已提交的数据,所以不存在脏读问题
  • 不可重复读
    不可重复读是一个事务读取到了其他事务已提交的更新的数据。可重复读隔离级别下的 MVCC 保存的是事务中第一个查询语句执行时的快照,其他事务之后的更新操作不会影响到快照,所以不存在不可重复读问题;而在读已提交隔离级别下的 MVCC 每次执行查询时都会建立快照,能够读取到其他事务已提交的修改数据,所以存在不可重复读问题

解决幻读问题

MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题

1
2
SELECT * FROM ... FOR UPDATE; -- 对行加写锁 / 排它锁,并且对表加意向排它锁
SELECT * FROM ... LOCK IN SHARE MODE; -- 加读锁 / 共享锁,并且对表加意向共享锁

间隙锁:当用范围条件而不是相等条件检索数据并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但不存在的记录叫做间隙,InnoDB也会对间隙加锁,这种锁机制就是间隙锁

InnoDB 存储引擎的行锁锁定的是索引,而不是记录本身。如果记录存在,加行锁;如果记录不存在,加 Next-Key Locks / 间隙锁;索引相同的记录都会被加锁,故在设计数据库索引时,最好设计成主键索引或唯一索引

mysql 进阶

切分

分表

分表是将一张大表按照一定的规则分解成多张具有独立存储空间的实体表;分表由开发人员完成

问题:

  • ID:全局唯一ID
  • 事务:分布式事务
  • 连接:分解成多个单表查询

分区

分区是将一张大表按照一定的规则将数据划分在多个位置存放,分区由数据库完成(表仍然在同一个物理机上,但可以在不同的磁盘上)

  • 水平分区

    • 将一张表按行切分成多张表
    • 可以通过哈希进行划分,比如 $hash(key)%N$
    • 可以按照范围进行划分,比如时间段
    • 可以使用映射表存储映射关系
  • 垂直分区

    • 将一张表按列切分成多张表
    • 可以按照列的关系密集程度来划分
    • 可以按照列的使用频率进行划分

MySQL支持RANGE,LIST,HASH和 KEY 四种分区,并且每个分区又都有一种特殊的类型。

  • range 分区

    根据列值所属范围进行分区,比如时间段;支持数字类型

  • range columns 分区

    参数是列名,支持多列;支持数字,date,datetime,字符串类型

  • list 分区

    列值匹配一个离散值集合中的某个值,比如男女;支持数字类型

  • list columns 分区

    参数是列名,支持多列;支持数字,date,datetime,字符串类型

  • hash 分区

    根据 hash 函数的返回值进行分区,基于模函数,支持数字类型,比如$hash(column)%N$

  • 线性 hash 分区

    基于另一种算法,它的优点是在数据量大的场景,譬如TB级,增加、删除、合并和拆分分区会更快,缺点是,相对于HASH分区,它数据分布不均匀的概率更大

  • key 分区

    key 分区使用系统提供的 hash 函数进行分区,支持多列;支持除 textBLOB 之外的所有数据类型

  • 线性 key 分区

    同线性 hash 类似

mysql 分区实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 例子:range分区
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (6),
PARTITION p1 VALUES LESS THAN (11),
PARTITION p2 VALUES LESS THAN (16),
PARTITION p3 VALUES LESS THAN MAXVALUE
);

分库

一个系统的多张数据表,存储到多个数据库实例中

主从复制

主从复制面试之作用和原理
mysql 主从复制原理
mysql实现主从复制

主从复制,建立一个或多个和主数据库一样的数据库环境,称为从数据库

形式:

  • 一主一从
  • 主主复制
  • 一主多从
  • 多主一从
  • 联级复制

作用 / 好处:

  • 数据热备份,避免数据丢失
  • 容灾,主数据库故障后可以使用从数据库
  • 读写分离,主数据库处理写操作及实时性要求比较高的读操作,从数据库处理读操作
  • 降低单机服务器压力

原理:

  • 主库 binlog 线程 :负责将主数据库上的数据更改写入二进制日志(Binary log)
  • 从库 I/O 线程:负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)
  • 从库 SQL 线程 :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中执行

问题

异步复制、全同步复制与半同步复制(逻辑上和技术上)

  • 主数据库宕机导致数据丢失

    解决:半同步复制,MySQL 5.5 集成了半同步复制,需要安装插件

    主从复制是异步复制

    异步

    半同步

  • 从数据库复制延迟

    解决:并行复制,从库多线程复制二进制日志

读写分离

mysql 读写分离

主数据库处理写操作及并发性要求比较高的读操作,从数据库处理读操作

作用 / 好处:

  • 读操作耗时短,写操作耗时长,读写分离缓解锁竞争,提高数据库的并发负载能力
  • 从服务器可以使用 MyISAM,提升查询性能以及节约系统开销
  • 提高安全性

实现:

  • 代码实现

  • 代理服务器

    pic

全局 ID

  1. 自动增长

    • 设置自增偏移和步长:伸缩性不好
    • redis 全局 ID 映射表:有延迟
  2. UUID (GUID)

    • UUID 共 128 位,包括机器识别号,时间戳,随机数

    • 缺点:存储空间大,无序,性能不好

  3. Snowflake

    SnowFlake

mysql 优化

大佬是怎么思考设计MySQL优化方案的

性能优化顺序

  1. 数据库结构设计和 SQL 优化

    • 索引:正确使用索引,索引不能是表达式的一部分,也不能作为函数的参数;对于多个查询条件使用多列索引比使用单列索引更高效,多列索引满足最长前缀匹配原则;对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引;小表可以直接全表扫描,中型表使用索引,大型表进行分区

    • SQL:使用 Explain 分析查询语句;只返回必要的行和列;不使用外键保证数据完整性和准确性;切分大查询;连表查询转化成单表查询;分批处理,比如使用 LIMIT

      MySql常用30种SQL查询语句优化方法

      • 尽量避免在 where 中使用!=<> ,否则将进行全表扫描
      • 尽量避免 null 判断,否则将进行全表扫描
      • 尽量避免使用 or 进行连接查询,否则将进行全表扫描;可以用 union代替
      • 尽量避免使用 innot in,否则会导致全表扫描;可以考虑 between ... and ...exist
    • 缓存:使用 redis 缓存热数据;使用本地内存作为二级缓存;将 MySQL 缓存区调大

    • 分区:一张大表按照一定的规则将数据划分在多个位置存放,MySQL 支持 range、list、hash、key 分区

    • 垂直分表:将一张大表根据字段关联程度或使用频率切分成多个小表

    • 水平分库分表:分布式部署;主从复制,读写分离

  2. 数据库参数配置和存储引擎的选择

    • 数据库参数配置:

      • innodbfileper_table = ON 开启独立表空间,默认开启

      • sort_buffer_size:每个线程排序缓存大小

      • join_buffer_size:每个线程连表查询缓存大小

      • read_buffer_size:每个线程查询缓存大小

      • read_rnd_buffer_size:每个线程索引缓存大小

      • Innodb_buffer_pool_size: InnoDB 缓存池大小,可以设置大一点

      • innodb_flush_log_at_trx_commit=(0,1,2):1是最安全的,0是性能最高,2折中

        0:每秒进行一次 log 写入 cache,并 flush log 到磁盘

        1:在每次事务提交执行 log 写入 cache,并 flush log 到磁盘

        2:每次事务提交,执行 log 数据写到 cache,每秒执行一次 flush log 到磁盘

      • query_cache_size:查询缓存大小

      • max_connections:允许的最大连接数

      • thread_concurrency:并发线程数

      • innodb_lock_wait_timeout :InnoDB 存储引擎锁的超时时间

    • 存储引擎选择:对于只读的数据库选择 MyISAM 存储引擎,比如读写分离的从数据库可以选择 MyISAM; InnoDB 使用独立表空间

  3. 系统优化和硬件升级

    • 选择高性能的硬盘

mysql 问题排查

MySQL 层面

  1. mysqlshow:功能强大的查看shell命令
  2. show [SESSION | GLOBAL] variables:查看数据库参数信息
  3. SHOW [SESSION | GLOBAL] STATUS:查看数据库的状态信息
  4. information_schema:获取元数据的方法
  5. SHOW ENGINE INNODB STATUS:Innodb引擎的所有状态
  6. SHOW PROCESSLIST:查看当前所有连接session状态
  7. explain:获取查询语句的执行计划
  8. show index:查看表的索引信息
  9. slow-log:记录慢查询语句
  10. mysqldumpslow:分析slowlog文件的

CPU 层面:top

内存层面:ps -ef ps -aux

IO 设备:iostat

网络层面:netstat

死锁

mysql 死锁

记录一次Mysql死锁排查过程

MySQL 死锁主要原因是不同连接加锁顺序不一致引起的

mysql 日志流程

pic