MySQL入门到精通

目录

基础

SQL

通用语法

  1. SQL语句可以单行或者多行书写,以分号结尾
  2. SQL语句可以使用空格/缩进来增强语句可读性
  3. MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
  4. 注释
sql 复制代码
--单行注释
# 单行注释
/*
多行注释
*/

分类

DDL

数据定义语言,用来定义数据库对象(数据库,表,字段)

数据库操作
sql 复制代码
# 查询
## 查询所有数据库
show databases;
## 查询当前数据库,显示当前数据库名称
select database();
# 创建
create database [if not exists] test [default charset utf8mb4] [collate 排序规则]; 
# 删除
drop database [if exists] test;
# 使用
use test;
表操作

查询

sql 复制代码
# 查询数据库所有表
show tables;
# 查询表结构
desc 表名
# 查询建表语句
show create table 表名

创建

sql 复制代码
create table t_user(
字段1 类型 [comment 字段1注释],
...
字段n 类型 [comment 字段n注释]
)[comment 表注释];
数据类型

修改

sql 复制代码
# 添加字段
alter table 表名 add 字段 类型 [comment 注释] [约束];
# 修改数据类型
alter table 表名 modify 字段 新类型
# 修改字段名和数据类型
alter table 表名 change 旧字段 新字段 类型;
# 删除字段
alter table 表名 drop 字段;
# 修改表名
alter table 表名 rename to 新表名;

删除

sql 复制代码
# 删除表
drop table if exists 表名;
# 删除表数据
truncate table 表名;

DML

数据操作语言,对数据进行增删改

sql 复制代码
# 添加指定字段数据
insert into 表名(字段1,字段2,...) values(值1,值2,...);
# 添加全部字段
insert into 表名 values(值1,值2,...);
# 批量添加
insert into 表名(字段1,字段2,...) values(值1,值2,...),(值1,值2,...)...;
sql 复制代码
update 表名 set 字段名1=值1,字段2=值2,... where 条件;
sql 复制代码
delete from 表名 [where 条件];

DQL

数据查询语言,用来查询数据库中表的记录

sql 复制代码
select
	字段列表
from 
	表名
where
	条件
group by 
	分组字段列表
having
	分组后条件
order by
	排序字段列表
limit 
	分页参数;

条件查询练习

sql 复制代码
# 查询年龄在15到20岁之间,两个都包含
select * from emp where age between 15 and 20;
select * from emp where age >=15 and age<=20;
# 查询性别为女且年龄小于25岁的员工
select * from emp where gender='女' and age < 25;
# 查询年龄等于18或20或40的员工
select * from emp where age in (18,20,40);
# 查询姓名为两个字的员工
select * from emp where name like '__';
# 查询身份证最后一位为X的员工
select * from emp where idcard like '%X';
聚合函数
sql 复制代码
# 优化器会将count(1) 优化成 count(*)来执行,count(*)会选择最小的飞聚簇索引进行扫描
select count(*) from emp;
select count(1) from emp;
# 会在聚簇索引上进行扫描
select count(主键) from emp;
select max(age) from emp;
select min(age) from emp;
select avg(age) from emp;
select sum(age) from emp;
分组查询
sql 复制代码
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];

where条件在分组前执行,having在分组后执行

where不能对聚合函数进行判断,having可以

练习

sql 复制代码
# 根据员工性别分组,并统计男员工和女员工的数量
select count(*) from emp group by gender;
# 根据性别分组,统计男员工和女员工的平均年龄
select avg(age) from emp group by gender;
# 查询年龄小于45的员工,根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress,count(*) from emp where age < 45 group by workaddress having count(*)>=3;

执行顺序:where > 聚合函数 > having

分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义

排序查询
sql 复制代码
# ASC 升序 DESC 降序
select 字段列表 from 表名 order by 字段1 排序方式,字段2 排序方式2;
分页查询
sql 复制代码
# 起始索引=(查询页码数-1)*每页显示记录数
select 字段列表 from 表名 limit 起始索引,查询记录数;

练习

sql 复制代码
# DQL练习
# 查询年龄为20-23岁的女性员工信息
select * from emp where age between 20 and 23 and gender = '女';
# 查询性别为男,年龄在20-40岁以内的姓名为三个字的员工
select * from emp where age='男' and age between 20 and 40 and name like '___';
# 统计员工表中,年龄小于60岁的,男性员工和女性员工的人数
select count(*) from emp where age<60 group by gender;
# 查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序
select name,age from emp where age<35 orer by age asc,entrydate desc;
# 查询性别为男,年龄在20-40岁之间的前5个员工,按年龄升序,入职时间升序
select * from emp where age between 20 and 40 order by age asc,entrydate asc limit 5;
执行顺序

DCL

管理用户
sql 复制代码
# 查询用户
use mysql;
select * from user;
# 创建用户
create user '用户名@主机名' identified by '密码';
# 修改用户密码
alter user '用户名@主机名' identified with mysql_native_password by '新密码';
# 删除用户
drop user '用户名@主机名';
权限控制
sql 复制代码
# 查询权限
show grants for '用户名@主机名';
# 授予权限
grant 权限列表 on 数据库名.表名 to '用户名@主机名';
# 撤销权限
revoke 权限列表 on 数据库名.表名 from '用户名@主机名';

函数

字符串函数

数值函数

日期函数

流程函数

约束

sql 复制代码
#添加外键
create table 表名(
字段名 字段类型,
...,
[constraint] [外键名称] foreign key(外键字段名) references 主表(主表列名)
);
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
sql 复制代码
alter table 表名 add constraint 外键名称 foreign key (外键字段) references 主表名(主表字段名) on update cascade on delete cascade;

多表查询

表间关系

相关概念

笛卡尔积: 笛卡尔乘积是指两个集合A和B中所有组合情况。

分类

连接查询

  • 内连接:相当于查询A、B交集部分数据
  • 外连接:
    • 左外连接:查询左表所有数据,以及两表相交数据
    • 右外连接:查询右表所有数据,以及两表相交数据
  • 自连接:当前表与自身的连接查询,自连接必须使用表别名

子查询

SQL语句中嵌套select语句,称为嵌套查询,又称子查询

子查询外部的语句可以是insert/update/delete/select的任何一个

  • 标量子查询(子查询结果为单个值)
  • 列子查询(子查询结果为一列)
  • 行子查询(子查询结果为一行)
  • 表子查询(子查询结果为多行多列)
    子查询的位置可以在where之后,from之后,select之后
内连接
sql 复制代码
# 隐式内连接
select 字段列表 from 表1,表2 where 条件;
# 显式内连接
select 字段列表 from 表1 [inner] join 表2 on 连接条件;
外连接
sql 复制代码
# 左外连接
select 字段列表 from 表1 left [outer] join 表2 on 条件;
# 右外连接
select 字段列表 from 表1 right [outer] join 表2 on 条件;
自连接

自连接可以是内连接也可以外连接

sql 复制代码
select 字段列表 from 表A 别名a join 表A 别名b on 条件;
联合查询

联合查询就是把多次查询的结果合并起来,形成一个新的查询结果集

union会将合并的结果集去重

sql 复制代码
select 字段列表 from 表A
union [all]
select 字段列表 from 表B;
子查询

标量子查询

列子查询

行子查询

表子查询

练习
sql 复制代码
# 查询员工的姓名,年龄,职位,部门信息
select * from emp;
# 查询年龄小于30岁的员工姓名,年龄,职位,部门信息
select * from emp where age <30;
# 查询所有员工的部门ID,部门名称
# 查询所有年龄大于40岁的员工,及其归属的部门名称,如果员工没有分配部门,也需要展示出来
select emp.*,dept.name from emp left join dept on emp.dept_id = dept.id where emp.age > 40; 
# 查询所有员工的工资等级
# 查询研发部所有员工的信息及工资等级
select * from empt where empt.dept_id = (select id from dept where dept.name='研发部');
# 查询研发部员工的平均工资
select avg(salary) from empt where empt.dept_id = (select id from dept where dept.name='研发部');
# 查询工资比灭绝高的员工信息
select * from empt where salary > (select salary from empt where name='灭绝');
# 查询比平均薪资高的员工信息
select * from empt where salary >(select avg(salary) from empt);
# 查询低于本部门平均工资的员工信息
select avg(salary),dept_id from empt group by dept_id;
# 查询所有的部门信息,并统计部门的员工人数
# 查询所有学生的逃课情况,展示学生姓名,学号,课程名称

事务

简介

事务是一组操作的集合,是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败

默认MySQL的事务是自动提交的,即执行一条DML语句,MySQL会隐式提交事务

事务操作

sql 复制代码
# 查看事务提交方式,1表示自动,0表示手动提交
select @@autocommit;
# 设置事务提交方式为手动提交
set @@autocommit=0;
# 提交事务
commit;
# 回滚事务
rollback;
# 开启事务
start transaction;
# 或
begin;
# 若未设置事务为手动提交时,可以通过begin或start 来开启事务
# 通过commit提交事务,rollback回滚事务

四大特性

原子性(Atomicity)

事务是不可分割的最小操作单元,要么全部成功,要么全部失败

一致性(Consistency)

事务完成时,必须使所有的数据保持一致状态

隔离性(Isolation)

数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行

持久性(Durability)

事务一旦提交或回滚,对数据库中的数据的改变是永久的

并发事务问题

事务隔离级别

设置隔离级别

进阶

存储引擎

MySQL体系结构

连接层

最上层是一些客户端和连接服务,主要完成一些类似于连接处理,授权认证及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限

服务层

第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程,函数等

引擎层

存储引擎真正负责MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,可以根据需要选择合适的存储引擎。

存储层

主要讲数据存储在文件系统之上,并完成与存储引擎的交互

存储引擎简介

存储引擎是存储数据,建立索引,更新、查询数据等技术的实现方式。存储引擎是基于表的,不是基于库的,存储引擎被称为表类型

sql 复制代码
# 创建表时指定存储引擎
create table 表名(
)engine=innodb;
# 查看当前数据库支持的存储引擎
show engines;

存储引擎特点

InnoDB

InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL5.5之后,InnoDB是默认的MySQL存储引擎

特点

DML操作遵循ACID模型,支持事务;

行级锁,提高并发访问性能;

支持外键 foreign key约束,保证数据的完整性和正确性;

xxx.ibd:xxx代表表名,innoDB引擎的每张表都对应这样一个表空间,存储该表的表结构(frm,sdi),数据和索引

参数:innodb_file_per_table

MyISAM

MyISAM是MySQL早期的默认存储引擎

特点

不支持事务,不支持外键

支持表锁,不支持行锁

访问速度快

xxx.sdi:存储表结构信息

xxx.MYD:存储数据

xxx.MYI:存储索引

Memory

Memory引擎的表数据存储在内存中,由于受到硬件问题,或者断电问题的影响,只能将这些表作为临时表或者缓存使用

特点

内存开放,hash索引

xxx.sdi:存储表结构信息

存储引擎选择

索引

概述

索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

优点

提高数据检索效率,降低数据库的IO成本

通过索引列队数据进行排序,降低数据排序的成本,降低CPU的消耗

缺点

索引列占空间;

索引大大提高了查询效率,同时降低更新表的速度;

索引结构

B树(多路平衡查找树)

n阶B树,最多可以存储n-1个key,n个指针,最少存储n/2个key(根节点最少1个key)。B树非叶子节点存储数据

B+树

B+树非叶子节点不保存数据,且叶子节点之间会有一个单向链表

MySQL中的B+树

在经典B+树的基础上做了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的指针,形成了带有顺序指针的B+Tree,提高区间访问的性能。环形指针是为了分页查询使用,从最后一页跳转到首页时方便快速。

Hash

哈希索引只支持判等查询,不支持范围查询,无法利用索引进行排序,查询效率通常高于B+树

在MySQL中,Memory支持哈希索引,InnoDB支持自适应哈希

索引分类

根据索引是否具有唯一性分类

主键索引中不允许有空值,唯一索引中允许有空值,且可以有多个空值

因为空值在判等操作中结果为unknown,因此主键索引无法根据空值判断是否找到元素

根据数据是否保存在索引中分类

有主键索引,主键是聚簇索引

否则,有唯一索引,唯一索引的第一个索引为聚簇索引

否则,InnoDB自动生成rowId作为隐藏聚簇索引

索引语法

sql 复制代码
# 创建索引
create [unique|fulltext] index index_name on table_name(col1,col2,...);
# 查看索引
show index from table_name;
# 删除索引
drop index index_name on table_name;

SQL性能分析

执行频率分析

sql 复制代码
# SQL执行频率 七个_
# 显示增删改查频率 
show [global|session] status like 'com_______';

慢查询日志

MySQL慢查询日志默认没有开启,在配置文件my.cnf中配置

bash 复制代码
# 打开慢查询日志开关 1 on 0 off
slow_query_log=1
# 单位是秒,查询时间超过2秒的
long_query_time=2

配置完成后,查看慢查询日志中记录的信息 /var/lib/mysql/localhost-slow.log

profile

查看所有SQL执行详细时间

sql 复制代码
# 查看每条SQL的耗时基本情况
show profiles;
# 查看指定query id的SQL语句各阶段耗时情况
show profile for query query_id;
# 查看指定query_id 的SQL语句CPU的使用情况
show profile cpu for query query_id;

explain/desc执行计划

explain执行计划各字段含义:

  • id:select的查询序列号,表示查询先后顺序,序号越大越先执行,同序号自上往下执行
  • 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:表示返回结果占读取行数的比值,越大越好,最大为1

索引使用

最左前缀法则

如果索引了多列,要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效。

范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效

索引列运算

不要在索引列上进行运算操作,索引将失效

字符串不加引号

字符串类型字段使用时,不加引号,索引会失效,此时相当于在索引列上加了函数运算

模糊查询

尾部模糊查询,索引不会失效,头部模糊查询,索引失效

or连接条件

or分割的条件,只要有一个没有索引,则所有的索引不会用上

数据分布影响

如果MySQL评估使用索引比不使用索引慢,则不使用索引

SQL提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些认为的提示来达到优化操作的目的

sql 复制代码
# use index 建议使用索引,使用与否MySQL自己决定
select * from table_name use index(index_name) where condition;
# ignore index 忽略索引
select * from table_name ignore index(index_name) where condition;
# force index 强制使用索引
select * from table_name force index(index_name) where condition;

覆盖索引

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中全部找到),减少使用select *

前缀索引

当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,这会让索引变大,查询时,浪费大量磁盘IO,影响查询效率。此时可以将字符串的一部分前缀,建立索引,可以大大节约索引空间,从而提高索引效率

sql 复制代码
create index index_name on table_name(column(n));

前缀长度可以根据索引的选择性来决定,而选择性是指不重复的索引值和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

sql 复制代码
select count(distinct email)/count(*) from tb_name;
select count(distinct substring(email,1,5))/count(*) from tb_name;

索引设计原则

SQL优化

插入数据

insert优化

sql 复制代码
#批量插入,建议插入数量在500-1000条
insert into tb_name values(),()...,();

手动提交事务

sql 复制代码
start transaction;
insert into tb_name values(),(),(),...();
commit;

主键顺序插入

大批量插入数据

主键优化

页分裂

页可以为空,也可以填充一般,也可以填充100%。每个页包含2-N行数据,根据主键排列。

主键乱序插入时,会发生页分裂现象,即将一页数据分成两个半页数据,然后将待插入数据放入合适的位置,再重新连接这两个页。

页合并

当删除一行记录时,时间上记录没有被物理删除,只是记录被标记位删除并且它的空间变得允许被其他记录声明使用。

当页中删除的记录达到MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或者后)看看是否可以将两个页合并以优化空间使用。

主键设计原则

  • 满足业务需求的情况下,尽量降低主键的长度
  • 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键
  • 尽量不要使用UUID做主键或者其他自然主键,如身份证号
  • 业务操作时,避免对主键的修改

order-by 优化

  • using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫filesort排序
  • using index:通过有序索引顺序扫描直接返回有序数据,这种即为using index,不需要额外排序,操作效率高
sql 复制代码
# 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
# 尽量使用覆盖索引
# 多字段排序,一个升序,一个降序,此时需要注意联合索引在创建时的规则
# 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区的大小sort_buffer_size(默认256k)

group by 优化

在分组操作时,可以通过索引来提高效率

分组操作时,索引的使用也是满足最左前缀法则的

limit 优化

一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化

count 优化

MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)时会直接返回个数,效率很高;

InnoDB引擎就麻烦了,它执行count(*)的时候,需要把数据一行一行地从引擎里读出来,然后累积计数

count的几种用法

sql 复制代码
# InnoDB引擎会遍历整张表,把每一行的主键id值取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)
count(主键)
# 没有not null约束:InnoDB引擎会遍历整张表,把每一行的字段值取出来,返回给服务层,服务层判断是否为null,不为null,累加
# 有not null约束:InnoDB引擎遍历整张表,把每一行的字段值取出来,返回给服务层,直接按行进行累加
count(字段)
# InnoDB引擎遍历整张表,不取值。服务层对于返回的每一行,放一个数字1进去,直接按行累加
count(1)
#InnoDB引擎不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加
count(*)
# count(1)和count(*)性能一样,都是选择最小的二级索引,将索引页加载到内存的缓冲池中,再统计行数

update 优化

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁

视图、存储器、触发器

视图

基本用法

创建

sql 复制代码
create [or replace] view 视图名称 as select语句 [with [CASCADED|LOCAL] CHECK OPTION];

查询

sql 复制代码
# 查看创建视图工具
show create view 视图名称;
# 查看视图数据
select * from 视图名称;

修改

sql 复制代码
alter view 视图名称 as select语句 [with [CASCADED|LOCAL] CHECK OPTION];

删除

sql 复制代码
drop view [if exists] 视图名称 [,视图名称];
检查选项

当使用with check option子句创建视图时,MySQL会通过视图检查正在修改的每一行,如插入,更新,删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,MySQL提供了两个选项CASCADEDLOCAL,默认值为CASCADED

LOCAL是递归检查CHECK OPTIONCASCADED是级联检查,无论底层视图是否有CHECK OPTION,都会检查是否满足条件。

更新条件

要使视图可更新,视图中的行和基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则该视图不可更新:

  1. 聚合函数或窗口函数(sum,min,max,count
  2. distinct
  3. group by
  4. having
  5. union或者union all
作用

简单

视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件

安全

数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据

数据独立

视图可帮助用户屏蔽真实表结构变化带来的影响

存储过程

存储过程是事先经过编译并存储在数据库中的一段SQL语句集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率有好处

存储过程思想上很简单,就是数据库SQL语言层面的代码封装和重用

特点

封装,复用,数据安全,提升效率

基本语法

创建

sql 复制代码
create procedure 名称([参数列表])
begin
--SQL语句
end;

调用

sql 复制代码
call 名称([参数]);

查看

sql 复制代码
# 查看指定数据库的存储过程及状态信息
select * from information_schema.routines where routine_schema='xxx';
show create procedure 名称;

删除

sql 复制代码
drop procedure [if exists] 名称;
变量
系统变量

MySQL服务器提供,非用户自定义,属于服务器层面,分全局和会话变量

查看系统变量

sql 复制代码
show [session|global] variables;
show [session|global] variables like '....';
select @@[session.global].变量名;

设置系统变量

sql 复制代码
set [session|global].系统变量名=值;
set @@[session|global].系统变量名=值;
自定义变量

赋值

sql 复制代码
#自定义变量不用初始化,初始值为null
set @var_name=expr;
set @var_name:=expr;
select @var_name:=expr;
select 字段名 into @var_name from tb_name;

使用

sql 复制代码
select @var_name;
局部变量

可用作存储过程内的局部变量和输入参数,局部变量的范围在其内声明的begin...end

声明

sql 复制代码
declare var_name 类型 [defalut ...];

赋值

sql 复制代码
set var_name = val;
set var_name := val;
select col_name into var_name from tb_name where condition;
if 判断

语法

sql 复制代码
if condition1 then
...
elseif condition2 then
...
else
...
end if;
参数

语法

sql 复制代码
create procedure p_name([in/out/inout] 参数名 参数类型)
begin
end;
case

语法

sql 复制代码
case case_value
	when v1 then s1;
	when v2 then s2;
	else s3;
end case;
# 或
case 
	when c1 then s1;
	when c2 then s2;
	esle s3;
end case;
while

语法

sql 复制代码
while condition do
	SQL语句
end while;
repeat

语法

sql 复制代码
repeat
 SQL逻辑;
 until condition;
end repeat;
loop

loop实现简单循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。loop可以配合一下两个语句使用:

  • leave:配合循环使用,退出循环
  • iterate:必须在循环中使用,作用是跳出本次循环,直接进入下一次循环
sql 复制代码
label:loop
if condition then
leave label;
end if;
SQL逻辑
end loop label;
游标

游标(cursor)是用来存储查询结果集的数据类型,类似于集合,在存储过程和函数中可以使用游标对结果集进行循环处理,游标的使用包括游标的声明,open,fetch和close,其语法如下

sql 复制代码
# 声明游标
declare cursor_name cursor for select语句;
# 打开游标
open cursor_name;
# 使用游标
fetch cursor_name into v1,v2,...;
# 关闭游标
close cursor_name;
条件处理程序

用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体语法如下:

sql 复制代码
declare handle_action handler for condition_value[,condition_value] statement;
# handler_action:
# 	continue 继续执行当前程序
# 	exit 终止执行当前程序
# condition_value
# 	SQLSTATE sqlstate_value:状态码,如02000
# 	SQLWARNING: 所有以01开头的SQLSTATE代码的简写
#	NOT FOUND: 所有以02开头的SQLSTATE代码简写
# 	SQLEXCEPTION: 	所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码的简写

存储函数

sql 复制代码
create function f_name([参数列表])
returns type [characteristic ...]
begin
 SQL
 return ...;
end;
# characteristic:
# 	deterministic:相同的输入参数总是产生相同的记过
# 	no sql:不包含SQL语句
# 	reads sql data:包含读取数据的语句,但不包含写入数据的语句

触发器

触发器是与表有关的数据库对象,指在insert/update/delete之前或之后触发,触发并执行触发器中定义的SQL语句集合。触发器可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。

使用别名OLD和NEW来引用触发器中发生变化的记录内容

语法

sql 复制代码
# 创建
create trigger trigger_name
before/after insert/update/delete
on tb_name for each row
begin
SQL语句
end;
# 查看
show triggers;
# 删除
drop trigger [schema_name.]trigger_name;

全局锁

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML语句,DDL语句,以及更新操作的事务提交语句都将被阻塞

典型应用场景时做全库的逻辑备份,对所有的表进行锁定,从而获得一致性视图,保证数据的完整性

sql 复制代码
# 加锁
flush tables with read lock;
# 解锁
unlock tables;

特点

数据库加全局锁,是一个比较重的操作,存在以下问题:

  • 如果主库备份,则备份期间不能进行更新操作,业务停摆
  • 如果从库备份,无法执行主库同步过来的二进制日志,会导致主从延迟

在InnoDB引擎中,可以在备份时加上参数--single-transaction参数来完成不加锁的一致性数据备份

这里利用了MVCC快照读,备份时开启事务,将会话的事务隔离级别设置成可重复读

表级锁

表锁

表锁分为共享读锁和独占写锁

sql 复制代码
# 加锁
lock tables 表名,... read/write;
# 解锁
unlock tables;//或者客户端断开连接
元数据锁(Meta Data Lock,MDL)

元数据锁加锁过程是系统自动控制的,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。其实是为了保证DDL语句的合法性,即在修改表的结构时,不能执行DML语句,在执行DML语句时,也不能执行DDL语句

意向锁

为了避免DML在执行时,加的行锁和表锁冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查

sql 复制代码
# 意向共享锁(IS) 与表读锁兼容
select ... lock in share mode;
# 意向排他锁(IX) 与表读锁和写锁互斥,意向锁之间不会互斥,因为意向锁是标记当前表中是否有行锁,如A行读锁,B行写锁,则同时存在IS和IX,不会互斥
insert,update,delete,select ... for update;

行级锁

每次操作锁定对应的行数据,锁粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。

InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。行级锁分为以下三类:

  1. 行锁:锁住单行记录,防止其他事务对此行进行update和delete,在RC和RR隔离级别下支持
  2. 间隙锁:锁住索引记录间隙(不包含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持
  3. 临键锁:行锁和间隙锁的结合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。
行锁

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁

排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁

默认情况下, InnoDB在RR事务隔离级别下运行,InnoDB使用next-key锁进行搜索和索引扫描,防止幻读。

  1. 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,会自动优化成行锁
  2. InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时升级为表锁
间隙锁
  1. 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁
  2. 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key锁退化为间隙锁
  3. 索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止

间隙锁的唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。

InnoDB引擎

逻辑存储结构

架构

内存架构
磁盘架构
后台线程

事务原理

MVCC

基本概念

MySQL管理

系统数据库

常用工具

运维

日志

错误日志

错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。

该日志默认是开启的,默认存放目录/var/log,默认的日志文件名为mysqld.log。查看日志位置:

sql 复制代码
show variables like '%log_error%';

错误日志示范

bash 复制代码
# 这条语句有警告,binlog日志采用statement格式存储,而由于在删除数据时使用了limit,会导致随机删除1000条数据,如果从库执行这条语句,可能导致与主库删除的数据不一致,导致主从不一致
2026-07-02T08:20:25.176221Z 8 [Warning] [MY-010908] [Server] Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. Statement: DELETE FROM his_config_info WHERE gmt_modified < '2026-06-02 16:20:25' LIMIT 1000

配置错误日志路径

bash 复制代码
#在mysqld下配置错误日志路径
[mysqld]
log_error=/var/log/mysqld.log

二进制日志

二进制日志(binlog)记录了所有的DDL语句和DML语句,但不包括数据查询语句(select,show)。

作用

  1. 灾难时的数据恢复
  2. MySQL的主从复制

日志格式

sql 复制代码
# statement 执行同样的语句存在数据不一致的风险
# row 安全
# mixed 系统判断语句是否安全,不安全使用row形式保存

日志查看

日志删除

对于比较繁忙的业务系统,每天生成的binlog数据巨大,如果长时间不清除,将会占用大量磁盘空间。可以通过以下几种方式清理日志:

也可以在mysql的配置文件中配置二进制日志的过期时间,设置后,二进制日志过期会自动删除

sql 复制代码
show variables like '%binlog_expire_logs_seconds%';

查询日志

查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的。如果需要开启查询日志,可以设置一下配置:

sql 复制代码
#是否开启查询日志 0 关闭 1开启
general_log=1
# 设置日志文件名,默认是host_name.log
general_log_file=mysql_query.log

慢查询日志

慢查询日志记录了所有执行时间超过参数long_query_time设置值并扫描记录数不小于min_examined_row_limit的所有SQL语句的日志,默认未开启。long_query_time默认是10秒,最小为0,精度可以到微秒。

bash 复制代码
# 慢查询日志
slow_query_log=1
# 执行时间参数
long_query_time=2

默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。可以使用log_slow_admin_statementslog_queries_not_using_indexes。这里是指只要满足不使用索引查找就记录下来,不用考虑是否满足long_query_time这个参数。

sql 复制代码
# 记录执行较慢的管理语句
log_slow_admin_statements=1
# 记录执行较慢的未使用索引的语句
log_queries_not_using_indexes=1

主从复制

概述

主从复制是指主数据库的DDL和DML操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。

MySQL支持一台主库同时向多态从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。

优点

  1. 主库出现问题,可以快速切换到从库提供服务
  2. 实现读写分离,降低主库的访问压力
  3. 可以在从库中执行备份,以避免备份期间影响主库服务

原理

  1. Master主库在事务提交时,会把数据变更记录在二进制日志文件Binlog中
  2. 从库读取主库的二进制日志文件Binlog,写入到从库的中继日志Relay log中
  3. slave重做中继日志中的时间,将改变反映到它自己的数据

搭建

主库配置

1.修改my.cnf配置文件

bash 复制代码
[mysqld]
#设置服务id,int型整数
server-id=1
# 设置是否仅读取操作
read-only=0
# 开启二进制日志
log-bin=mysql-bin

2.重启MySQL服务

3.登录MySQL,创建远程连接账号,并授予主从复制权限

这里是设置主从复制的权限,当从库携带该用户名和密码来拷贝主库数据时,主库予以确认,这个与客户端连接服务器,进行增删改查没有联系

sql 复制代码
# MySQL 9.x以下版本
create user 'xht'@'%' identified with mysql_native_password by '123456';
grant replication slave on *.* to 'xht'@'%';

4.通过指令,查看二进制日志坐标

sql 复制代码
#如果没有开启二进制日志文件,要先开启 SHOW VARIABLES LIKE 'log_bin'; 查看开启与否
show master status;
# 或MySQL 9.x版本
show binary log status;
# 字段说明
# file 从哪个日志文件开始推送日志
# position 从哪个位置开始推送日志
# binlog_ignore_db:指定不需要同步的数据库

从库配置

1.修改配置文件my.cnf

powershell 复制代码
[mysqld]
server-id=2
#只读 普通用户只读
read-only=1 
#超级管理员也只读
super-read-only=1

2.重启服务

3.登录MySQL,设置主库配置

sql 复制代码
# mysql 8.+
change replication source to source_host='mysql-net',source_user='xht',source_password='123456',source_log_file='mysql-bin.000004',source_log_pos=688; 
# mysql 5.7
CHANGE MASTER TO 
    MASTER_HOST='mysql-dn1',
    MASTER_USER='xht',
    MASTER_PASSWORD='123456',
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=154;

4.开启同步操作

sql 复制代码
# mysql 8.+
start replica;
# mysql 5.7
start slave;

5.查看主从同步状态

sql 复制代码
# mysql8.+
show replica status;
# mysql 5.7
show slave status;

分库分表

问题

随着互联网及移动互联网的发展,应用系统的数据量在成指数式增长,若采用单数据库进行数据存储,存在以下性能瓶颈:

  1. IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较低。请求数据太多,带宽不够,网络IO瓶颈
  2. CPU瓶颈:排序,分组,连接查询,聚合统计等SQL会耗费大量的CPU资源,请求数太多,CPU出现瓶颈

拆分策略

垂直拆分

垂直分库

以表为依据,根据业务将不同表拆分到不同库中

垂直分表

以字段为依据,根据字段属性将不同字段拆分到不同表中

水平拆分

** 水平分库**

以字段为依据,按照一定策略,将一个库的数据拆分到多个库中

水平分表

以字段为依据,按照一定策略,将一个表的数据拆分到多个表中

实现技术

  • shardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析,改写,路由处理。需要自行编码配置实现,只支持java语言,性能较高
  • MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不及前者

MyCat

概念介绍

绿色部分是MyCat中涉及到的逻辑概念,物理结构就是实际的MySQL服务器

入门

schema.xml

xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
        <!--数据库信息-->
        <schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
                <!--表的分片信息-->
                <table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
        </schema>
        <!--分片节点信息-->
        <dataNode name="dn1" dataHost="dhost1" database="db01" />
        <dataNode name="dn2" dataHost="dhost2" database="db01" />
        <dataNode name="dn3" dataHost="dhost3" database="db01" />
        <!--分片详细信息-->
        <dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn1:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn2:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn3:3306" user="root" password="123456" />
        </dataHost>
</mycat:schema>

server.xml

xml 复制代码
                <property name="memoryPageSize">1m</property>

                <!--
                        单位为k
                -->
                <property name="spillsFileBufferSize">1k</property>

                <property name="useStreamOutput">0</property>

                <!--
                        单位为m
                -->
                <property name="systemReserveMemorySize">384m</property>


                <!--是否采用zookeeper协调切换  -->
                <property name="useZKSwitch">false</property>


        </system>

        <!--配置登录mycat的用户名密码-->
        <user name="root">
                <property name="password">123456</property>
                <property name="schemas">DB01</property>
        </user>

        <user name="user">
                <property name="password">user</property>
                <property name="schemas">DB01</property>
                <property name="readOnly">true</property>
        </user>

autopartition-long.txt

bash 复制代码
# range start-end ,data node index
# K=1000,M=10000.
#id在0-5000000放入0
0-500M=0
500M-1000M=1
1000M-1500M=2
配置

schema.xml

schema标签用于定义MyCat实例中的逻辑库,一个MyCat实例中,可以有很多逻辑库,通过schema标签来划分不同的逻辑库

table标签定义了MyCat中逻辑库schema下的逻辑表

xml 复制代码
        <!--name:指定自定义的逻辑库名-->
        <!--checkSQLschema:SQL语句操作时制定了数据库名称,执行时是否自动去除:true自动去除,要明白这里的DB01与实际数据库中的db01不相等,mycat把sql原样发出去后,找不到DB01.xxx的数据表,因此会报错;mysql中数据库是区分大小写的,即DB01 != db01-->
        <!--sqlMaxLimit:如果未指定limit进行查询,列表查询模式查询多少条记录-->
        <schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
                <!--name:定义逻辑表名-->
                <!--dataNode:定义逻辑表所属的dataNode,多个dataNode用逗号分开-->
                <!--rule:分片规则,分片规则在rule.xml里定义-->
                <!--primaryKey:逻辑表对应真实表中的主键-->
                <!--type:逻辑表类型,目前有全局表和普通标,默认普通表,全局表为global-->
                <table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
        </schema>

dataNode标签定义了MyCat中的数据节点,即数据分片,一个dataNode标签是一个独立的数据分片

xml 复制代码
        <!--name:定义数据节点名称-->
        <!--dataHost:数据库实例主机名称,引用自dataHost标签中的name属性-->
        <!--database:定义分片所属的数据库,mysql服务器上的物理数据库-->
        <dataNode name="dn1" dataHost="dhost1" database="db01" />
        <dataNode name="dn2" dataHost="dhost2" database="db01" />
        <dataNode name="dn3" dataHost="dhost3" database="db01" />

dataHost标签在MyCat逻辑库中作为底层标签存在,直接定义了具体的数据库实例,读写分离,心跳语句

xml 复制代码
        <!--name:唯一标识,供上层标签使用-->
        <!--maxCon/minCon:最大最小连接数-->
        <!--balance:负载均衡策略,取值0,1,2,3-->
        <!--writeType:写操作分发方式(0:写操作转发到第一个writeHost,第一个挂了切第二个;1:写操作随机分发到配置的writeHost)-->
        <!--dbDriver:数据库驱动,支持native,jdbc-->
        <dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn1:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn2:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn3:3306" user="root" password="123456" />
        </dataHost>

rule.xml

rule.xml中定义所有拆分表的规则,在使用中可以灵活使用分片算法,或者对同一个算使用不同参数,让分片过程可配置化。主要包含两类标签:tableRule,Function

server.xml

xml 复制代码
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
	<tableRule name="auto-sharding-long">
		<rule>
			<columns>id</columns>
			<algorithm>rang-long</algorithm>
		</rule>
	</tableRule>
	
	<function name="rang-long"
		class="io.mycat.route.function.AutoPartitionByLong">
		<property name="mapFile">autopartition-long.txt</property>
	</function>
	
</mycat:rule>

server.xml

user标签配置MyCat的登录信息

xml 复制代码
        <!--name:用户名-->
        <user name="root">
                <!--密码-->
                <property name="password">123456</property>
                <!--可访问的逻辑数据库,多个用逗号隔开-->
                <property name="schemas">DB01</property>

                <!-- 表级 DML 权限设置 -->
                <!--
                是否开启DML权限检查,默认为false
                <privileges check="false">
                        <schema name="TESTDB" dml="0110" >
                                配置指定逻辑库的权限  对应增删改查
                                <table name="tb01" dml="0000"></table>
                                <table name="tb02" dml="1111"></table>
                        </schema>
                </privileges>
                 -->
        </user>

        <user name="user">
                <property name="password">user</property>
                <property name="schemas">DB01</property>
                <!--是否只读,默认为false-->
                <property name="readOnly">true</property>
        </user>
</mycat:server>
分片

垂直拆分

垂直分库将同一个库中的多个表根据业务拆分到不同的数据库上

schema.xml

xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
                <table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
        </schema>
        <!--垂直拆分的数据库-->
        <schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100">
                <!--垂直拆分不需要分片规则-->
                <table name="tb_goods_base" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_brand" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_cart" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_desc" dataNode="dn1_shopping" primaryKey="goods_id" />
                <table name="tb_goods_item" dataNode="dn1_shopping" primaryKey="id" />

                <table name="tb_order_item" dataNode="dn2_shopping" primaryKey="id" />
                <table name="tb_order_master" dataNode="dn2_shopping" primaryKey="order_id" />
                <table name="tb_order_pay_log" dataNode="dn2_shopping" primaryKey="out_trade_no" />

                <table name="tb_user" dataNode="dn3_shopping" primaryKey="id" />
                <table name="tb_user_address" dataNode="dn3_shopping" primaryKey="id" />
                <table name="tb_areas_provinces" dataNode="dn3_shopping" primaryKey="id" />
                <table name="tb_areas_city" dataNode="dn3_shopping" primaryKey="id" />
                <table name="tb_areas_region" dataNode="dn3_shopping" primaryKey="id" />
        </schema>
        <dataNode name="dn1" dataHost="dhost1" database="db01" />
        <dataNode name="dn2" dataHost="dhost2" database="db01" />
        <dataNode name="dn3" dataHost="dhost3" database="db01" />
        <!--指定垂直拆分的分片节点及数据库-->
        <dataNode name="dn1_shopping" dataHost="dhost1" database="shopping" />
        <dataNode name="dn2_shopping" dataHost="dhost2" database="shopping" />
        <dataNode name="dn3_shopping" dataHost="dhost3" database="shopping" />
        <dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn1:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn2:3306" user="root" password="123456" />
        </dataHost>
        <dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master" url="mysql-dn3:3306" user="root" password="123456" />
        </dataHost>
</mycat:schema>

server.xml

xml 复制代码
        <user name="root">
                <property name="password">123456</property>
                <!--指定可以访问的数据库-->
                <property name="schemas">DB01,SHOPPING</property>

                <!-- 表级 DML 权限设置 -->
                <!--
                <privileges check="false">
                        <schema name="TESTDB" dml="0110" >
                                <table name="tb01" dml="0000"></table>
                                <table name="tb02" dml="1111"></table>
                        </schema>
                </privileges>
                 -->
        </user>

        <user name="user">
                <property name="password">user</property>
                <property name="schemas">DB01</property>
                <property name="readOnly">true</property>
        </user>

创建数据表时建议在MySQL服务器上创建

全局表

联表查询时,不能跨服务器查询,因此,需要将联合的表定义为全局表,保证每个分片上的表数据一致

xml 复制代码
        <schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100">
                <table name="tb_goods_base" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_brand" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_cart" dataNode="dn1_shopping" primaryKey="id" />
                <table name="tb_goods_desc" dataNode="dn1_shopping" primaryKey="goods_id" />
                <table name="tb_goods_item" dataNode="dn1_shopping" primaryKey="id" />

                <table name="tb_order_item" dataNode="dn2_shopping" primaryKey="id" />
                <table name="tb_order_master" dataNode="dn2_shopping" primaryKey="order_id" />
                <table name="tb_order_pay_log" dataNode="dn2_shopping" primaryKey="out_trade_no" />

                <table name="tb_user" dataNode="dn3_shopping" primaryKey="id" />
                <table name="tb_user_address" dataNode="dn3_shopping" primaryKey="id" />
                <!--这两张表配置成全局表-->
                <table name="tb_areas_provinces" dataNode="dn1_shopping,dn2_shopping,dn3_shopping" primaryKey="id" type="global" />
                <table name="tb_areas_city" dataNode="dn1_shopping,dn2_shopping,dn3_shopping" primaryKey="id" type="global"/>
                <table name="tb_areas_region" dataNode="dn1_shopping,dn2_shopping,dn3_shopping" primaryKey="id" type="global"/>
        </schema>

水平拆分

跟垂直拆分类似,不过需要配置分片规则,这里不再过多描述

分片规则

范围分片

根据指定字段及其配置的范围与数据节点对应情况,决定该数据属于哪个分片

取模

根据指定的字段值与节点数量进行求模运算,根据运算结果,决定该数据属于哪个分片

一致性hash

一致性哈希,相同的哈希因子计算值总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置,分区节点增加会影响到部分数据迁移

枚举

通过在配置文件中配置可能的枚举值,指定数据分布到不同数据节点上,本规则适用于按照省份、性别、状态拆分数据等业务

应用指定

运行阶段由应用自主决定路由到哪个分片,直接根据字符子串(必须是数字)计算分片号

固定分片hash算法

该算法类似于十进制的求模运算,但是为二进制的操作,例如,取id的二进制低10位与1111111111进行位&运算

特点

  • 此算法会将连续的值分配到相同的分片,降低事务处理的难度
  • 可以均匀分配,也可以非均匀分配
  • 分片字段必须为数字类型

    这里的partitionCountpartitionLength要结合起来看,2个长度为256的分片长度,和1个512的分片长度
    字符串hash解析
    截取字符串中的指定位置的子字符串,进行hash算法,算出分片

    按(天)日期分片

    这里如果超过了分片配置的时间范围,会按照sPartionDay这个规则来进行分片
    自然月
MyCat管理与监控

读写分离

读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力

通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle和SQL Server

一主一从

MySQL的主从复制,是基于二进制文件bin-log实现的

主从读写分离

MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件dataHost标签的balance属性控制

xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
        <schema name="TEST_RW" checkSQLschema="true" sqlMaxLimit="100" dataNode='dn1' />
        <dataNode name="dn1" dataHost="dhost1" database="db01" />
        <dataHost name="dhost1" maxCon="1000" minCon="10" balance="1"
                        dbType="mysql" dbDriver="native"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master1" url="master1:3306" user="root" password="123456">
                       <readHost host="slave1" url="slave1:3306" user="root" password="123456" />
                </writeHost>
        </dataHost>
</mycat:schema>

负载均衡策略

如果master主数据库服务器挂了,那么slave从数据库服务器也无法被MyCat访问

双主双从

一个主机master1用于处理所有写请求,它的从机slave1和另一台主机master2还有它的从机slave2负责所有读请求。当master1主机宕机后,master2主机负责写请求,master1,master2互为备机。

搭建

双主服务器配置

xml 复制代码
[mysqld]
# 每台服务器的id要不一样
server-id=1
# 指定同步的数据库
binlog-do-db=db01
binlog-do-db=db02
# 当前主库作为从数据库时,有写入操作也要更新二进制文件
log-slave-updates
# 打开写入二进制文件开关
log-bin=mysql_bin
sql 复制代码
# 创建xht用户,并设置密码,该用户可在任意主机连接该MySQL服务
create user 'xht'@'%' identified with mysql_native_password by '123456';
# 为xht用户添加主从复制权限
grant replication slave on *.* to 'xht'@'%';

# 查看主库二进制日志坐标,不打开二进制开关的话看不到任何信息
show master status;

双从服务器配置

xml 复制代码
[mysqld]
# 每台服务器的id要不一样
server-id=2
sql 复制代码
# 设置从库上连接主库进行主从复制的用户和日志信息
# 这里为保证两个主数据库之间也能相互复制,也要设置这些信息
change master to master_host='ip',master_user='xht',master_password='123456',master_log_file='xxx',master_log_pos=123;
# 启动从库复制功能
start slave;
show slave status;
双主双从读写分离
xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
        <schema name="TEST_RW2" checkSQLschema="true" sqlMaxLimit="100" dataNode='dn1' />
        <dataNode name="dn1" dataHost="dhost1" database="db01" />
        <dataHost name="dhost1" maxCon="1000" minCon="10" balance="1"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="master1" url="master1:3306" user="root" password="123456">
                       <readHost host="slave1" url="slave1:3306" user="root" password="123456" />
                </writeHost>
                <writeHost host="master2" url="master2:3306" user="root" password="123456">
                       <readHost host="slave2" url="slave2:3306" user="root" password="123456" />
                </writeHost>
        </dataHost>
</mycat:schema>

master1宕机后,master2会接收写请求,此时slave1不可访问,slave2接收读请求

相关推荐
@Mike@1 小时前
07-数据库学习笔记(数据库哈希表)
数据库·哈希算法·散列表
wear工程师1 小时前
MySQL 分页为什么会重复数据:补上 id 还不够
sql·mysql
喜欢的名字被抢了1 小时前
FastAPI 接入异步 PostgreSQL 完成任务 CRUD 与数据库迁移
数据库·postgresql·fastapi
Goodbye2 小时前
给 AI 装上记忆:基于 Milvus 向量数据库与 RAG 的智能日记系统实战
数据库
九皇叔叔2 小时前
RHEL 9.8 安装 Redis 8.8.1
数据库·redis·bootstrap
Python私教2 小时前
如意 Django CRM 容器化实战:后端、前端、数据库、Redis 的协同启动逻辑
前端·数据库·django
小张同学a.2 小时前
zabbix企业级监控平台3——服务监控与API实战
linux·运维·mysql·nginx·tomcat·zabbix
九皇叔叔2 小时前
RHEL9 安装 MySQL 8.4.11.LTS 版本
android·mysql·adb
海兰2 小时前
【数据库】tdsql(MySQL )的事务隔离级别
android·数据库·mysql