Mysql(继续更新)

INnoDB   三特性   事务   外键    行级锁(开启事务时,查询后加FOR UPDATE)

MySQL 使用 InnoDB,在 默认隔离级别 —— REPEATABLE READ(可重复读) 下  开启事务,执行 UPDATE 时默认会加行锁  只要事务没有提交  这条数据会锁住

Mysql概述

数据库(DataBase),简称:DB.存储数据的仓库,数据是有组织的进行存储

数据库管理系统(DataBase Management System),简称:DBMS.操作和管理数据库的大型软件

SQL(Structured Query Language):操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准

Windows中Mysql的启动命令

net start mysql80

net stop mysql80

Mysql客户端连接

mysql [-h 127.0.0.1] [-P 3306] -u root -p

系统管理类的 SQL 命令 元数据查询

查询所有数据库:SHOW DATABASES;

查询当前使用的数据库: SELECT DATABSES();

  • SHOW DATABASES;:查看所有数据库

  • SHOW TABLES;:查看当前数据库中的所有表

  • SHOW COLUMNS FROM 表名;:查看表结构

  • SHOW INDEX FROM 表名;:查看索引

  • SHOW GRANTS FOR 用户;:查看权限

查询当前数据库所有表

SHOW TABLES;

查询表结构

DESC 表名;

查询指定表的建表语句

SHOW CREATE TABLE 表名

SQL分类

DDL(Data Definition Language):数据定义语言,用来定义数据库对象(数据库,表,字段)

常用关键字

  • CREATE:创建数据库、表、视图等

  • DROP:删除数据库、表、视图等

  • ALTER:修改表结构(增加/删除列、修改列类型等)

  • TRUNCATE:清空表数据(比 DELETE 快,不可回滚)

  • RENAME:重命名数据库对象

创建数据库:

CREATE DATABASES [ IF NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [ COLLATE 排序规则];

删除数据库

DORP DATABASE [IF EXISTS] 数据库名;

使用数据库

USE 数据库名

例如

create database itcast;

创建表

CREATE TABLE [IF NOT EXISTS] 表名(

        字段1 字段1类型 [COMMENT 字段1注释],

        字段2 字段2类型 [COMMENT 字段2注释]

)[COMMENT 表注释]

例如:

CREATE TABLE tb_user(id int comment '编号',name varchar(50) comment '姓名',age int comment '年龄',gender varchar(1) comment '性别') comment '用户表';

查看表结构:DESC tb_user;

查看指定表的建表语句:SHOW CREATE TABLE tb_user;

Mysql的数据类型
1.数值类型

有符号:指出现负数的范围    无符号:指没有负数的范围

DECIMAL(10, 2) 第一个参数10代表精度   第二个参数代表标度

精度表示最多可存储 10 位数字

标度表示其中 2 位是小数位

例如:12345678.11

默认情况下在Navicat中即使不显示指定精度和标度

针对于Decimal类型需要显示指定精度和标度  不然无法存小数

Doubel即使0,0也可以存小数  应该是Navicat的显示误区

例如你希望用double表示分数  可以用double(4,1) 最多4位  小数点后保留1位

默认都是有符号的

指定无符号示例

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    age TINYINT UNSIGNED,
    score INT,
    balance DECIMAL(10,2) UNSIGNED
);
 

2.字符串类型

3.日期时间类型

练习

CREATE TABLE emp (
    id INT COMMENT '编号',
    workno VARCHAR(10) COMMENT '工号',
    name VARCHAR(10) COMMENT '姓名',
    gender CHAR(1) COMMENT '性别',
    age TINYINT UNSIGNED COMMENT '年龄',
    idcard CHAR(18) COMMENT '身份证号',
    entrydate DATE COMMENT '入职时间'
) COMMENT='员工表';
 

DDL-表操作-修改和删除
添加字段

ALTER TABLE 表名 ADD 字段名 类型 [UNSIGNED] [NOT NULL] [DEFAULT 值] [COMMENT '说明'];

修改数据类型

ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度) [UNSIGNED] [NOT NULL | NULL] [DEFAULT 值] [COMMENT '注释'];

修改字段名和字段类型  注意用CHANGE的话必须写“旧字段名”和“新字段名”,哪怕名字不变!

ALTER TABLE 表名 
CHANGE 旧字段名 新字段名 新数据类型(长度) [UNSIGNED] [NOT NULL | NULL] [DEFAULT 值] [COMMENT '注释'];

删除字段

ALTER TBALE 表名 DROP 字段名;

修改表名

ALTER TABLE 表名 RENAME 新表名;

删除表

删除整张表(连结构都没了)

DROP TABLE [IF EXISTS] 表名;

清空表数据,但保留表结构  重置表的自增计数器(如果有)不是逐行删除(像 DELETE 那样)

对于大表,TRUNCATE 比 DELETE FROM 表名 快得多 DELETE是DML操作

TRUNCATE TABLE 表名;

DDL-数据库操作总结

SHOW DATABASES;                            -- 查看所有数据库
CREATE DATABASE 数据库名;                  -- 创建数据库
USE 数据库名;                              -- 切换当前数据库
SELECT DATABASE();                         -- 查看当前所用数据库
DROP DATABASE [IF EXISTS] 数据库名;        -- 删除数据库(推荐加 IF EXISTS)

DDL-表操作总结

SHOW TABLES;                               -- 查看当前数据库中所有表
CREATE TABLE 表名 (
    字段名 数据类型 [约束] [COMMENT '注释'],
    ...
);                                          -- 创建表

DESC 表名;                                  -- 查看表结构(字段+类型+约束)
SHOW CREATE TABLE 表名;                     -- 查看完整建表语句

-- 表结构变更(操作不能混用在一条 ALTER 中):
ALTER TABLE 表名 ADD 列名 类型(长度) ... ;         -- 添加字段
ALTER TABLE 表名 MODIFY 列名 新类型(长度) ... ;     -- 修改字段类型
ALTER TABLE 表名 CHANGE 旧名 新名 类型(长度) ... ;  -- 改字段名+类型
ALTER TABLE 表名 DROP COLUMN 列名;                -- 删除字段
ALTER TABLE 表名 RENAME TO 新表名;                 -- 修改表名

DROP TABLE [IF EXISTS] 表名;                      -- 删除表
 

DML(Data Manipulation Language):数据操作语言,用来对数据库表中的数据进行增删改

常用关键字

  • INSERT:插入数据

  • UPDATE:更新数据

  • DELETE:删除数据

添加数据INSERT

全量字段插入

INSERT INTO T_USER VALUES(1,"张三",18);

批量插入

INSERT INTO t_user VALUES(1,"张三",18),(2,"李四",19);

指定字段插入

INSERT INTO t_user(name,age)VALUES("王五",17),("赵六",19);

一些特殊用法

整张表数据复制

INSERT INTO 表B
SELECT * FROM 表A;
 

指定字段复制

INSERT INTO 表B(字段1, 字段2, ...)
SELECT 字段A1, 字段A2, ...
FROM 表A;

复制时加条件

INSERT INTO 表B
SELECT * FROM 表A
WHERE 条件;

跨数据库复制

INSERT INTO 新库.表B
SELECT * FROM 旧库.表A;
 

备份原表数据

CREATE TABLE emp_bak LIKE emp;
INSERT INTO emp_bak SELECT * FROM emp;

注意:插入数据时,指定字段顺序需要与值得顺序对应

字符串和日期型数据应该包含在引号中

插入的数据大小应该在字段的规定范围内
 

修改数据UPDATE

UPDATE 表名 SET 字段名1=值,字段名2=值2,...[WHERE 条件];

如果没有条件则修改整张表数据

删除数据DELETE

DELETE FROM 表名 [WHERE 条件];

DQL(Data Query Language):数据查询语言,用来查询数据库中表的记录

常用关键字

  • SELECT:查询数据

  • FROM:指定数据来源的表

  • WHERE:设置查询条件

  • GROUP BY:分组

  • HAVING:过滤分组后的结果

  • ORDER BY:排序

  • JOIN:多表连接查询

SELECT

        字段列表

FROM 

        表名列表

WHERE

        条件列表

GROUP BY

        分组字段列表

HAVING

        分组后条件列表

ORDER BY 

        排序字段列表

LIMIT

        分页参数

执行顺序

from --- join --- on --- where --- group by --- having --- select --- distinct --- order by ---- limit

CREATE TABLE emp (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '编号',
    workno VARCHAR(10) COMMENT '工号',
    name VARCHAR(10) COMMENT '姓名',
    gender CHAR(1) COMMENT '性别',
    age TINYINT UNSIGNED COMMENT '年龄',
    idcard CHAR(18) COMMENT '身份证号',
    workaddress VARCHAR(50) COMMENT '工作地址',
    entrydate DATE COMMENT '入职时间'
) COMMENT='员工表';

INSERT INTO emp (id, workno, name, gender, age, idcard, workaddress, entrydate)
VALUES
(1, '1', '柳岩', '女', 20, '123456789012345678', '北京', '2000-01-01'),
(2, '2', '张无忌', '男', 18, '123456789012345670', '北京', '2005-09-01'),
(3, '3', '韦一笑', '男', 38, '123456789712345670', '上海', '2005-08-01'),
(4, '4', '赵敏', '女', 18, '123456757123845670', '北京', '2009-12-01'),
(5, '5', '小昭', '女', 16, '123456789012345678', '上海', '2007-07-01'),
(6, '6', '杨逍', '男', 28, '12345678931234567X', '北京', '2006-01-01'),
(7, '7', '范遥', '男', 40, '123456789212345670', '北京', '2005-05-01'),
(8, '8', '黛绮丝', '女', 38, '123456157123465670', '天津', '2015-05-01'),
(9, '9', '范凉凉', '女', 45, '123156789012345678', '北京', '2010-04-01'),
(10, '10', '陈友谅', '男', 53, '123456789012345670', '上海', '2011-01-01'),
(11, '11', '张士诚', '男', 55, '12356789712345670', '江苏', '2015-05-01'),
(12, '12', '常遇春', '男', 32, '12346475712345670', '北京', '2004-02-01'),
(13, '13', '张三丰', '男', 88, '12365678712345678', '江苏', '2020-11-01'),
(14, '14', '灭绝', '女', 65, '123456791297123456', '西安', '2019-05-01'),
(15, '15', '胡青牛', '男', 70, '12345674971234567X', '西安', '2018-04-01'),
(16, '16', '周芷若', '女', NULL, '123456789012345670', '北京', '2012-06-01');
基本查询

SELECT 字段1,字段2,字段3,...FROM 表名;

SELECT * FROM 表名;

设置别名

SELECT 字段1 [AS 别名1],字段2 [AS 别名2],.... FROM 表名;

去除重复记录

SELECT DISTINCT 字段列表 FROM 表名;

基本查询练习

1.查询指定字段 name,workno,age

SELECT name,workno,age FROM emp;

2.查询所有字段

SELECT * FROM emp; #最好列出所有字段 不要用*

3.查询所有员工的工作地址,起别名

SELECT workaddress AS address FROM emp;

4.查询公司员工的上班地址(不要重复)

SELECT DISTINCT workaddress AS address FROM emp;

条件查询(WHERE)

SELECT 字段列表 FROM 表名 WHERE 条件列表;

条件:

条件查询练习

查询年龄等于88的员工

SELECT * FROM emp where age=88;

查询年龄小于20的员工信息

SELECT * FROM emp WHERE age<20;

查询年龄小于等于20的员工

SELECT * FROM emp WHERE age<=20;

查询没有身份证号的员工信息

SELECT * FROM emp WHERE idcard IS NULL; 

查询有身份证号的员工信息

SELECT * FROM emp WHERE idcard IS NOT NULL; 

查询年龄不等于88的员工信息

SELECT * FROM emp WHERE age!=88;

SELECT * FROM emp WHERE age<>88;

查询年龄在15岁(包含)到20岁(包含的)员工信息;

SELECT * FROM emp
WHERE age >= 15 AND age <= 20;

SELECT * FROM emp
WHERE age >= 15 &&age <= 20;

SELECT * FROM emp
WHERE age BETWEEN 15 AND 20;等价于age >= 15 AND age <= 20

注意:使用BETWEEN 参数1 AND 参数2  时候 参数1<=参数2

查询性别为女且年龄小于25岁员工信息

SELECT * FROM emp WHERE gender='女' AND age<25;

SELECT * FROM emp WHERE gender='女' && age<25;

查询年龄等于18或20或40的员工信息

SELECT * FROM emp WHERE age = 18 OR age=20 OR age=40;

SELECT * FROM emp WHERE age IN(18,20,40);

查询姓名为2个字的员工信息

SELECT * FROM emp WHERE name like '__'; #两个下划线   一个_代表一个字符

SELECT * FROM emp
WHERE CHAR_LENGTH(name) = 2;

查询身份证最后一位是X的员工信息

SELECT * FROM emp
WHERE idcard LIKE '%X';

SELECT * FROM emp
WHERE idcard LIKE '_________________X'; #前面写17个下划线也行

聚合函数(COUNT MAX MIN AVG SUM)

将一列数据做为一个整体,进行纵向计算

常见聚合函数

COUNT   统计数量

MAX        最大值

MIN          最小值

AVG         平均值

SUM        求和

语法:

SELECT 聚合函数(字段列表) FROM 表名;

SELECT COUNT(*) FROM emp; 

SELECT COUNT(1) FROM emp; #效率上和COUNT(*)几乎等价

SELECT COUNT(age) FROM emp; #会自动排除age为null的行

注意:如果指定列名,为null的列不参与统计,会自动排除NULL的列

聚合函数练习

统计该企业员工数量

SELECT COUNT(*) FROM emp;

统计该企业员工平均年龄

SELECT AVG(age) FROM emp;

统计该企业员工最大年龄

SELECT MAX(age) FROM emp;

统计该企业员工的最小年龄

SELECT MIN(age) FROM emp;

统计西安地区员工年龄之和

SELECT * FROM emp WHERE workaddress='西安';

分组查询(GROUP BY)

语法:

SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 [HAVING 分组后过滤条件];

WHERE与HAVING的区别

执行时机不同:WHERE执行在GROUP BY之前,不满足WHERE条件的不参与GROUP BY,HAVING是GROUP BY之后对结果进行过滤

判断条件不同:WHERE不能对聚合函数进行判断,而HAVING可以

分组查询练习

根据性别分组,统计男性员工和女性员工数量

SELECT gender, COUNT(*) AS total
FROM emp
GROUP BY gender;  #分组字段不写入查询  查询变得没有意义

根据性别分组,统计男性员工和女性员工的平均年龄

SELECT gender, AVG(age) AS total
FROM emp
GROUP BY gender;

查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址

SELECT workaddress,COUNT(*) total FROM emp where age<45 GROUP BY workaddress HAVING total>3;

SELECT workaddress,COUNT(*) total FROM emp where age<45 GROUP BY workaddress HAVING COUNT(*)>3; #select 之后起了别名  除了ORDER BY之后 HAVING之后也是可以的

注意:

执行顺序  WHERE --- GROUP BY --- 聚合函数 ---HAVING

分组之后,查询的一般为聚合函数和分组字段,查询其他字段无任何意义,一般来说如果用了分组查询,分组的字段不写入查询也会变得无意义(因为不知道这是个什么) 除非就是写死,只有自己知道这是个啥

1. FROM
2. JOIN
3. ON
4. WHERE
5. **GROUP BY**   ← 把数据分组
6. **聚合函数计算**(如 COUNT、SUM)
7. HAVING
8. SELECT
9. ORDER BY
10. LIMIT

排序查询(ORDER BY)

语法:

SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1,字段2 排序方式2;

ASC 代表升序  默认值

DESC 降序

如果多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序

排序查询练习

根据年龄对公司的员工进行升序排序

SELECT * FROM emp ORDER BY age; #默认ASC 可以不写

根据入职时间,对员工进行降序排序

SELECT * FROM emp ORDER BY entrydate DESC;

根据年龄对公司员工进行升序排序,年龄相同按入职时间进行降序排序

SELECT * FROM emp ORDER BY age,entrydate DESC;

分页查询(LIMIT)

语法:

SELECT 字段列表 FROM emp LIMIT 起始索引,查询记录数;

如果将pageNum当页数   pageSize当每页显示条数

注意:该公式不能直接当SQL写入  需要计算好(pageNum-1)*pageSize,pageSize

SELECT 字段列表 FROM emp LIMIT (pageNum-1)*pageSize,pageSize;

注意:分页查询,不同数据库有不同实现,Mysql中是LIMIT

DQL练习

查询年龄为20,21,22,23岁的员工信息

SELECT * FROM emp WHERE age=20 OR age=21 OR age=22 OR age=23;

SELECT * FROM emp WHERE age BETWEEN 20 AND 23;

SELECT * FROM emp WHERE age IN(20,21,22,23);

查询性别为男,并且年龄在20-40岁(都包含)以内的姓名为三个字的员工信息

SELECT * FROM emp WHERE gender='男' AND age BETWEEN 20 AND 40 AND name LIKE '___';

统计员工表中,年龄小于60岁的,男性员工共和女性员工的人数

SELECT gender,COUNT(*) FROM emp WHERE age<60 GROUP BY gender;

查询所有年龄小于35岁员工的姓名,年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序

SELECT name, age 
FROM emp 
WHERE age <= 35 
ORDER BY age, entrydate DESC;

查询性别为男,且年龄在20-40岁(包含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序

SELECT * FROM emp WHERE gender='男' AND age BETWEEN 20 AND 40 ORDER BY age,entrydate LIMIT 5;

DCL(Data Control Language):数据控制语言,用来创建数据库用户,控制数据库的访问权限

常用关键字

  • GRANT:授予权限

  • REVOKE:撤销权限

  • DENY:拒绝权限(部分数据库支持,如 SQL Server)

DCL管理用户

1.查询用户

USE mysql;   

SELECT * FROM user;   #这个user是表名

2.创建用户

CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';   #这里的USER是关键字

3修改用户密码

ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';

4.删除用户

DROP USER '用户名'@'主机名';

创建用户root1 可以从任何地址来连接数据库   密码是abc123456

CREATE USER 'root2'@'%' IDENTIFIED BY 'abc123456';

修改用户登录密码

ALTER USER 'root1'@'%' IDENTIFIED WITH mysql_native_password BY 'abc1234561';

删除用户root1

DROP USER 'root1'@'%';

TCL管理练习

 创建用户'hrui',只能够在当前主机localhost访问,密码'123456'

#该用户创建之后没有权限 只能看到information_schema一张表

CREATE USER 'hrui'@'localhost' IDENTIFIED BY '123456';

修改用户登录密码

ALTER USER 'root1'@'%' IDENTIFIED WITH mysql_native_password BY 'abc1234561';

删除用户root1

DROP USER 'root1'@'%';

权限设置  默认创建用户后  该用户登录之后  没有权限 只能看到information_schema和performance_schema两张表

查询用户权限

SHOW GRANTS FOR '用户名'@'主机名';

新创建的用户只能看到

授予权限

GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';  #如果  *.* 表示所有数据库,所有表

GRANT ALL ON *.* TO 'root1'@'%'; #授予root1所有权限   注意:RDS上不能这么做 不能给与非root用户所有的权限  可以设置具体的库和具体的表

GRANT ALL PRIVILEGES ON testdb.* TO 'root1'@'%';和GRANT ALL ON testdb.* TO 'root1'@'%';效果事一样的

撤销权限

REVOKE all on *.* FROM 'root1'@'%'; 撤销所有权限

注意:多个权限用逗号分隔,授权时数据库和表名可以使用 * 进行统配 *.* 就是所有库所有表

阿里云RDS不允许创建非root用户 的 *.* 全库权限

重点

1.用户管理

CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码'; #可以在任何库执行  不需要指定库

ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码';

DROP USER '用户名'@'主机名';#可以在任何库执行  不需要指定库 就是不用use xxx

2.权限控制

GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';

REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

select * from user; #需要指定数据库 use mysql;

TCL(Transaction Control Language):事务控制语句,用于控制事务

常用关键字

  • BEGIN / START TRANSACTION:开始事务

  • COMMIT:提交事务

  • ROLLBACK:回滚事务

  • SAVEPOINT:设置保存点

  • SET TRANSACTION:设置事务属性

函数

指一段可以直接被另一段程序调用的程序或代码

字符串函数

CONCAT 参数可以是1个或者多个  依次拼接参数 模糊查询时候经常用到

注意SUBSTRING   start是从1开始的    TRIM 无法去掉str中间的空格

数值函数

常用的数值函数

日期函数

流程函数

常用示例

END是为了结束语法  必写  然后取个别名  ELSE 可以不写  不写的话都没有匹配到就返回NULL

约束

约束:作用于表中字段上的规则,用于限制存储在表中的数据

目的:保证数据库中的数据的正确,有效性完整性

各种约束示例

DEFAULT NULL,   DEFAULT 0, DEFAULT 'XXX'

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID唯一标识',
    name VARCHAR(10) NOT NULL UNIQUE COMMENT '姓名',
    age INT CHECK (age > 0 AND age <= 120) COMMENT '年龄',
    status CHAR(1) DEFAULT '1' COMMENT '状态',
    gender CHAR(1) COMMENT '性别'
) COMMENT='用户表';
 

约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束

外键约束

当一张表中的字段引用了另一张表的主键时,这个字段称为“外键”(foreign key),这个关系称为外键约束

这样 如果要删除dept表中数据前,先要删除emp表中关联数据

示例

后面加ON

多表查询

关于sql92和sql99写法

sql92写法  两张表用","分割,关联关系放在where后面

SELECT emp.name, dept.name
FROM emp, dept
WHERE emp.dept_id = dept.id;
 

sql99语法

SELECT emp.name, dept.name
FROM emp
INNER JOIN dept ON emp.dept_id = dept.id;
 

多表关系

在关系型数据库中,常见的表与表之间关系有这 三种:一般现在都不建议外键

1.一对一

CREATE TABLE user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50)
);

CREATE TABLE user_detail (
    id INT PRIMARY KEY AUTO_INCREMENT,   -- 自己的主键
    user_id INT UNIQUE,                  -- 外键 + 唯一约束实现一对一
    address VARCHAR(100),
    phone VARCHAR(20),
    FOREIGN KEY (user_id) REFERENCES user(id)
);
查询示例

SELECT u.username, d.phone
FROM user u
LEFT JOIN user_detail d ON u.id = d.user_id
WHERE u.id = 1;
 

A 表的一条记录只能对应 B 表的一条记录,反之亦然

2.一对多

A 表一条记录可以对应 B 表的多条记录

CREATE TABLE dept (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

CREATE TABLE emp (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT,
    FOREIGN KEY (dept_id) REFERENCES dept(id)
);
查询示例,查某个部门下的员工

SELECT d.name AS 部门名, e.name AS 员工名
FROM dept d
LEFT JOIN emp e ON d.id = e.dept_id
WHERE d.id = 1;
查某个员工在哪个部门

SELECT e.name AS 员工名, d.name AS 部门名
FROM emp e
JOIN dept d ON e.dept_id = d.id
WHERE e.id = 1;

3.多对多

A 表中的记录可以对应 B 表的多条记录,B 表中的记录也能对应 A 表的多条记录  需要一张中间表保存A表ID和B表ID

CREATE TABLE student (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

CREATE TABLE course (
    id INT PRIMARY KEY,
    name VARCHAR(50)
);

CREATE TABLE student_course ( -- 中间表
    student_id INT,
    course_id INT,
    PRIMARY KEY (student_id, course_id), #可以用这个做主键 因为student_id和course_id        一定唯一,也可以单独创建主键  自行维护  外键现在一般都不建议
    FOREIGN KEY (student_id) REFERENCES student(id),
    FOREIGN KEY (course_id) REFERENCES course(id)
);

查询示例:查某个员工选了哪些课程

SELECT s.name AS 学生, c.name AS 课程
FROM student s
JOIN student_course sc ON s.id = sc.student_id
JOIN course c ON sc.course_id = c.id
WHERE s.id = 1;

多表查询创建几张关联表

#创建部门表并插入数据

CREATE TABLE dept (
    id INT AUTO_INCREMENT COMMENT 'ID' PRIMARY KEY,
    name VARCHAR(50) NOT NULL COMMENT '部门名称'
) COMMENT '部门表';

INSERT INTO dept (id, name) VALUES
(1, '研发部'),
(2, '市场部'),
(3, '财务部'),
(4, '销售部'),
(5, '总经办'),
(6, '人事部');

#创建员工表并插入数据
CREATE TABLE emp (
    id INT AUTO_INCREMENT COMMENT 'ID' PRIMARY KEY,
    name VARCHAR(50) NOT NULL COMMENT '姓名',
    age INT COMMENT '年龄',
    job VARCHAR(20) COMMENT '职位',
    salary INT COMMENT '薪资',
    entrydate DATE COMMENT '入职时间',
    managerid INT COMMENT '直属领导ID',
    dept_id INT COMMENT '部门ID'
) COMMENT '员工表';

INSERT INTO emp (id, name, age, job, salary, entrydate, managerid, dept_id) VALUES
(1, '金庸', 66, '总裁', 20000, '2000-01-01', NULL, 5),
(2, '张无忌', 20, '项目经理', 12500, '2005-12-05', 1, 1),
(3, '杨逍', 33, '开发', 8400, '2000-11-03', 2, 1),
(4, '韦一笑', 48, '开发', 11000, '2002-02-05', 2, 1),
(5, '常遇春', 43, '开发', 10500, '2004-09-07', 3, 1),
(6, '小昭', 19, '程序员鼓励师', 6600, '2004-10-12', 2, 1),
(7, '灭绝', 60, '财务总监', 8500, '2002-09-12', 1, 3),
(8, '周芷若', 19, '会计', 48000, '2006-06-02', 7, 3),
(9, '丁敏君', 23, '出纳', 5250, '2009-05-13', 7, 3),
(10, '赵敏', 20, '市场部总监', 12500, '2004-10-12', 1, 2),
(11, '鹿杖客', 56, '职员', 3750, '2006-10-03', 10, 2),
(12, '鹤笔翁', 19, '职员', 3750, '2007-05-09', 10, 2),
(13, '方东白', 19, '职员', 5500, '2009-02-12', 10, 2),
(14, '张三丰', 88, '销售总监', 14000, '2004-10-12', 1, 4),
(15, '俞莲舟', 38, '销售', 4600, '2004-10-12', 14, 4),
(16, '宋远桥', 40, '销售', 4600, '2004-10-12', 14, 4),
(17, '陈友谅', 42, NULL, 2000, '2011-10-12', 1, NULL);
 

#在员工表中创建外键

ALTER TABLE emp
ADD CONSTRAINT fk_emp_dept_id
FOREIGN KEY (dept_id) REFERENCES dept(id);

笛卡尔积演示

SELECT * FROM emp,dept;

没有 WHERE 条件或 JOIN 连接条件,数据库就会把 emp 表中的每一行,与 dept 表中的每一行都组合一遍。

  • emp 有 17 条数据

  • dept 有 6 条数据

  • 17 × 6 = 102 行

需要写连接条件

多表查询分类 

多表查询分类:

1.连接查询

        1.1)内连接:相当于查询A,B交集部门数据

        1.2)外连接:

                2.1)左外连接:查询左表所有数据,以及两张表交集部门数据

                2.2)右外连接:查询右表所有数据,以及两张表交集部门数据

        1.3)自连接:当前表与自身的连接查询,自连接必须使用表别名

2.子查询:把单表分为两张表,需要有连接条件

内连接

返回的是两张表交集部门的数据

sql92写法(隐式写法)

SELECT 字段列表 FROM 表1,表2 WHERE 表1.字段=表2.字段;

sql99写法(显式写法)

SELECT 字段列表
FROM 表1 [INNER] JOIN 表2 ON 表1.字段 = 表2.字段;

内连接练习

查询每一个员工的姓名,及关联的部门的名称(隐式内连接实现)

SELECT a.name,b.name deptName FROM emp a,dept b WHERE a.dept_id=b.id;

查询每一个员工的姓名,及关联的部门名称(显式内连接实现)

SELECT a.name,b.name deptName FROM emp a JOIN dept b ON a.dept_id=b.id;

外连接

左外连接

语法

SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2  ON 连接条件;

以左表为主,查左表所有数据,及交集数据 交集没有的以null填充

右外连接

语法

SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN  表2 ON 连接条件; 

以右表为主,查右表所有数据,及交集数据 交集没有的以null填充

自连接

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

SELECT 字段列表 FROM 表1 别名A JOIN 表2 别名B ON 连接条件;

连接条件

员工的manage_id=老板表.id

联合查询UNION,UNION ALL

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

语法

SELECT 字段1, 字段2, ...
FROM 表1
WHERE 条件
UNION [ALL]
SELECT 字段1, 字段2, ...
FROM 表2
WHERE 条件;


注意:需要保证查询出来的字段名 或者别名是相同的并且列数是相同的

子查询

定义:SQL语句中嵌套SELECT语句,称为嵌套查询,又称为子查询

SELECT * FROM 表1 WHERE column1=(SELECT column1 FROM 表2)

子查询的写法很多

子查询外部的语句可以是INSERT/UPDATE/DELETE/SELECT的任何一个

① SELECT * 
   FROM A 
   WHERE EXISTS (
       SELECT id 
       FROM B 
       WHERE B.aid = A.id
   )

#为什么用DISTINCT   如果A和B是1对多情况 A表数据匹配B表多条会出现重复

② SELECT DISTINCT A.*
   FROM A
   JOIN B ON A.id = B.aid

③ SELECT * 
   FROM A 
   WHERE A.id IN (
       SELECT B.aid 
       FROM B
   )
三种写法结果是等价的

子查询根据结果不同,可以分为

1.标量子查询(子查询结果为单个值)

2.列子查询(子查询结果为一列)

3.行子查询(子查询结果为一行)

4.表子查询(子查询结果为多行多列)

根据子查询位置,分为:WHERE之后,FROM 之后,SELECT之后.

标量子查询

子查询结果为单个值

常用的操作符: =    <>   >   >=    <    <=

查询销售部的所有的员工信息

SELECT * FROM emp WHERE dept_id=(SELECT id FROM dept WHERE name='销售部')

查询在方东白入职之后入职的员工信息

SELECT * FROM emp WHERE entrydate>(SELECT entrydate FROM emp WHERE name='方东白')

列子查询

子查询结果为一列

常用操作符:IN      NOT IN    ANY    SOME   ALL

查询销售部和市场部的所有员工信息

SELECT * FROM emp WHERE dept_id IN(SELECT id FROM dept WHERE name IN('销售部','市场部'))

查询比财务部所有员工工资都高的员工 (这里将财务部周芷若的工资改为8000)插入数据时候数据有问题

SELECT * FROM emp WHERE salary >(SELECT MAX(salary) FROM emp WHERE dept_id=(SELECT id FROM dept WHERE name='财务部'))

SELECT * FROM emp WHERE salary >all(SELECT salary FROM emp WHERE dept_id=(SELECT id FROM dept WHERE name='财务部'))

查询比研发部其中任意一人工资高的员工信息

SELECT * FROM emp WHERE salary>any(SELECT salary FROM emp WHERE dept_id=(SELECT id FROM dept WHERE name='研发部'))

行子查询

子查询结果为一行(可以是多列)

常用操作符: =     <>    IN    NOT IN

查询与张无忌的薪资及直属领导相同的员工信息

薪资相同

SELECT salary FROM emp WHERE salary=(SELECT salary FROM emp WHERE name='张无忌')

直属领导相同

SELECT manageid FROM emp  WHERE name='张无忌' 

SELECT * 
FROM emp 
WHERE salary = (
    SELECT salary FROM emp WHERE name = '张无忌'
)
AND managerid = (
    SELECT managerid FROM emp WHERE name = '张无忌'
);

可以这么写

SELECT * 
FROM emp 
WHERE (salary, managerid) = (
    SELECT salary, managerid 
    FROM emp 
    WHERE name = '张无忌'
);

表子查询

子查询结果为多行多列

常用操作符:IN

查询与鹿杖客 宋远桥的职位和薪资相同的员工信息

鹿杖客 宋远桥的职位和薪资

SELECT job,salary FROM emp WHERE name IN('鹿杖客','宋远桥')

SELECT * FROM emp WHERE (job,salary) IN(SELECT job,salary FROM emp WHERE name IN('鹿杖客','宋远桥'))

查询入职日期是2006-01-01之后的员工信息,及其部门信息

SELECT
    A.*,
    B.name
FROM emp A
LEFT JOIN dept B ON A.dept_id = B.id
WHERE A.entrydate > '2006-01-01';

也可以这样

SELECT e.*, d.* 
FROM (
    SELECT * FROM emp WHERE entrydate > '2006-01-01'
) e
LEFT JOIN dept d ON e.dept_id = d.id;
 

多表查询案例

新增下面这张表  工资等级表(妈了个巴子资本主义啊) 孙中山先生是伟大的'理想家'......

CREATE TABLE salgrade (
    grade INT COMMENT '等级',
    losal INT COMMENT '最低工资',
    hisal INT COMMENT '最高工资'
) COMMENT='薪资等级表';

INSERT INTO salgrade VALUES 
(1, 0,     3000),
(2, 3001,  5000),
(3, 5001,  8000),
(4, 8001, 10000),
(5,10001, 15000),
(6,15001, 20000),
(7,20001, 25000),
(8,25001, 30000);

查询员工的姓名,年龄,职位,部门信息

SELECT a.name,a.age,a.job,b.name deptName FROM emp a LEFT JOIN dept b ON a.dept_id=b.id

查询年龄小于30岁的员工姓名,年龄,职位,部门信息

SELECT a.name,a.age,a.job,b.name deptName FROM emp a LEFT JOIN dept b ON a.dept_id=b.id WHERE a.age<30

查询拥有员工的部门ID,部门名称   (查询交集部分去重)

SELECT id,name FROM dept WHERE id IN(SELECT dept_id FROM emp WHERE dept_id is not null)

SELECT DISTINCT d.id,d.name FROM emp e,dept d WHERE e.dept_id=d.id

查询所有年龄大于40岁的员工,及其归属的部门名称,如果员工没有分配部门,也需要展示出来

SELECT a.name,b.name deptName FROM emp a LEFT JOIN dept b ON a.dept_id=b.id WHERE a.age>40

查询所有员工的工资等级

SELECT
    a.NAME,
    b.grade 
FROM
    emp a LEFT JOIN salgrade b 
ON 
  a.salary BETWEEN b.losal AND b.hisal

或者

SELECT a.name, b.grade 
FROM emp a 
LEFT JOIN salgrade b 
    ON a.salary >= b.losal AND a.salary <= b.hisal;

查询研发部所有员工的信息及工资等级

SELECT
    a.NAME,
    c.grade,
    b.NAME deptName  
FROM
    emp a
    JOIN dept b ON a.dept_id = b.id JOIN salgrade c ON a.salary BETWEEN c.losal 
    AND c.hisal 
WHERE
    b.NAME = '研发部'

查询研发部员工的平均工资

SELECT
    a.name AS deptName,
    AVG(b.salary) AS avgSalary
FROM dept a
LEFT JOIN emp b ON a.id = b.dept_id
WHERE a.name = '研发部'
GROUP BY a.name;

查询工资比灭绝高的员工信息

SELECT * FROM emp WHERE salary>(SELECT salary FROM emp WHERE name='灭绝')

查询比平均薪资高的员工信息

SELECT * FROM emp WHERE salary>(SELECT AVG(salary) FROM emp)

查询低于本部门平均工资的员工信息

SELECT a.*
FROM emp a
JOIN (
    SELECT dept_id, AVG(salary) AS avg_salary
    FROM emp
    GROUP BY dept_id
) AS b ON a.dept_id = b.dept_id
WHERE a.salary < b.avg_salary;
 

查询所有的部门信息,并统计部门的员工人数

SELECT deptName, COUNT(*) 
FROM (SELECT a.*, b.name deptName 
      FROM emp a 
      JOIN dept b ON a.dept_id = b.id) t
GROUP BY deptName

事务

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

默认情况下,使用Mysql默认自动提交事务的

创建事务演示表

CREATE TABLE account (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    name VARCHAR(10) COMMENT '姓名',
    money INT COMMENT '余额'
) COMMENT='账户表';
INSERT INTO account (id, name, money) 
VALUES 
    (NULL, '张三', 2000),
    (NULL, '李四', 2000);

事务操作

以下两种都是临时会话级别设置事务手动提交

方式1:

开启事务: START TRANSACTION  或者  BEGIN

提交事务: COMMIT  

回滚事务: ROLLBACK

方式2

可以通过上面方式  也可以设置

SELECT @@autocommit;   #如果是1 自动提交   0手动提交
SET @@autocommit = 0;

方式2不需要START TRANSACTION  或者  BEGIN   可以直接用COMMIT   ROLLBACK

一般只需要了解会话级别事务即可

方式1

方式2

事务四大特性
1. 原子性(Atomicity

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

💡 比如转账:扣钱成功但收钱失败,就要全部回滚。

2. 一致性(Consistency

事务执行后,必须使数据库从一个一致状态变为另一个一致状态

💡 比如转账后,张三减少的钱和李四增加的钱总数不变,前后一致。

3. 隔离性(Isolation

多个事务并发执行时,要互不干扰,互不影响,数据库提供隔离机制保障这一点。

💡 比如你在查库存时,别人的下单操作不会影响你看到的结果。

4. 持久性(Durability

事务一旦提交,对数据库的修改就是永久性的,即使系统崩溃也不会丢失

💡 比如提交转账后,即使断电重启,数据也已经改了。

 

并发事务问题

两个或多个事务在“同时”对同一份数据(同一条)进行操作或读取,从而引发数据不一致、错乱等问题。

例如张三的银行卡

在一个事务中

SELECT money FROM account WHERE name = '张三';  -- 得到 1000
UPDATE account SET money = 1000 - 100 WHERE name = '张三';  -- 更新为 900

 在另外一个事务中

SELECT money FROM account WHERE name = '张三';  -- 也看到 1000!
UPDATE account SET money = 1000 - 200 WHERE name = '张三';  -- 更新为 800

结果谁最后提交就覆盖了另一个,导致少减了一笔! 

脏读

一个事务 读取了另一个事务尚未提交的数据

Read Uncommitted(读未提交)发生

举例

当A事务开启,执行 但是还没有提交

UPDATE account SET money = 0 WHERE name = '张三';


此时事务 B 执行

SELECT money FROM account WHERE name = '张三';  -- 查到 0(但其实没提交)

然后事务 A 回滚了,张三余额还是原来的,但事务 B 却读到了“脏数据”

不可重复读

同一个事务中,前后两次读取同一条数据,结果不一样(因为别的事务修改了数据并提交了)

Read Committed(读已提交)会发生

举例

当事务A开启

SELECT money FROM account WHERE name = '张三';  -- 第一次查到 1000

此时事务B对该数据进行了操作

UPDATE account SET money = 500 WHERE name = '张三';
COMMIT;

当A事务再次查询时候

SELECT money FROM account WHERE name = '张三';  -- 查到 500(发生了不可重复读)
 

幻读

幻读是指:同一个事务内,使用相同查询条件进行多次查询时,结果集的“行数”或“记录”发生了变化原因是其他事务在期间插入或删除了“符合条件的新记录”。

Repeatable Read(可重复读)

举例

事务A开启

SELECT * FROM account WHERE money > 1000;  -- 查到 2 条记录
 

事务B插入

INSERT INTO account(name, money) VALUES ('王五', 2000);
COMMIT;

当事务A再次执行相同查询

SELECT * FROM account WHERE money > 1000;  -- 现在查到 3 条记录(多了一条“幻影”)
 

事务隔离级别

默认自己安装的Mysql是可重复读     阿里云RDS是读已提交

查看事务隔离级别

SELECT @@transaction_isolation;
-- 或旧版本:
SELECT @@tx_isolation;

会话级别设置

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

全局设置

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

可选级别

READ UNCOMMITTED     读未提交

READ COMMITTED    读已提交

REPEATABLE READ   可重复读

SERIALIZABLE   序列化读

Mysql进阶篇

存储引擎

在Java(编程语言)中,"引擎"(Engine)就是某一类核心功能的实现模块/组件,它封装了复杂的底层逻辑,开发者只需要调用接口就能使用。

MySQL 的存储引擎是数据库内部用于管理表数据的核心模块,负责实现数据的存储、索引的建立与维护、数据的读取、更新、删除等底层功能。存储引擎是基于表的,而不是基于库的,所以存储引擎也被称表类型

就像 Java 引擎对外暴露接口供你使用一样,MySQL 把数据管理的复杂逻辑封装在“引擎”里,不同引擎有不同的能力,比如是否支持事务、是否支持全文索引等。

  • 决定数据怎么存(数据结构、磁盘布局)

  • 决定索引怎么建(B+ 树、哈希等)

  • 决定数据怎么读写(是否缓存、是否支持并发控制)

  • 决定是否支持事务、外键、崩溃恢复等高级功能

在创建表时如果没有显式指定存储引擎(ENGINE),MySQL 会使用系统设置的“默认引擎”,Mysql的默认引擎是InnoDB,并不是基于库的

default_storage_engine 是 MySQL 中用于指定“默认存储引擎”的系统变量
当你创建一张表时没有显式写 ENGINE=xxx,就会自动使用这个变量指定的存储引擎。

查看默认引擎

SHOW VARIABLES LIKE 'default_storage_engine';

创建表时候指定存储引擎

CREATE TABLE 表名 (
    字段1 字段1类型 COMMENT '字段1注释',
    字段2 字段2类型 COMMENT '字段2注释',
    ...
    字段n 字段n类型 COMMENT '字段n注释'
) ENGINE = INNODB COMMENT = '表注释';
 

MySQL 内置已“注册”的所有存储引擎类型

SHOW ENGINES; 

InnoDB,MyISAM,MEMORY三种引擎介绍
InnoDB介绍

Mysql5.5之后,InnoDB是默认的Mysql存储引擎

InnoDB特点

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

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

支持外键FOREIGN KEY约束,保证数据的完整性和正确性

每张使用 InnoDB 存储引擎的表,在磁盘上会有一个 .ibd 文件,这个文件就是该表的**“独立表空间”**,用来存储表的:

  • 结构(配合 .sdi 文件)

  • 数据

  • 索引

这个行为是由参数 innodb_file_per_table 控制的。

参数:innodb_file_per_table

SHOW VARIABLES LIKE 'innodb_file_per_table';

该文件是二进制的 普通记事本打开看不到啥

可以通过cmd命令

ibd2sdi emp.ibd                     ibd2sdi是命令   emp.ibd是表空间  好比表

我这里出现这个原因可能是文件损坏了  因为安装过不同版本的

换了一个库的表.ibd就好了

MyISAM介绍

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

特点

不支持事务,不支持外键

支持表锁,不支持行锁

访问速度快(具体说和InnoDB比,看具体场景分析)

文件结构 .myd   .myi   .sdi

MEMORY介绍

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

特点

内存中存放

hash索引(默认)

文件  xxx.sdi存储表结构信息无具体数据

主要用的是InnoDB    MyISAM被MongoDB取代    MEMORY被Redis取代

Linux中Mysql安装 

https://www.mysql.com/


mkdir /usr/local/develop

cd /usr/local/develop

上传到服务器

rpm -ivh mysql84-community-release-el7-1.noarch.rpm

yum install -y mysql-community-server

systemctl start mysqld

systemctl status mysqld

查看初始密码

grep 'temporary password' /var/log/mysqld.log

mysql -u root -p

修改密码

ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPassword123!';

use mysql;

select host,user from user;

其实是建议再创建一个root   Mysql支持同一个用户名有多个不同的登录规则

在Mysql中  用户是通过 user@host的组合来做唯一标识的而不是单纯看用户名

我是直接Update了  如果需要重新创建  执行下面三跳命令

CREATE USER 'root'@'%' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;这个好比刷新 好像不需要执行

UPDATE mysql.user SET host='%' WHERE user='root' AND host='localhost';

exit

vim /etc/my.cnf

bind-address = 0.0.0.0 

systemctl restart mysqld

安全组打开3306

其他安装方式

你也可以下载整个RPM包

https://dev.mysql.com/downloads/mysql/

mkdir /usr/local/develop

cd /usr/local/develop

将下载的

mysql-8.4.4-1.el7.aarch64.rpm-bundle.tar  上传到服务器

tar -xzf mysql-8.4.4-1.el7.aarch64.rpm-bundle.tar

这种方式没有试验  使用的是上面的   解压之后  安装是有顺序的  大致如下

安装顺序

systemctl start mysqld

systemctl restart mysqld

systemctl stop mysqld

启动之后  查询密码

grep 'temporary password' /var/log/mysqld.log

mysql -u root -p

ALTER  USER  'root'@'localhost'  IDENTIFIED BY '1234';

如果需要的话

索引

索引是 MySQL 中用于提高数据查询效率的数据结构,它类似于书的目录或字典的检索页,通过有序的数据结构,快速定位到目标数据。

什么是索引:索引是用于提高数据查询效率有序的数据结构

没有索引的查询是全表查询:每条数据都需要查一遍

MySQL 的索引数据结构不是二叉树,而是多路搜索树——最常见的是 B+ 树。

以二叉树为例子介绍索引  注意 二叉树不是索引数据结构  就是说为什么快了

索引的好处是提高了查询,因索引需要维护,降低了增删改的效率

索引的好处是:提高查询效率
索引的代价是:增删改操作时要维护索引结构,导致效率下降

索引数据结构

Mysql的索引实在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种

我们平常所说的索引,如果没有特别指明,都是指B+树结构的索引

注意二叉树不是索引的数据结构  下图讲的是为什么二叉树不作为索引数据结构的原因

红黑树面对大数据时候还是无法避免层级较深,导致检索速度慢

B-TREE

B+TREE 

索引的分类 

总结一句话: 主键索引一张表只能有一个  其他索引都可以有多个

MySQL 索引分为:主键索引(唯一 + 非空)、唯一索引(值不能重复)、普通索引(仅加速查询)、全文索引(文本匹配)。使用时根据数据特征和查询需求选择合适的类型。

 

创建索引
索引语法

CREATE [UNIQUE FULLTEXT] INDEX index_name ON table_name(列1,列2);

1.主键索引(PRIMARY KEY)

每个表只能有一个主键索引

主键值不能重复,不能为NULL(非空且唯一),实际是一个特殊的唯一索引

2.唯一索引(UNIQUE)

保证某一列或多列的值唯一

可以有多个唯一索引,允许NULL值(但只能有一个NULL)

CREATE UNIQUE INDEX inx_email ON users(email);

或者建表时

CREATE TABLE users ( 
    id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE,
    username VARCHAR(100) UNIQUE
);

CREATE TABLE tasks (
    user_id INT,
    project_id INT,
    task_name VARCHAR(100),
    UNIQUE KEY uniq_user_project (user_id, project_id)
); 

3.普通索引

即B-TREE索引

普通索引,没有唯一性限制,用于提高查询速度

CREATE INDEX inx_name ON users(name);  

普通索引(非唯一索引)是可以为 NULL 的!

4.复合索引

包含多个列的索引

只有查询条件命中从最左边开始的一段,才能用到索引

CREATE INDEX inx_name_age ON users(name,age);

5.全文索引(FULLTEXT)

用于全文搜索,主要针对CHAR VARCHAR TEXT

CREATE FULLTEXT INDEX idx_content ON articles(content)

MyISAM 和 InnoDB 引擎都支持 FULLTEXT(MySQL 5.6 及以上 InnoDB 开始支持)。

6.空间索引

用于地理空间数据(如点,多边形),依赖GIS

必须在使用MyISAM引擎时有效

CREATE SPATIAL INDEX idx_location ON places(location);

MySQL 复合索引的使用原则 —— 特别是 最左前缀原则

例如创建了 复合索引或者多列联合唯一索引

例如 A列  B列  C列   D列

A  B  C是有索引的

只要查询条件中有A  都会使用到索引   即使查询条件中有D

查看索引

SHOW INDEX FROM table_name

删除索引

DROP INDEX index_name ON table_name;

注意:

练习

CREATE TABLE tb_user (
    id INT PRIMARY KEY auto_increment COMMENT '主键',
    NAME VARCHAR ( 50 ) NOT NULL COMMENT '用户名',
    phone VARCHAR ( 11 ) NOT NULL COMMENT '手机号',
    email VARCHAR ( 100 ) COMMENT '邮箱',
    profession VARCHAR ( 11 ) COMMENT '专业',
    age TINYINT UNSIGNED COMMENT '年龄',
    gender CHAR ( 1 ) COMMENT '性别 , 1: 男, 2: 女',
    STATUS CHAR ( 1 ) COMMENT '状态',
createtime datetime COMMENT '创建时间' 
) COMMENT '系统用户表';

INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('吕布', '17799990000', 'lvbu666@163.com', '软件工程', 23, '1',
'6', '2001-02-02 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('曹操', '17799990001', 'caocao666@qq.com', '通讯工程', 33,
'1', '0', '2001-03-05 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('赵云', '17799990002', '17799990@139.com', '英语', 34, '1',
'2', '2002-03-02 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('孙悟空', '17799990003', '17799990@sina.com', '工程造价', 54,
'1', '0', '2001-07-02 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('花木兰', '17799990004', '19980729@sina.com', '软件工程', 23,
'2', '1', '2001-04-22 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('大乔', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2',
'0', '2001-02-07 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('露娜', '17799990006', 'luna_love@sina.com', '应用数学', 24,
'2', '0', '2001-02-08 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38,
'1', '5', '2001-05-23 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('项羽', '17799990008', 'xiaoyu666@qq.com', '金属材料', 43,
'1', '0', '2001-09-18 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('白起', '17799990009', 'baiqi666@sina.com', '机械工程及其自动
化', 27, '1', '2', '2001-08-16 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('韩信', '17799990010', 'hanxin520@163.com', '无机非金属材料工
程', 27, '1', '0', '2001-06-12 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('荆轲', '17799990011', 'jingke123@163.com', '会计', 29, '1',
'0', '2001-05-11 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('兰陵王', '17799990012', 'lanlinwang666@126.com', '工程造价',
44, '1', '1', '2001-04-09 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('狂铁', '17799990013', 'kuangtie@sina.com', '应用数学', 43,
'1', '2', '2001-04-10 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('貂蝉', '17799990014', '84958948374@qq.com', '软件工程', 40,
'2', '3', '2001-02-12 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('妲己', '17799990015', '2783238293@qq.com', '软件工程', 31,
'2', '0', '2001-01-30 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('芈月', '17799990016', 'xiaomin2001@sina.com', '工业经济', 35,
'2', '0', '2000-05-03 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1',
'1', '2001-08-08 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('狄仁杰', '17799990018', 'jujiamlm8166@163.com', '国际贸易',
30, '1', '0', '2007-03-12 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('安琪拉', '17799990019', 'jdodm1h@126.com', '城市规划', 51,
'2', '0', '2001-08-15 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('典韦', '17799990020', 'ycaunanjian@163.com', '城市规划', 52,
'1', '2', '2000-04-12 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('廉颇', '17799990021', 'lianpo321@126.com', '土木工程', 19,
'1', '3', '2002-07-18 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('后羿', '17799990022', 'altycj2000@139.com', '城市园林', 20,
'1', '0', '2002-03-10 00:00:00');
INSERT INTO tb_user (name, phone, email, profession, age, gender, status,
createtime) VALUES ('姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29,
'1', '4', '2003-05-26 00:00:00');

1.name字段为姓名字段,该字段的值可能会重复,为该字段创建索引

CREATE INDEX inx_user_name ON tb_user(name);

2.phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引

CREATE UNIQUE INDEX inx_user_phone ON tb_user(phone);

3.为profession,age,status创建联合索引

CREATE INDEX inx_user_pro_age_sta ON tb_user(profession,age,status);

4.为email建立合适的索引来提升查询效率

CREATE INDEX inx_user_email ON tb_user(email);

SQL性能分析(SQL优化)
SQL执行频率

SHOW [SESSION|GLOBAL] STATUS查看服务状态信息

SHOW SESSION STATUS

SHOW GLOBAL STATUS

SHOW GLOBAL STATUS LIKE 'Com_______';

SHOW GLOBAL STATUS LIKE 'Com_select'

慢查询日志

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志

Mysql的慢查询日志默认没有开启,需要在Mysql的配置文件(/etc/my.cnf)中配置

SHOW VARIABLES LIKE 'SLOW_QUERY_LOG'

这里用的是阿里云RDS  默认是开启的

没有开启是OFF

开启Profile详情

show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里了.通过have_profiling参数,可以查看当前Mysql是否支持profile操作

SELECT @@have_profiling

以下是阿里云RDS配置  默认是关闭的

SELECT @@profiling

可以通过SET  开启profiling

SET profiling=1

开启profile详情之后

通过指令可以查看执行耗时情况

开启profile之后,当你执行SQL操作  通过

SHOW profiles查看执行情况

SHOW profile for query 16;  查看指定sql各阶段耗时情况

Explain执行计划

开启profile可以看到SQL的执行时间,但是单纯看到执行时间还是不能精确定位分析SQL的优化

explain执行计划可以看到包括SELECT语句执行过程,例如如果连接和连接顺序,是否用到索引

通过EXPLAIN或者DESC命令获取Mysql如何执行SELECT语句信息

直接在SELECT语句之前加上关键字expain/desc

EXPLAN SELECT 字段列表 FROM 表名 WHERE 条件;

索引使用与失效

SELECT * FROM tb_sku WHERE sn = '10000003145004';

如果一张表的数据量很大 那么可以对sn创建索引

SELECT * FROM tb_sku WHERE sn = '10000003145004' AND other_field = 'xxx';

只要 sn 是查询条件的一部分,MySQL 仍然会使用 sn 的索引来过滤第一步的结果集。

然后,它会在命中的 sn 结果基础上,继续判断其他字段(如 other_field)的条件是否满足

最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列.如果跳跃某一列,索引将部分失效(后面的字段索引失效)

例如A列  B列  C列  D列   ABC是复合索引  D没有索引

注意只要WHERE条件中存在   和位置在前或者在后没关系

范围查询

联合索引中,出现范围查询,范围查询,右侧的列索引将失效

索引列运算

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

字符串不加引号 

字符串类型字段使用时,不加引号,索引将失效

模糊查询

如果仅仅是尾部模糊匹配,索引不会失效.如果是头部模糊匹配,索引失效

OR连接条件 

用OR分割开的条件,如果OR前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到

索引使用原则

数据分布影响:如果Mysql评估使用索引比全表扫描慢,则不适用索引

当大部分需要查的时候也可能会走全表扫描

当查询的数据量占表中“大部分”时,MySQL 很可能放弃使用索引,选择🔁全表扫描,因为它认为这样反而更快!    注意是可能

由查询优化器决定

主要由 MySQL 查询优化器(Query Optimizer) 根据多个因素动态评估查询成本,决定是否使用索引。

索引的选择(SQL提示)

当你创建了联合索引, 例如 A  B  C 三个字段     而又对A也创建了一个索引

当你用A字段进行查询时候

Mysql查询优化器会动态选择用哪一个索引

我们也可以指定使用哪个索引,称为SQL提示

简单来说,就是SQL语句中加入一些认为的提示来达到优化操作的目的

SQL提示

1.USE INDEX 告诉Mysql用哪个索引

例如:

SELECT * FROM tb_user USE INDEX(inx_use_pro) WHERE profession='软件工程';

2.IGNORE INDEX  告诉Mysql不要用哪个索引

SELECT * FROM tb_user IGNORE INDEX(inx_use_pro) WHERE profession='软件工程';

3.FORCE INDEX 告诉Mysql必须用这个索引

SELECT * FROM tb_user FORCE INDEX(inx_use_pro) WHERE profession='软件工程';

覆盖索引

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

查询条件是索引并且返回的字段也是索引就是覆盖索引,使用SELECT * 容易出现回表查询 除非所有字段都创建了索引

前缀索引

当字段类型为字符串(VARCHAR,TEXT等)时,有时候需要索引很长的字符串.会使索引变得很大,影响查询效率.可以将字符串的一部分前缀,建立索引,可以节约索引空间.提高索引效率

前缀索引:指的是 只对字符串字段的前 N 个字符建立索引,而不是整列。

创建前缀索引

CREATE INDEX inx_table_xxx ON table_name(CLOLUMN(n))

例如

CREATE INDEX idx_email_prefix ON users(email(10));

单例索引和联合索引的选择

单例索引:即一个索引只包含单个列

联合索引:即一个索引包含多个列

索引设计原则

SQL优化

创建索引可以当作查询数据的优化,以下介绍其他操作的优化

插入数据优化

 在已经有sql脚本的情况下  可以使用load命令

主键优化

主键顺序插入的性能高于主键乱序插入----------->即为主键优化

order by优化

group by优化

limit优化

LIMIT查询时候问题出在哪里

SELECT * FROM tb_user LIMIT 0,10;

SELECT * FROM tb_user LIMIT 1000000000,10;  越往后查询越慢

一个非常常见且代价昂贵的问题是使用 LIMIT 2000000, 10,这会导致 MySQL 必须扫描并排序 前 2000010 条记录,然后丢弃前 2000000 条,仅返回后 10 条。因此,随着 offset 越大,查询性能会指数级下降,排序和扫描成本非常高。

通过覆盖索引加子查询来提高效率

count优化

当数据量大的时候  使用InnoDB  COUNT(*)查询是比较耗时的

暂时没有好的优化,如果非要优化,自己计数

例如使用Redis等内存级别数据库  每插入一条数据就+1    删除一条数据-1    比较繁琐

COUNT(*)效率最高 可以说约等于 COUNT(1)>COUNT(主键ID)>COUNT(其他字段)

update优化

INnoDB   三特性   事务   外键    行级锁

就是说更新时候通过索引更新效率高

不是“一定”加表锁,而是“可能”会加表锁
默认情况下,InnoDB 仍然尝试加行锁」,即便没有命中索引,它会全表扫描并对每一行加行锁
但在某些情况下,为了提升性能或避免死锁冲突,可能自动退化成表锁(锁升级)。 

视图/存储过程/触发器

视图

视图(View)是一种虚拟存在的表.视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。

视图(View)本质上就是一个被命名的查询语句

创建视图

CREATE [OR REPLACE] VIEW 视图名称 [(列名列表)]
AS SELECT语句
[WITH [CASCADED | LOCAL] CHECK OPTION]


举例   SELECT后面查询的表称为基表

CREATE VIEW active_users AS
SELECT id, username FROM users WHERE status = 'active';

 

创建视图 

CREATE VIEW stu_v_1 AS SELECT id,name FROM student WHERE id<=10;

查询视图

查看创建视图语句:SHOW CREATE VIEW 视图名称;

查看视图数据:SELECT * FROM 视图名称 ....;

修改视图

注意修改视图加上:OR REPLACE

CREATE OR REPLACE VIEW stu_v_1 AS 
SELECT id, name, age 
FROM student 
WHERE id <= 20;
 

或者

ALTER VIEW stu_v_1 AS SELECT .......;

删除视图

DROP VIEW [IF EXISTS] 视图名称1 [,视图名2];

视图的创建增删改查

创建视图

CREATE OR REPLACE VIEW stu_v_1 AS SELECT id,name FROM student WHERE id<20;

查询视图中的数据

SELECT * FROM stu_v_1;

往视图中插入数据

INSERT INTO stu_v_1 VALUES(6,'Tom');   注意插入的数据实际是插入到基表中的

视图的检查选项

当创建视图时候使用WITH CHECK OPTION子句创建时,Mysql会通过视图检查正在更改的每个,

新增,更新,删除是否符合视图定义.Mysql允许基于另一个视图创建视图.会检查视图规则

Mysql提供两个规则选项

1.CASCADED(默认)

例如

CREATE VIEW v1 AS SELECT id,name FROM student WHERE id<20;

如果后面不加

WITH [CASEADED|LOCAL] CHECK OPTION 

这样在增删改时候视图是不会检查条件的 条件就是id<20

CREATE VIEW v2 AS SELECT id,name FROM v1 WHERE id<20 WITH CASEADED CHECK OPTION;

如果这样的话

在增删改时候视图会检查条件,不光检查V2条件 还会检查V1的条件,相当于在创建视图V1时候后面也加了WITH CASEADED CHECK OPTION;

2.LOCAL

WITH LOCAL CHECK OPTION的作用是如果视图本身有条件需要满足条件,不会强制父视图满足(如果有WITH [CASEADED|LOCAL ] CHECK OPTION需要满足,没有拉到)

触发器

触发器是与表有关的数据库对象,在INSERT/UPDATE/DELETE之前或之后,触发并执行触发器中定义的SQL语句集合.自动执行一段你提前定义好的 SQL 代码(这些代码叫“触发器体”)。

所谓OLD和NEW 是触发器给了我们一个定义:用来引用新的数据和老的数据

在Mysql中现在只支持行级触发器,还不支持语句级触发器

触发器的创建查看删除

不支持修改触发器,可以先删除再重新创建

触发器练习

通过触发器记录tb_user表的数据变更日志,将变更日志插入到日志表user_logs中,包含增加修改,删除

tb_user表上面有记录

注意:user_logs和tb_user可以在同一个Mysql实例中

跨库需要 数据库.表    不同实例间就需要应用代码层去处理

CREATE TABLE user_logs (
  id INT(11) AUTO_INCREMENT PRIMARY KEY,
  operation VARCHAR(20) NOT NULL COMMENT '操作类型,例如 insert/update/delete',
  operate_time DATETIME NOT NULL COMMENT '操作时间',
  operate_id INT(11) NOT NULL COMMENT '操作的ID',
  operate_params VARCHAR(500) COMMENT '操作参数'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT触发器

应该对字段进行NULL判断,不然只要插入时候有一个是NULL,导致operate_params整个为NULL

CREATE TRIGGER tb_user_insert_trigger
AFTER INSERT ON tb_user
FOR EACH ROW
BEGIN
  INSERT INTO user_logs (id, operation, operate_time, operate_id, operate_params)
  VALUES (
    NULL,
    'insert',
    NOW(),
    NEW.id,
    CONCAT(
      '插入的数据内容为: id=', NEW.id,
      ', name=', NEW.name,
      ', phone=', NEW.phone,
      ', email=', NEW.email,
      ', profession=', NEW.profession
    )
  );
END;

 修改之后   看是否有需要  如果你在代码层面已经控制的话就不需要了

CREATE TRIGGER tb_user_insert_trigger
AFTER INSERT ON tb_user
FOR EACH ROW
BEGIN
  INSERT INTO user_logs (id, operation, operate_time, operate_id, operate_params)
  VALUES (
    NULL,
    'insert',
    NOW(),
    NEW.id,
    CONCAT(
      '插入的数据内容为: id=', IFNULL(NEW.id, ''),
      ', name=', IFNULL(NEW.name, ''),
      ', phone=', IFNULL(NEW.phone, ''),
      ', email=', IFNULL(NEW.email, ''),
      ', profession=', IFNULL(NEW.profession, '')
    )
  );
END;
 

查看触发器指令

SHOW TRIGGERS;

删除触发器

DROP TRIGGER tb_user_insert_trigger;

 

当往tb_user插入数据之后

INSERT INTO tb_user
VALUES (NULL, 'HRUI', '12345', 'HRUI@163.COM', '不务正业', 13, '男', 1, NOW());

UPDATE触发器 

也需要对NULL的判断  这里就不写了   参考INSERT

CREATE TRIGGER tb_user_update_trigger
AFTER UPDATE ON tb_user
FOR EACH ROW
BEGIN
  INSERT INTO user_logs (id, operation, operate_time, operate_id, operate_params)
  VALUES (
    NULL,
    'insert',
    NOW(),
    NEW.id,
    CONCAT(
      '更新之前的数据: id=', OLD.id,
      ', name=', OLD.name,
      ', phone=', OLD.phone,
      ', email=', OLD.email,
      ', profession=', OLD.profession,

      ';更新之前的数据: id=', NEW.id,
      ', name=', NEW.name,
      ', phone=', NEW.phone,
      ', email=', NEW.email,
      ', profession=', NEW.profession
    )
  );
END;

DELETE触发器

CREATE TRIGGER tb_user_delete_trigger
AFTER DELETE ON tb_user
FOR EACH ROW
BEGIN
  INSERT INTO user_logs (id, operation, operate_time, operate_id, operate_params)
  VALUES (
    NULL,
    'insert',
    NOW(),
    OLD.id,      注意是OLD.id
    CONCAT(
      '删除之前的数据: id=', OLD.id,
      ', name=', OLD.name,
      ', phone=', OLD.phone,
      ', email=', OLD.email,
      ', profession=', OLD.profession
    )
  );
END;

锁是计算机协调多个进程或线程并发访问某一资源的机制.

在数据库中,这里的“资源”主要是指 数据本身,也可能包括元数据等。

数据库除了传统的资源竞争(比如 CPU、内存、I/O),还会面临一个更核心的问题:

多个用户同时访问或修改相同数据,如何保证数据的一致性与正确性?

在数据库中,锁是保障数据一致性的重要机制,也是并发性能调优中必须面对的复杂问题。 

全局锁

全局锁是对整个数据库实例加锁,加锁后整个实例进入只读状态

  • 所有 DML(如 INSERTUPDATEDELETE)和 DDL(如 ALTERDROP)语句都会被阻塞

  • 已经提交的事务可以读,但写入、结构变更都会被锁住

 FLUSH TABLES WITH READ LOCK 锁会在连接断开时自动释放   是会话级别

DML和DDL操作会挂起,只能进行DQL

事务可以开启但不能提交

挂起可以理解为卡住状态,当应用程序对数据库进行增删改操作时候,应用长时间得不到回应会报错

通过全局锁进行数据库备份

在mysql客户端 FLUSH TABLES WITH READ LOCK     对数据库库实例加全局锁

在Windows命令窗口   mysqldump [-h120.0.0.1 -P6666] -uroot -p123456 数据库名 > D:/db01.sql

在mysql客户端  UNLOCK TABLES;  解锁    加锁和解锁可以在同一窗口会话中进行

不加锁进行备份 

表级锁

表级锁,每次操作锁住整张表.锁定粒度大,发生冲突概率高,并发度最低.

MyISAM,INNODB,BDB等存储引擎中可以使用表级锁

-- 给表加读锁(其他连接不能写)
LOCK TABLES table_name READ;

-- 给表加写锁(其他连接不能读也不能写)
LOCK TABLES table_name WRITE;
 

解锁  注意不用加表名  并且断开会话连接会自动解锁

UNLOCK TABLES;
 

行级锁

行级锁,每次操作锁住对应的行数据.锁定粒度最小,发生锁冲突的概率最低,并发度最高.应用在INnoDB引擎中

默认InnoDB核心讲解

Mysql管理

Mysql运维篇

日志

主从复制

概述 

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

Mysql支持一台主库同时向多台从库进行复制,从库同时也可以做为其他从库的主库,实现链状复制

主库(Master)

从库(Slave)

Mysql主从复制的优点

1.主库出现问题,可以快速切换到从库提供服务(这个可以想想代码层面如何实现)

2.实现读写分离,降低主库的访问压力

3.可以在从库中执行备份,以避免备份期间影响主库服务

原理

Mysql主从复制原理是基于二进制日志

SHOW VARIABLES LIKE 'log_bin';
-- 正常情况下返回 Value = ONSHOW VARIABLES LIKE 'binlog_format';
-- 通常返回 ROWSHOW BINARY LOGS;
-- 显示现有的binlog文件列表

Mysql8中   binlog默认开启  如果需要显示关闭(找死)

搭建

1.两个Mysql实例

2.配置主从

主库配置

配置完成之后重启Mysql

systemctl restart mysqld;

主库创建用于复制的用户

CREATE USER '用户名'@'%' IDENTIFIED WITH mysql_native_password BY '密码';

  • @'%' 表示允许任意主机(即从库)使用该用户连接主库

  • 使用 mysql_native_password 插件认证(兼容性最好)

授权该用户具备主从复制的权限

GRANT REPLICATION SLAVE ON *.* TO '用户名'@'%';

  • REPLICATION SLAVE 权限是必须的

  • 它允许该用户从主库 读取 binlog 并进行同步

当然如果你使用root用户 那么上面这些可以不进行配置  use mysql; select * from user看下root用户是否允许本机或者所有IP来进行连接   Mysql 以 用户名/主机  来定义用户   例如可以多个root 但是host不一样

看下二进制坐标

SHOW master status;

主库配置就完毕了

从库配置

注意: read-only=1 针对的普通用户  对root用户就不针对了

如果连超级用户也要针对    注意 中划线和下划线无所谓

# 保证 server-id 唯一
server-id=2

# 普通只读保护
read_only=1

# 限制超级用户写入(更安全)
super_read_only=1
 

重启从库

systemctl restart mysqld;

主库和从库进行关联  在从数据库执行命令

如果用的是root

CHANGE MASTER TO
  MASTER_HOST='主库IP',
  MASTER_USER='root',
  MASTER_PASSWORD='你的密码',
  MASTER_LOG_FILE='xxx',
  MASTER_LOG_POS=xxx;

查看版本

SELECT VERSION();
 

MySQL 8.0.23 及以后(新语法):

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='xxx.xxx.xxx.xxx',
  SOURCE_USER='用户名',
  SOURCE_PASSWORD='密码',
  SOURCE_LOG_FILE='binlog文件名',
  SOURCE_LOG_POS=位置;

MySQL 8.0.22 及以前(旧语法):

CHANGE MASTER TO
  MASTER_HOST='xxx.xxx.xxx.xxx',
  MASTER_USER='用户名',
  MASTER_PASSWORD='密码',
  MASTER_LOG_FILE='binlog文件名',
  MASTER_LOG_POS=位置;

SOURCE_LOG_FILE='binlog文件名',   SOURCE_LOG_POS=位置; 可以在主库中

SHOW MASTER STATUS;  来查看

登录主库   SHOW SLAVE STATUS\G;可以查看从库状态     \G只是一种格式,没什么特别含义

在主库进行建表    增删改   都会在从库同步

分库分表

读写分离

介绍

读写分离是基于主从复制的    都可以了解下

就是把对数据的读和写操作分开.减轻单台数据库的压力

通过MyCat即可轻易实现,MyCat不仅支持Mysql,也支持Oracle和SQL Server

注意:Spring Boot + dynamic-datasource 的读写分离是代码层面控制读写分离 属于应用层的读写分离

MyCat是通过中间件层实现的,Spring Boot 的应用只连接一个逻辑数据源(Mycat),它背后再去路由主从数据库。属于中间件层的读写分离  端口 8066 

一主一从

一主一从的搭建上面已经有相关介绍

登录主库

SHOW SLAVE status\G;  查看从库状态

一主一从读写分离

双主双从

双主双从读写分离

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/76198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[IOI 1994] 数字三角形 Number Triangles

题目链接 思路&#xff08;上到下&#xff09;&#xff1a; ①从上往下递推&#xff1a; f[i][j] max(f[i-1][j] g[i][j], f[i-1][j-1]g[i][j]) ②对最后一层&#xff0c;遍历一下&#xff0c;找到最大的答案。 代码&#xff08;上到下&#xff09;&#xff1a; #inclu…

基于Qt的串口通信工具

程序介绍 该程序是一个基于Qt的串口通信工具&#xff0c;专用于ESP8266 WiFi模块的AT指令配置与调试。主要功能包括&#xff1a; 1. 核心功能 串口通信&#xff1a;支持串口开关、参数配置&#xff08;波特率、数据位、停止位、校验位&#xff09;及数据收发。 AT指令操作&a…

第5篇:Linux程序访问控制FPGA端LEDR<三>

Q&#xff1a;如何具体设计.c程序代码访问控制FPGA端外设&#xff1f; A&#xff1a;以控制DE1-SoC开发板的LEDR为例的Linux .C程序代码。头文件fcntl.h和sys/mman.h用于使用/dev/mem文件&#xff0c;以及mmap和munmap内核函数&#xff1b;address_map_arm.h指定了DE1-SoC_Com…

【学生管理系统升级版】

学生管理系统升级版 需求分析&#xff1a;注册功能:登录功能&#xff1a;验证码规则&#xff1a;忘记密码&#xff1a; 实操&#xff1a;系统主页面注册功能登录功能忘记密码效果演示 需求 为学生管理系统书写一个登陆、注册、忘记密码的功能。     只有用户登录成功之后&…

CSS Grid布局:从入门到放弃再到真香

Flexbox 与 Grid 布局&#xff1a;基础概念与特点 Flexbox Flexbox&#xff08;Flexible Box Layout&#xff09;&#xff0c;即弹性盒布局模型&#xff0c;主要用于创建一维布局&#xff0c;能够轻松实现元素在一行或一列中的排列、对齐与分布。通过display: flex属性启用 Fl…

C++怎么调用类中的函数

1. 栈上对象 调用普通成员方法 普通成员方法需要通过类的对象实例&#xff08;或指针、引用&#xff09;来调用。 示例&#xff1a; class MyClass { public:void normalMethod() {std::cout << "普通成员方法被调用" << std::endl;} };int main() {M…

go游戏后端开发31:麻将游戏的碰牌与胡牌逻辑

以下是润色后的版本&#xff1a; 1. 碰牌逻辑 1.1 触发碰牌 当一个玩家弃牌后&#xff0c;其他玩家可以选择碰牌。如果当前玩家决定碰牌&#xff0c;系统需要通知所有玩家这一操作。碰牌操作完成后&#xff0c;当前玩家需要出一张牌&#xff0c;系统同样需要通知所有玩家。 …

十分钟机器学习之--------------线性回归

线性回归&#xff08;linear regression&#xff09;是一种基于数学模型的算法&#xff0c;首先假设数据集与标签之间存在线性关系&#xff0c;然后简历线性模型求解参数。在实际生活中&#xff0c;线性回归算法因为其简单容易计算&#xff0c;在统计学经济学等领域都有广泛的应…

学透Spring Boot — 017. 处理静态文件

这是我的《学透Spring Boot》专栏的第17篇文章&#xff0c;了解更多内容请移步我的专栏&#xff1a; Postnull CSDN 学透 Spring Boot 目录 静态文件 静态文件的默认位置 通过配置文件配置路径 通过代码配置路径 静态文件的自动配置 总结 静态文件 以前的传统MVC的项目…

深入理解 JavaScript 数组查找:如何高效获取特定元素

深入理解 JavaScript 数组查找&#xff1a;如何高效获取特定元素 深入理解 JavaScript 数组查找&#xff1a;如何高效获取特定元素引言问题场景解决方案1. 使用 Array.prototype.find()2. 处理 Proxy 对象的情况3. 备选方案&#xff1a;Array.prototype.filter()4. 传统 for 循…

HTML5+CSS3小实例:纯CSS绘制七巧板

实例:纯CSS绘制七巧板 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale…

[electron]自动注册IPC的解决方案

前言 主进程和渲染进程通过IPC进行通信&#xff0c;每次需要定义名称并注册&#xff0c;很多代码都是重复书写&#xff0c;并且如果主进程和渲染进程开发人员是同一个的话&#xff0c;很多东西都可以简化。 渲染进程通过ipcRenderer.invoke与主进程通信&#xff0c;主进程通过i…

JS—防抖和节流:1分钟掌握防抖和节流

个人博客&#xff1a;haichenyi.com。感谢关注 一. 目录 一–目录二–防抖三–节流四–进阶应用五–总结 二. 防抖&#xff08;Debounce&#xff09; 防抖&#xff08;Debebounce&#xff09;和节流&#xff08;Throttle&#xff09;都是前端开发中用于优化高频事件性能的两…

测试模板1

本篇技术博文摘要 &#x1f31f; 引言 &#x1f4d8; 在这个变幻莫测、快速发展的技术时代&#xff0c;与时俱进是每个IT工程师的必修课。我是盛透侧视攻城狮&#xff0c;一名什么都会一丢丢的网络安全工程师&#xff0c;也是众多技术社区的活跃成员以及多家大厂官方认可人员&a…

Nginx配置Http响应头安全策略,未设置X-Content-Type-Options响应头【原理扫描】

文章目录 前言一、漏洞扫描问题二、漏洞描述三、解决方法3.1、Nginx配置概览3.2、注意事项 四、感谢 前言 第三方安全检测机构甩过来一篇漏洞扫描报告&#xff0c;需要我们整改。 一、漏洞扫描问题 漏洞扫描问题如下&#xff1a; 未设置X-Content-Type-Options响应头【原理扫…

Gerapy二次开发:用户管理专栏新增与编辑页面开发

用户管理专栏新增与编辑页面开发 写在前面Vue表单设计与开发Vue的this.$refs功能实现前端Create.vueEdit.vueSubstance.vue效果预览后端urls.pyviews.py整体效果预览新增编辑总结欢迎加入Gerapy二次开发教程专栏! 本专栏专为新手开发者精心策划了一系列内容,旨在引领你深入探…

HOW - 实现 useClickOutside 或者 useClickAway

场景 在开发过程中经常遇到需要点击除某div范围之外的区域触发回调&#xff1a;比如点击 dialog 外部区域关闭。 手动实现 import { useEffect } from "react"/*** A custom hook to detect clicks outside a specified element.* param ref - A React ref object…

SpringBoot整合sa-token,Redis:解决重启项目丢失登录态问题

SpringBoot整合sa-token&#xff0c;Redis&#xff1a;解决重启项目丢失登录态问题 &#x1f525;1. 痛点直击&#xff1a;为什么登录状态会消失&#xff1f;2.实现方案2.1.导入依赖2.2.新增yml配置文件 3.效果图4.结语 &#x1f600;大家好&#xff01;我是向阳&#x1f31e;&…

Redis 持久化+性能管理+缓存

目录 一.Redis 持久化 1.持久化概述 2.持久化分类 3.RDB和AOF持久化 1.RDB持久化 2.RDB触发条件 &#xff08;1&#xff09;手动触发 &#xff08;2&#xff09;自动触发 &#xff08;3&#xff09; 执行流程​ &#xff08;4&#xff09;启动时加载 3.AOF持久化 &…

进程间通讯(IPC)

进程间通讯&#xff08;IPC&#xff09;详解&#xff1a;Linux 中的几种实现方式 在计算机操作系统中&#xff0c;进程间通讯&#xff08;IPC, Inter-Process Communication&#xff09;是一个至关重要的概念&#xff0c;尤其是在多进程操作系统中&#xff0c;进程间需要通过一…