黑马数据库学习笔记

news2024/11/28 2:33:22

课程地址

(基础篇)MySQL的启动

mysql 默认使用 3306 端口

在 centos下,启动 mysql 数据库:service mysqld start;

查看状态/启动/停止/重启:systemctl status/start/stop/restart mysqld;

登录到mysql数据库:mysql -uroot -psyc13140;

MySQL的层次结构为:用户,数据库,表

关系型数据库:RDBMS,Relational Database Management System。建立在关系模型理论的基础上,由多张连接的二维表组成的数据库

在这里插入图片描述

查看数据库和表:

show databases;

use db1;
select database();	# 查询当前数据库
show tables;

MySQL数据类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SQL语言分类

CRUD:cerate retrive update delete

在这里插入图片描述

DDL

数据定义语言:数据库的定义,数据库表的定义,字段的定义

show databases;		-- 查看所有数据库
select database();	-- 显示当前使用的数据库

create database [if not exists] mydb1 [default charset utf8mb4] [collate utf8mb4_general_ci];
alter database mydb1 character set utf8;
drop database [if exists] mydb1;

show tables;
create table emp(
	id int [comment ""],
	name varchar(20) [comment ""],
	sex int [comment ""],
	birthday date [comment ""],
	salary double [comment ""],
	hiredate date [comment ""],
	resume text [comment ""]
) [comment ""];

desc emp;	# describe emp
show create table emp;	# 查看建表过程
drop table emp;

创建表

在这里插入图片描述

create table tb_user(
	id int comment "id",
	name varchar(20) comment "name",
	age int comment "age",
	gender varchar(1) comment "gender"
)comment "user table";

执行sql脚本:

source /home/daniel/scott.sql;

创建表练习:

在这里插入图片描述

create table emp(
	id int,
	work_no varchar(10),
	name varchar(10),
	gender char(1),
	age tinyint unsigned,
	id_card char(18),
	entry_date date
);

修改表

alter table emp add nickname varchar(20) comment "昵称";
alter table emp modify nickname varchar(25);
alter table emp change nickname username varchar(30);
alter table emp drop username;
alter table emp rename to worker;

删除表

drop table worker;
truncate table worker;	# 删除表,并重新创建该表

小结

# 数据库操作
show databases;
create database db_name;
user db_name;
select database();
drop database db_name;

# 表操作
show tables;
create table tb_name(field1, field2, ...);
desc tb_name;
show create table tb_name;
alter table tb_name add/modify/change/drop/rename to ...;
drop table tb_name;

DML

insert into tb_name(field1, field2, ...) values (value1, value2...);
insert into tb_name values(v1, v2);
# 批量添加数据
insert into tb_name (f1, f2, ...) values (value1, value2...), (value1, value2...), (value1, value2...);
insert into tb_name values (value1, value2...), (value1, value2...), (value1, value2...);

对于这样一张表:

create table emp(
	id int,
	name varchar(20),
	sex int,
	birthday date,
	salary double,
	hiredate date,
	resume text
);
# insert into tb(field1, field2, field3) values(value1, value2, value3);
insert into emp values(1,'daniel',1,'1999-10-19',50000,'2023-10-10','i hope so');
update emp set salary=salary+5000;
delete from emp where name='daniel';
delete from emp;	# 删除所有数据!

如果插入的数据已存在,则使用 ignore 修饰 insert

insert ignore into actor values(3, "ED", "CHASE", "2006-02-15 12:34:33");

DQL

SELECT
	字段列表
FROM
	表名列表
WHERE
	条件列表
GROUP BY
	分组字段列表
HAVING
	分组后条件列表
ORDER BY
	字段1 排序方式1, 字段2 排序方式2
LIMIT	# 分页
	[<offset>,] <row count>]
select deptno, dname, loc from dept;
select empno, ename, sal, sal*12 as package from emp;	# as起别名
select distinct city from user_profile;		# 去重

where

在这里插入图片描述

select * from emp where sal=800 and deptno=20;	# 查找部门编号为20,薪水为800的员工
select * from emp where deptno in (20, 30);		# 查找部门编号为20或30的员工
select name from emp where idcard is null;

在解析where条件时, 是从右向左解析的,所以应该将容易假的值放在右边,利用逻辑短路特性

like

%:匹配任意多个字符
_:匹配任意一个字符

select * from emp where ename like 'S%';	# 查询名字以S开头的员工
select * from emp where ename like '____';	# 查询名字中有四个字母的员工

group by

在这里插入图片描述

# 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义
select gender, count(*) from emp group by gender;
select gender, avg(age) from emp group by gender;

# 查询年龄小于45岁的员工,并根据工作地分组,获取员工数量>=3的地址
select workaddress, count(*)
from emp
where age < 45
group by workaddress
having count(*) >= 3;

order by

select * from emp order by age asc;
select * from emp order by entrydate desc;
select * from emp order by age asc, entrydate desc;

limit

select * from article limit 1,3		# 取第2 3 4条数据

DQL练习:

在这里插入图片描述

select * from emp where age <= 23 and age >= 20;
select * from emp where gender = "男" and age between 20 and 40 and name like "___";
select gender, count(*) from emp where age < 60 group by gender;
select name, age from emp where age <= 35 order by age, entrydate desc;
select * from emp where gender = "男" and (age between 20 and 40) order by age, entrydate limit 5;

DQL的执行顺序

在这里插入图片描述

函数

聚合函数

将一数据作为一个整体,进行纵向计算。包括:count max min avg sum等

主要应用于 group by 分组操作

select count(*) from emp;	# null不参与聚合函数计算
select avg(age) from emp;

字符串函数

在这里插入图片描述

select concat("hello", "mysql");
select lower("Hello");
select upper("Hello");
select lpad("1", 5, "0");
select rpad("1", 5, "0");
select trim("   hello world   ");
select substring("hello, mysql", 1, 5);

-- 使用逗号分隔结果
select dept_no, group_concat(emp_no) as employees
from dept_emp
group by dept_no;

在这里插入图片描述

update emp set workno = lpad(workno, 5, "0");

数值函数

在这里插入图片描述

select ceil(1.4);
select floor(1.9);
select mod(7, 4);
select rand();	// 生成 (0, 1) 之间的验证码
select round(3.1415926, 3);

通过数据库的函数,生成一个6位数的随机验证码:

select lpad(round(rand() * 1000000, 0), 6, "0");

日期函数

在这里插入图片描述

取日期中的年月日:

select curdate();
select curtime();
select now();

select year(now());
select month(now());
select day(now());

select date_add(now, interval 70 day);
select datediff("2021-12-01", "2021-11-01");

查询所有员工的入职天数,并根据入职天数倒序排序:

select name, datediff(now(), entrydate) as days from emp order by days desc;

求时间差:

timestampdiff(MINUTE, start_time, submit_time)

流程控制函数

在这里插入图片描述

select if(true, "ok", "default");
select ifnull("ok", "default");
select ifnull(null, "default");

查询emp表的员工姓名和工作地址,如果工作地址为北京或上海,则显示一线城市;否则显示二线城市

select
	name,
	( case workaddress 
    	when "北京" then "一线城市"
    	when "上海" then "一线城市"
    	else "二线城市"
     end ) as "工作地点"
from emp;

根据成绩显示结果:

select
	name,
	( case
		when score >= 85 then "优秀"
		when score >= 60 then "及格"
		else "不及格"
     end) as "成绩"
from stu;

约束

在这里插入图片描述
在这里插入图片描述

create table user (
	id int primary key auto_increment,
    name varchar(10) not null unique,
    age int check(age > 0 and age <=120),
    status char(1) default "1",
    gender char(1)
)comment "用户表"

外键约束

外键用于建立两张表数据之间的连接,从而保证数据的一致性和完整性

在这里插入图片描述

alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
alter table emp drop foreign key fk_emp_dept_id;

一般是不允许直接删除父表的外键记录的,因为子表有很多记录关联到了父表

删除更新行为

在这里插入图片描述

alter table emp
add constraint fk_emp_dept_id
foreign key (dept_id)
references dept(id)
on update cascade
on delete cascade;

多表查询

多表关系

多对一:

在这里插入图片描述

多对多:创建一张中间表

在这里插入图片描述

一对一:一般用作单表拆分

在这里插入图片描述

多表查询

多表查询的过程其实就是构造多个表的一个笛卡尔积,然后利用连接条件筛选出正确的结果,消除无效的笛卡尔积

在这里插入图片描述

select * from emp, dept where emp.dept_id = dept.id;

在这里插入图片描述

内连接

查询两张表的交集部分

隐式内连接:

select emp.name, dept.name from emp, dept where emp.dept_id = dept.id;

select e.name, d.name
from emp as e, dept as d	-- 如果已经为表起了别名,则后面引用表时不能再用原表名,必须使用别名
where e.dept_id = d.id;

显式内连接(使用 on 连接):

select e.name, dept.name
from emp as e inner join dept as d
on emp.dept_id = dept.id;

外连接

select * from tb1 left outer join tb2 on cond;		-- 查询左表
select * from tb1 right outer join tb2 on cond;		-- 查询右表
-- 查询emp表的所有数据,和对应的部门信息
select e.*, d.name
from emp as e left outer join dept as d
on e.dept_id = d.id;

-- 查询dept表的所有数据,和对应的员工信息
select d.*, e.*
from emp as e right outer join dept as d
on e.dept_id = d.id;

自连接

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

查询员工,及其所属领导的名字:

select worker.name, leader.name from emp as worker, emp as leader where worker.managerid = leader.id;

select worker.name, leader.name
from emp as worker inner join emp as leader
on worker.managerid = leader.id;

查询所有员工emp及其领导的名字emp,如果员工没有领导,也需要查询出来(使用外连接):

select a.name as "worker", b.name as "leader"
from emp as a left outer join emp as b
on a.managerid = b.id;

联合查询

将多次的查询结果合并起来,形成一个新的查询结果集

-- 将薪资 < 5000的员工和年龄 > 50的员工全部查询出来
select * from emp where salary < 5000
union all
# union -- 会去重
select * from emp where age > 50;

联合查询的多张表的列数和每列的字段类型必须保持一致。union会将结果去重;union all会直接将数据合并在一起

子查询

在这里插入图片描述

标量子查询

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

select * from emp where dept_it = (select id from dept where name = "销售部");

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

select * from emp where entrydate > (select entrydate from emp where name = "方东白");

列子查询

在这里插入图片描述

-- 查询销售部和市场部的所有员工信息
select * from emp where dept_id in (select id from dept where name = "销售部" or name = "市场部");

-- 查询比财务部所有人工资都高的员工信息
select *
from emp
where salary > all(  -- max(
    select salary
    from emp
    where dept_id = (
        select id
        from dept
        where name = "财务部"
    )
);

-- 查询比研发部任意一人工资高的员工信息
select * 
from emp 
where salary > any(  -- min( -- some(
    select salary
    from emp
    where dept_id = (
        select id
        from dept
        where name = "研发部"
    )
);

行子查询

-- 查询与“张无忌”薪资和直属领导相同的员工信息
select *
from emp
where (salary, managerid) = (	--( tuple equals
	select salary, managerid
    from emp
    where name = "张无忌"
);

表子查询

-- 查询与“鹿仗客”,“宋远桥”的职位和薪资相同的员工信息
select *
from emp
where (job, salary) in (
	select job, salary
	from emp
	where name = "鹿仗客" or name = "宋远桥"
);

-- 查询入职日期是“2006-01-01”之后的员工信息和部门信息
select e.*, d.*
from (
	select *
	from emp
	where entrydate > "2006-01-01"
) as e left join dept as d
on e.dept_id = d.id;

案例练习

三张表:emp dept salgrade

在这里插入图片描述

-- 1 查询员工的姓名,年龄,职位,部门信息 隐式内连接
select e.name, e.age, e.job, d.name
from emp as e, dept as d
where e.dept_id = d.id;

-- 2 查询年龄小于30岁的员工姓名,年龄,职位,部门信息 显式内连接
select e.name, e.age, e.job d.name
from emp as e inner join dept as d
on e.dept_id = d.id
where e.age < 30;

-- 3 查询拥有员工的部门id,部门名称
select distinct d.id, d.name
from emp as e, dept as d
where e.dept_id = d.id;

-- 4 查询所有age > 40的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来 左外连接
select e.*, d.*
from emp as e left outer join dept as d
on e.dept_id = d.id
where e.age > 40;

-- 5 查询所有员工的工资等级
select e.*, s.grade
from emp as e, salgrade as s
where e.salary >= s.lowsal and e.salary <= s.highsal;

-- 6 查询“研发部”所有员工的信息及工资等级
select e.*, s.grade
from emp as e,
	 salgrade as s,
	 dept as d
where
	e.dept_id = d.id and
	e.salary between s.lowsal and s.highsal and
	d.name = "研发部";
	
	
-- 7 查询“研发部”员工的平均工资
select avg(e.salary)
from emp as e, dept as d
where e.dept_id = d.id and d.name = "研发部";

-- 8 查询工资比“灭绝”高的员工信息 标量子查询
select *
from emp
where salary > (
	select salary from emp where name = "灭绝"
);

-- 9 查询比平均薪资高的员工信息 
select *
from emp
where salary > (
	select avg(salary) from emp
);

-- 10 查询低于本部门平均工资的员工信息
select *
from emp as e1
where e1.salary < (
	select avg(e2.salary)
    from emp as e2
    where e1.dept_id = e2.dept_id
);

-- 11 查询所有部门的信息,并统计部门的员工人数
select
	d.id,
	d.name, 
	(select count(*) from emp as e where e.dept_id = d.id) as "人数"
from dept as d;

-- 12 查询所有学生的选课情况,展示出学生名称,学号,课程名称
select s.name, s.no, c.name
from student as s, course as c, student_course as sc
where s.id = sc.studentid and c.id = sc.courseid;

事务

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

在这里插入图片描述

MySQL的事务默认是自动提交的,也就是说,当执行一条DML语句,MySQL会立即地隐式提交事务

建表:

create table account (
    id int primary key auto_increment,
    name varchar(10),
    money int
);

insert into account
values
    (1, "zhangsan", 2000),
    (2, "lisi", 2000);
    
-- 恢复数据
update account set money = 2000 where name = "zhangsan" or name = "lisi";
-- 查看/设置事务提交方式
select @@autocommit;
set @@autocommit = 0;
-- 提交事务
commit
-- 回滚事务
rollback

如果设置了手动提交,则执行DML语句后,数据库的内容不会发生变化,直到commit

set @@autocommit = 0;
update account set money = money - 1000 where name = "zhangsan";
update account set money = money + 1000 where name = "lisi";

-- 直到执行commit
commit

临时开启一个事务:

begin -- start transaction 

总结,使用事务有2种方式:

  • 通过 set @@autocommit = 0 关闭自动提交,全部手动commit/rollback
  • 通过 begin 开启一个事务

ACID

在这里插入图片描述
并发事务的3个问题,多个并发事务在操作数据库时引发的问题:

  • 脏读:一个事务读到另一个事务还没有提交的数据
  • 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同(中间被update了)
  • 幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在

事务的隔离级别

在这里插入图片描述

查看/设置事务的隔离级别:

select @@transaction_isolation;
-- set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable};
set session transaction isolation level read uncommitted;

事务隔离级别越高,数据越安全,但性能越低

总结:

事务简介:一组操作的集合,这组操作要么全部执行成功,要么全部执行失败

开启事务:start transcaction

提交/回滚事务:commit/rollback

事务四大特性:原子性、一致性、隔离性、持久性

并发事务问题:脏读、不可重复度、幻读

事务隔离级别:read uncommitted/read committed/repeatable read/serializable

建表:

create table account(
       name varchar(10),
       money int
     );
insert into account values ("zhangsan", 2000), ("lisi", 2000);

脏读演示

在这里插入图片描述

不可重复读演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

幻读演示

幻读会抛出主键重复异常:查询时查询不到,但是插入时报告主键重复

(进阶篇)存储引擎

存储引擎:存储数据,建立索引,更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的(同一个库下的多张表可以有不同的存储引擎),所以存储引擎通常也被称为表类型

show create table account;

CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `money` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

默认的存储引擎是 InnoDB

查看支持的存储引擎:

show engines;
-- 创建表my_myisam,并指定MyISAM存储引擎
create table my_myisam (
	id int,
    name varchar(10)
) engine = MyISAM;

-- 创建表my_memory,并指定Memory存储引擎
create table my_memory (
	id int,
    name varchar(10)
) engine = Memory;

InnoDB

InnoDB 引擎的3大特性(优点):事务、外键、行级锁

  • DML操作遵循ACID模型,支持事务
  • 行级锁,提高并发访问性能
  • 支持外键约束,保证数据的完整性和正确性

innodb_file_per_table

在这里插入图片描述

MyISAM

  • 不支持事务和外键
  • 只支持表锁,不支持行锁
  • 访问速度快

Memory

表数据存放在内存中,只能作为临时表或缓存来使用。支持hash索引(只能进行等值匹配,不能进行范围查找)

在这里插入图片描述

索引

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

select * from user where age = 45;

在这里插入图片描述

索引的缺点:

  • 占用更多的空间
  • 增加了insert update delete的开销

在这里插入图片描述

B 树结构:

在这里插入图片描述

在这里插入图片描述

面试题:为什么InnoDB存储引擎选用B+tree索引结构?

在这里插入图片描述
索引的分类:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于这样一条查询语句:

select * from user where name = "Arm";

它会先走二级索引查到id值,然后再用id值从聚集索引中查到row数据,这个过程被称为回表查询

以下的2条SQL语句,哪个执行效率高?

select * from user where id = 10;		-- id是主键
select * from user where name = "Arm";	-- name有索引

答:第一个效率更高,因为它无需回表查询

索引语法

create [unique|fulltext] index index_name on table_name(col_name, ...);		-- 创建索引
show index from table_name;
drop index index_name from table_name;

索引可以关联到多个列(字段):

  • 如果索引关联到单个字段,称之为单列索引
  • 如果索引关联到单个字段,称之为多列索引,或联合索引
    在这里插入图片描述
create index idx_user_name on tb_user(name);
create unique index idx_user_phone on tb_user(phone);
create index idx_user_pro_age_stu on tb_user(profession, age, status);
create index idx_user_email on tb_user(email);

drop index idx_user_email from tb_user;

SQL性能分析

查看执行频次

查看当前数据库的操作是以“增删改”等 DML 语句为主,还是以查询 DQL 语句为主。如果主要是 DQL,可以考虑建立索引加速查询

mysql> show global status like "Com_______";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 0     |
| Com_delete    | 0     |
| Com_insert    | 0     |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 125   |
| Com_signal    | 0     |
| Com_update    | 0     |
| Com_xa_end    | 0     |
+---------------+-------+
10 rows in set (0.00 sec)

慢查询日志

记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10s)的所有SQL语句的日志。MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件 /etc/my.cnf 中配置如下信息:

slow_query_log=1
long_query_time=2

show_profiles

select @@have_profiling;		# 查看是否支持profile
select @@profiling;				# 查看profiling是否打开
set profiling=1;				# 打开profiling
show profiles;
show profile for query query_id;
show profile cpu for query query_id;

explain

explain 或者 desc 获取MySQL如何执行 select 语句的信息,包括在select语句执行过程中表如何连接和连接的顺序(sql语句的执行计划)

explain select * from tb1 where cond1;		# 结果的id值越大,越优先执行;如果id相同,从上到下执行

在这里插入图片描述

explain 执行计划各字段含义:

  • id:select 查询的序列号,表示查询中执行 select 子句或操作表的顺序(结果的id值越大,越优先执行;如果id相同,从上到下执行)
  • select_type:simple(简单表,不需要表连接或者子查询)、primary(主查询,即外层的查询)、union(联合查询 union 后面的查询语句)、subquery(子查询)
  • type:连接类型,性能由好到差:NULL、system、const、eq_ref、range、index、all
  • possible_key:可能使用的索引
  • key:实际使用的索引
  • key_len:索引使用的字节数,该值为索引字段最大可能长度
  • rows:MySQL 预估的必须执行查询的行数
  • filtered:返回结果行数占需要读取行数的百分比,值越大越好

最左前缀法则

例如对于联合索引 idx_user_profession_age_status

在这里插入图片描述

在这里插入图片描述

最左索引存在即可,与顺序无关(会自动调整),比如下面的 sql,索引长度仍然是 54(全部索引):

select * from tb_user where status = "0" and age = 31 and profession = "软件工程"

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

尽量使用 >=<=,这样索引不会失效

select * from tb_user where profession = "SE" and age > 30 and status = '0';		# status失效
select * from tb_user where profession = "SE" and age >= 30 and status = '0';		# status正常

索引失效

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

select * from tb_user where phone = "19858190715";		# 索引有效
select * from tb_user where substring(phone, 10, 2) = "15";		# 索引失效

字符串不加引号,索引会失效(隐式类型转换)

select * from tb_user where phone = 19858190715;		# 索引失效
select * from tb_user where profession = "SE" and age = 30 and status = 0; 	# status索引失效

如果仅在尾部进行模糊匹配,索引不会失效。如果是在头部进行模糊匹配,索引将会失效

select * from tb_user where profession like "软件%";		# 索引有效
select * from tb_user where profession like "%工程";		# 索引失效
select * from tb_user where profession like "%工%";		 # 索引失效

对于or连接的条件,如果一侧有索引,另一侧没有索引,则全部索引失效

select * from tb_user where phone = "19858190715" or age = 23;		# 索引失效
select * from tb_user where id = 10 or age = 23;					# 索引失效

数据分布对索引的影响:如果MySQL评估使用索引比全表扫描还慢,则不使用索引(如结果集就是全表数据)

select * from tb_user where id >= 1;					# 直接全表,不走索引
select * from tb_user where profession is not null;		  # 直接全表,不走索引

SQL提示

在SQL语句中加入一些人为的提示来达到优化的操作目的(同时存在联合索引和单列索引时指定使用单列索引

# use index
select * from tb_user use index(idx_user_pro) where profession = "SE";

# ignore index
select * from tb_user ignore index(idx_user_pro) where profession = "SE";

# force index
select * from tb_user force index(idx_user_pro) where profession = "SE";

覆盖索引

explain 返回的 Extra 的 2 种情况:

  • using index condition:查询使用了索引,但是需要回表查询数据(低效)
  • using where; using index:查询使用了索引,但是需要的数据在索引列中都能直接找到,所以不需要回表查询数据(高效)
select id, profession, age, status from tb_user where profession = "SE" and age = 31 and status = '0';

-- name 字段必须通过回表查询才能得到
select id, profession, age, status, name from tb_user where profession = "SE" and age = 31 and status = '0';

--  一般会引发回表查询
select * from tb_user where profession = "SE" and age = 31 and status = "0";

在这里插入图片描述

为什么不推荐 select *:极易产生回表查询

练习题:下面哪条SQL语句会产生回表查询?

select * from tb_user where id = 2;
select id, name from tb_user where name = "Arm";
select * from tb_user where name = "Arm";		# 这条语句会产生回表查询,性能低

一张表,有4个字段(id username password status),由于数据量大,需要对以下SQL语句进行优化,该如何进行?

select id, username, password from tb_user where username = "itcast";

答:对username password建立联合索引,避免回表查询(username 加速查询;passwrod 避免回表)

前缀索引

在保证选择性的前提下,取很长的字符串的一部分建立索引

在这里插入图片描述

create index idx_user_email_5 on tb_user(email(5));

在这里插入图片描述

通过前缀索引获取到数据后需要再检查是否满足where条件

在业务场景中,如果存在多个查询条件,考虑针对查询字段建立索引时,建议建立联合索引,而非单列索引

select id, phone, name from tb_user where phone = "19888876539" and name = "daniel";

create unique index idx_user_phone_name on tb_user(phone, name);
select id, phone, name from tb_user use index(idx_user_phone_name) where phone = "19888876539" and name = "daniel";	# 建立联合索引后,不再需要回表查询

联合索引的B+树结构:

在这里插入图片描述

每个节点有2个键值数据

在这里插入图片描述

索引失效情景:

  • 不遵循“最左前缀法则”
  • 在索引列上进行函数运算
  • 字符串不加引号
  • like模糊匹配,% 在前面
  • or连接的条件,一侧有索引,另一侧没有索引,索引失效
  • MySQL评估,走全表扫描比走索引还快,索引失效(数据分布的影响)

SQL优化

insert优化

  • 使用批量插入

  • 手动提交事务

    • start transaction;
      insert into tb_test values(1, "tom"), (2, "cat"), (3, "jerry");
      insert into tb_test values(4, "tom"), (5, "cat"), (6, "jerry");
      insert into tb_test values(7, "tom"), (8, "cat"), (9, "jerry");
      commit;
      
  • 主键顺序插入

  • 如果一次性插入大量数据,使用 insert 插入性能低,此时可以使用 load 指令进行插入

    • mysql --local-infile -uroot -ppswd	# 连接数据库时加入--local-infile选项
      set global local_infile = 1;
      load data local infile "/root/sql1.log" into table "tb_user" fields terminated by "," lines terminated by "\n";
      

主键优化

在这里插入图片描述

根据叶子节点的有序性,主键应顺序插入;如果主键乱序插入,会产生页分裂现象,浪费了时间

在这里插入图片描述

当删除一行记录时,并不会进行物理删除,而只会进行标记。当删除的记录达到 50% 时,会进行页合并以节省空间

在这里插入图片描述
主键设计原则:

  • 尽量降低主键长度
  • 插入数据时尽量选择顺序插入,选择使用 AUTO_INCREMENT 自增主键
  • 尽量不要使用UUID做主键或其他自然主键
  • 业务操作时,尽量避免对主键的修改

order by优化

mysql 有 2 种排序方式:

  • using index:排序过程直接通过有序索引直接返回数据,效率高
  • using filesort:需要将返回的结果在排序缓冲区中排序,所有不是通过索引直接返回排序结果的排序都称为 filesort 排序
-- age 和 phone 没有索引
select id, age, phone from tb_user order by age;		-- using filesort
select id, age, phone from tb_user order by age, phone;		-- using filesort

create index idx_user_id_age on tb_user(age, phone);
select id, age, phone from tb_user order by age;		-- using index
select id, age, phone from tb_user order by age, phone;		-- using index
select id, age, phone from tb_user order by age desc, phone desc;		-- backward index scan; using index
select id, age, phone from tb_user order by phone, age;		-- using index; using filesort
select id, age, phone from tb_user order by age asc, phone desc;		-- using index; using filesort

-- 建立排序方式不同的索引
create index idx_user_id_age_ad on tb_user(age asc, phone desc);
select id, age, phone from tb_user order by age asc, phone desc;		-- using index

在这里插入图片描述

order by优化:

  • 根据排序字段建立适当的索引,多字段排序时也遵循最左前缀法则
  • 尽量使用覆盖索引
  • 多字段排序,一个升序一个降序,需要注意联合索引在创建时的规则(一个升序一个降序)
  • 如果不可变地出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size

group by优化

select profession, count(*) from tb_user group by profession;	-- using temporary

create index idx_user_pro_age_sta on tb_user(profession, age, status);	-- 创建联合索引
select profession, count(*) from tb_user group by profession;	-- using index
select profession, count(*) from tb_user group by age;			-- using index; using temporary
select profession, count(*) from tb_user group by profession, age;	-- using index
select profession, count(*) from tb_user where profession = "SE" group by age;	-- using index

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

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

limit优化

select * from tb_sku limit 10, 10;		-- 0s
select * from tb_sku limit 1000000, 10; 	-- 3s
select * from tb_sku limit 5000000, 10; 	-- 11s

limit 的查询时间随着 offset 的增大而增大。因为它需要将前面的所有数据都排序,仅返回少量数据

使用覆盖索引 + 多表联查(子查询)进行优化:

select s.* from tb_sku as s, (select id from tb_sku order by id limit 9000000, 10) as a where s.id = a.id;

count优化

在这里插入图片描述

count的几种用法:

-- count(*) count(pk) count(字段) count(1)
select count(1) from tb_user;	-- 24

在这里插入图片描述

update优化

核心:尽量根据主键/索引字段进行数据更新,避免行锁升级为表锁

对于这样一张表:

create table course (
    id int primary key auto_increment,
    name varchar(10)
);

insert into course (id, name)
values (1, "Java"),
       (2, "PHP"),
       (3, "MySQL"),
       (4, "Hadoop");

InnoDB引擎3大特性:事务,外键,行级锁

行级锁的条件下,下面的两个事务得以成功并发执行:

begin; 	-- transcation1
update course set name = "javaEE" where id = 1;
commit;

begin; 	-- transcation2
update course set name = "Kafuka" where id = 4;
commit;

但是如果where条件使用name:

begin; 	-- transcation1
update course set name = "springboot" where name = "PHP";	-- 行锁升级为表锁
commit;

begin; 	-- transcation2
update course set name = "Kafka" where id = 4;		-- 等待上一条update提交
commit;

name没有索引,被加了表锁。所以使用update更新数据时要根据索引字段进行更新,解决上面的问题的办法就是为 name 字段建立索引:

create index idx_course_name on course(name);

在这里插入图片描述

小结

在这里插入图片描述

视图

视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。

-- 创建视图
create [or replace] view view_name[(column_name)] as (select from 基表) [with [cascaded | local] check option];

对于这样一张表:

create table student (
    id int primary key auto_increment,
    name varchar(20),
    no char(10)
);

insert into student (id, name, no)
values (1, "daiqisi", "2000100100"),
       (2, "xiexun", "2000100102"),
       (3, "yintianzheng", "2000100103"),
       (4, "weiyixiao", "2000100104");

创建一个视图:

create or replace view stu_v1 as (select id, name from student where id <= 10);
-- 查看创建视图的语句
show create view view_name;
-- 查看视图数据
select * from view_name;

查询视图:

select * from stu_v1 where id < 3;

修改视图:

create or replace view view_name[(column_name)] as (select from 基表) [with [cascaded | local] check option];
alter view view_name[(column_name)] as (select from 基表) [with [cascaded | local] check option];

alter view stu_v1 as select id, name from student where id <= 10;

删除视图:

drop view if exists view_name;

检查选项

drop view if exists  stu_v1;
create or replace view  stu_v1 as select id, name from student where id <= 20;

select * from stu_v1;

insert into stu_v1 values(6, "Tom");    -- 插到了基表
insert into stu_v1 values(30, "Tom");   -- 插入了基表,但是在本视图查不到

为了避免出现上面这种基表和视图数据不一致的情况,增加检查选项

cascaded

create or replace view  stu_v1 as select id, name from student where id <= 20 with cascaded check option;

select * from stu_v1;

insert into stu_v1 values(6, "Tom");    -- 插到了基表
insert into stu_v1 values(30, "Tom");   -- 插入失败

视图可以基于视图创建,对于级联的 check option,不仅会检查当前的 check option,还会检查上一级视图的 check option

在这里插入图片描述

local

create or replace view  stu_v4 as select id, name from student where id <= 15;

insert into stu_v4 values(6, "Tom");    -- 成功
insert into stu_v4 values(16, "Tom");   -- 成功

create or replace view  stu_v5 as select id, name from stu_v4 where id >= 10 with local check option;
insert into stu_v5 values(13, "Tom");   -- 成功
insert into stu_v5 values(17, "Tom");   -- 成功,因为 stu_v4 没有定义 local check option.如果是 cascaded 则不会成功

create or replace view stu_v6 as select id, name from stu_v5 where id < 20;
insert into stu_v6 values(14, "Tom");   -- 成功

视图更新

只能在视图和基表的行是一对一的关系下才能更新视图

在这里插入图片描述

create view stu_v_count as select count(*) from student;
insert into stu_v_count values(10);     -- The target table stu_v_count of the INSERT is not insertable-into

视图的作用:

  • 简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件
  • 安全:通过视图用户只能查询和修改他们所能见到的数据数据库可以授权,但不能授权到数据库特定行和特定的列上
  • 数据独立:视图可帮助用户屏蔽真实表结构变化带来的影响

视图案例

根据如下需求,定义视图:

  1. 为了保证数据库表的安全性,开发人员在操作 tb_user 表时,只能看到的用户的基本字段,屏蔽手机号和邮箱两个字段
  2. 查询每个学生所选修的课程(三张表联查),这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图
create view tb_user_v1 as select id, name, profession, age, gender, status, createtime from tb_user;
select * from tb_user_v1;

create view tb_student_course_v1 as
    select s.name as student_name,
           s.no as studnet_no,
           c.name as course_name
    from
        student as s,
        student_course as sc,
        course as c
    where
        s.id = sc.studnetid and c.id = sc.courseid;

存储过程

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

特点:

  • 封装,复用
  • 可以接受参数,也可以返回数据
  • 减少网络交互,效率提升
create procedure p1()
begin
    select count(*) from student;
end;

call p1();

select * from information_schema.ROUTINES where ROUTINE_SCHEMA = "itcast";
show create procedure p1;

drop procedure if exists p1;

在命令行中,执行创建存储过程的 SQL 时,需要通过关键字 delimiter 指定 SQL 语句的结束符(在 datagrip 则没有这个问题)

delimiter $$

变量

系统变量

在这里插入图片描述
查看系统变量:

show variables;
show session variables;

show session variables like 'auto%';

select @@autocommit;    -- 查看特定系统变量的值
select @@global.autocommit;

设置系统变量:

set session autocommit = 0;
select @@session.autocommit;

如果没有指定 SESSION/GLOBAL,默认是 SESSION

mysqld 重启后,全局参数会失效,若不想失效可以在 /etc/my.cnf 配置

用户自定义变量

在这里插入图片描述

set @myname = 'itcast';     -- 无需提前声明
set @myage := 10;
select count(*) into @mycnt from student;

select @myname, @myage, @mycnt;

局部变量

在这里插入图片描述

create procedure p2()
begin
    declare stu_count int default 0;
    select count(*) into stu_count from student;
    select stu_count;
end;

call p2();

if

if c1 then
	...
elseif c2 then
	...
else
	...
end if;
create procedure p3()
begin
    declare score int default 58;
    declare result varchar(10);

    if score >= 85 then
        set result := 'great';
    elseif score >= 60 then
        set result := 'pass';
    else
        set result := 'failure';
    end if;
    select result;
end;

call p3();

参数

在这里插入图片描述

create procedure p4(in score int, out result varchar(10))
begin
    if score >= 85 then
        set result := 'great';
    elseif score >= 60 then
        set result := 'pass';
    else
        set result := 'failure';
    end if;
end;

call p4(90, @result);
select @result;

-- 将 200 分制的分数转化为百分制并返回
create procedure p5(inout score double)
begin
    set score := score / 2;
end;

set @score = 180;
call p5(@score);
select @score;  -- 90

case

在这里插入图片描述

create procedure p6(in month int)
begin
    declare result varchar(10);
    case
        when month >= 1 and month <= 3 then
            set result := '1';
        when month >= 4 and month <= 6 then
            set result := '2';
        when month >= 7 and month <= 9 then
            set result := '3';
        when month >= 10 and month <= 12 then
            set result := '4';
        else
            set result := 'invalid';
    end case;
    select concat(month, ': ', result);
end;

call p6(8);

while

在这里插入图片描述

create procedure p7(in n int)
begin
    declare total int default 0;
    while n > 0 do
        set total := total + n;
        set n := n - 1;
    end while;
    select total;
end;

call p7(10);

repeat

在这里插入图片描述

create procedure p8(in n int)
begin
    declare total int default 0;
    repeat
        set total := total + n;
        set n := n - 1;
    until  n <= 0 end repeat;
    select total;
end;

call p8(100);

loop

在这里插入图片描述
求从 1 ~ n 的和:

create procedure p9(in n int)
begin
    declare total int default 0;
    sum: loop
        if n <= 0 then
            leave sum;
        end if;
        set total := total + n;
        set n := n - 1;
    end loop;
    select total;
end;

call p9(100);

计算 1 ~ n 之间偶数累加的值:

create procedure p10(in n int)
begin
    declare total int default 0;
    sum: loop
        if n <= 0 then
            leave sum;
        end if;

        if n % 2 = 1 then
            set n := n - 1;
            iterate sum;
        end if;

        set total := total + n;
        set n := n - 1;
    end loop;
    select total;
end;

call p10(100);

cursor

在这里插入图片描述

需求:根据传入的参数 uage,来查询用户表tb_user中,所有的用户年龄小于等于 uage 的用户姓名(name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表(id,name,profession)中

create procedure p11(in uage int)
begin
    declare uname varchar(100);
    declare upro varchar(100);
    declare u_cursor cursor for select name, profession from tb_user where age <= uage;

    drop table if exists tb_user_pro;
    create table if not exists tb_user_pro(
        id int primary key auto_increment,
        name varchar(100),
        profession varchar(100)
    );

    open u_cursor;
    while true do
        fetch u_cursor into uname, upro;
        insert into tb_user_pro values (null, uname, upro);
    end while;
    close u_cursor;
end;

call p11(30);

handler

在这里插入图片描述

declare exit handler for SQLSTATE '02000' close u_cursor;   -- 类似于出错后的中断回调
declare exit handler for not found close u_cursor;   -- 类似于出错后的中断回调
create procedure p11(in uage int)
begin
    declare uname varchar(100);
    declare upro varchar(100);
    declare u_cursor cursor for select name, profession from tb_user where age <= uage;

    declare exit handler for SQLSTATE '02000' close u_cursor;   -- 类似于出错后的中断回调
    -- declare exit handler for not found close u_cursor;   -- 类似于出错后的中断回调

    drop table if exists tb_user_pro;
    create table if not exists tb_user_pro(
        id int primary key auto_increment,
        name varchar(100),
        profession varchar(100)
    );

    open u_cursor;
    while true do
        fetch u_cursor into uname, upro;
        insert into tb_user_pro values (null, uname, upro);
    end while;
    close u_cursor;
end;

call p11(30);

存储函数

在这里插入图片描述

需求:使用存储函数实现从 1 ~ n 的累计

create function f1(n int)
returns int deterministic
begin
    declare total int default 0;
    while n > 0 do
        set total := total + n;
        set n := n - 1;
    end while;
    return total;
end;

select f1(100);

触发器

在这里插入图片描述

行级触发器和语句级触发器的区别:比如一条 update 语句一次更新 5 行,行级触发器会被触发 5 次,而语句级触发器会被触发 1 次

insert

在这里插入图片描述

需求:通过触发器记录 user 表的数据变更日志(user_logs),包含增加,修改,删除

创建日志表:

create table if not exists user_logs(
    id int primary key auto_increment,
    operation varchar(20) not null,
    operate_time datetime not null,
    operate_id int not null,
    operate_params varchar(500)
) default charset=utf8;

插入数据触发器:

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('inserted data: id = ', new.id, ', name = ', new.name));
end;

update

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, 'update', now(), new.id, concat('updated data: id = ', new.id, ', name = ', new.name));
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, 'delete', now(), old.id, concat('deleted data: id = ', old.id, ', name = ', old.name));
end;

view:

  • 虚拟存在的表,不保存查询结果,只保存查询的 SQL 逻辑
  • 简单,安全,数据独立

procedure:

  • 事先定义并存储在数据库中的一段 SQL 语句的集合
  • 减少网络交互,提高性能,封装重用
  • 变量、if、case、参数(in/out/inout)、while、repeat、loop、cursor、handler

function:

  • 存储函数是有返回值的存储过程,参数类型只能为 IN 类型
  • 存储函数可以被存储过程替代

trigger:

  • 可以在表数据进行 insert、update、delete 之前或之后触发
  • 保证数据完整性、日志记录、数据校验

  • 全局锁:锁住数据库中的所有表
  • 表级锁:每次操作锁住整张表
  • 行级锁:每次操作锁住对应的行数据

全局锁

对整个数据库加锁,加锁后整个库就处于只读状态,后续的 DML语句、DDL 语句、已经更新操作的事务提交语句都将被阻塞

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

在这里插入图片描述

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

  • 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆
  • 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟

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

mysqldump --single-transaction -uroot -p123456 itcast> itcast.sql

表级锁

表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中

表级锁主要分为3类:

  • 表锁
  • 元数据锁(meta data lock,MDL)
  • 意向锁

表锁

表锁又可被分为2类:共享读锁&独占写锁

  • read lock(共享)
  • write lock(独占)

语法:

lock tables tb read/write;
unlock tables;

对于读锁,当前客户端和其他客户端都能读,但是都不能写

对于写锁,其他客户端不能读写,当前客户端能够读写

在这里插入图片描述

元数据锁

在这里插入图片描述
在这里插入图片描述

意向锁

如下图,线程 A 根据主键 update id = 3 的数据时,会自动加上行锁。此时如果线程 B 过来加表锁,它需要逐行检查每一行是否添加了行锁,效率很差

为了解决这个问题,引入意向锁:为了避免行锁与表锁之间的冲突,在 InnoDB 中引入了意向锁,使表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查

在这里插入图片描述
如果另一个任务试图在该表级别上应用共享或排它锁,则受到由第一个任务控制的表级别意向锁的阻塞。第二个任务在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁

意向锁不会与行级的共享 / 排他锁互斥

在这里插入图片描述
在这里插入图片描述

行级锁

在这里插入图片描述

行锁

在这里插入图片描述
在这里插入图片描述

如果不通过索引检索数据,InnoDB 会对表中的所有记录加锁,升级为表锁:
在这里插入图片描述

间隙锁

间隙锁可以防止幻读

在这里插入图片描述
在这里插入图片描述

小结

概述:

  • 在并发访问时,解决数据访问的一致性、有效性问题
  • 全局锁、表级锁、行级锁

全局锁:

  • 对整个数据库实例加锁,加锁后整个实例就处于只读状态
  • 性能较差,数据逻辑备份时使用

表级锁:

  • 操作锁住整张表,锁定粒度大,发生锁冲突的概率高
  • 表锁、元数据锁、意向锁

行级锁:

  • 操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最低
  • 行锁、间隙锁、临键锁

InnoDB 引擎

逻辑存储结构

在这里插入图片描述

架构

在这里插入图片描述

内存结构

在这里插入图片描述

磁盘结构

在这里插入图片描述

后台线程

将 InnoDB 缓存中存在的数据在合适的时机刷新到磁盘当中

在这里插入图片描述

事务原理

在这里插入图片描述

redo log

用于刷新脏页到磁盘发生错误时,进行数据恢复使用
在这里插入图片描述

刷新 redo log file 要比刷新 ibd 文件快得多,因为前者是随机磁盘 IO,后者是顺序磁盘 IO,性能更高

2个 redo log 是循环写的,并不会永久保留下来,占用空间不大

undo log

undo log 主要用于实现事务的原子性

回滚日志,用于记录数据被修改前的信息,作用含两个:提供回滚 和 MVCC(多版本并发控制)

undo log 和 redo log记录物理日志不一样,它是逻辑日志。可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然。当 update 一条记录时,它记录一条对应相反的 update 记录。当执行 rollback 时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚
undo log 销毁:undo log 在事务执行时产生,事务提交时,并不会立即删除 undo log,因为这些日志可能还用于MVCC
undo log 存储:undo log 采用的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含 1024 个 undo log segment

MVCC

在这里插入图片描述
在这里插入图片描述

隐式字段

在这里插入图片描述

undo log

回滚日志,在 insert、update、delete 的时候产生的便于数据回滚的日志

当 insert 的时候,产生的 undo log 日志只在回滚时需要,在事务提交后,可被立即删除

而 update、delete 的时候,产生的 undo log 日志不仅在回滚时需要,在快照读时也需要,不会立即被删除

对于这样一条记录:

在这里插入图片描述
其版本链如下:

在这里插入图片描述

read view

在这里插入图片描述

在这里插入图片描述

RC 级别分析

下图说明了事务 5 的第二个查询在 MVCC 版本链上匹配的过程,通过直接观察可以看出,事务 5 的第二次读取,能够读取到事务 3 提交的记录

首先匹配表中的记录,事务 ID 为 4,所有规则均不成立

接着匹配 undo log 版本链上的第一条数据,事务 ID 为3,满足第二条匹配规则:记录的事务 ID 小于当前活跃的最小事务 ID,说明该事务已经提交,可以读取

在这里插入图片描述

RR 级别分析

在这里插入图片描述

小结

逻辑存储结构:表空间、段、区、页、行

架构:内存结构&磁盘结构

MVCC:

  • 记录隐藏字段
  • undo log 版本链
  • readView

MySQL 管理

在这里插入图片描述

常用工具:

在这里插入图片描述

mysql:客户端工具,-e 执行 SQL 并退出

mysqladmin:MySQL 管理工具

mysqlbinlog:二进制查看工具

mysqlshow:查看数据库,表,字段的统计信息

mysqldump:数据备份工具

mysqlimport/source:数据导入工具

(运维篇)日志

错误日志

错误日志是 MySOL 中最重要的日志之一,它记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息

当数据库出现任何故障导致无法正常使用时,建议首先查看此日志

该日志是默认开启的,默认存放目录 /var/log/,默认的日志文件名为 mysqld.log

通过如下命令查看错误日志位置:

show variables like '%log_error%';

查看错误日志:

tail -f /var/log/mysqld.log

二进制日志

binlog 记录了所有 DDL 和 DML 语句,但不包括查询语句

作用:

  • 灾难时的数据恢复
  • MySQL 的主从复制
show variables like '%log_bin%';

在这里插入图片描述
在这里插入图片描述

查询日志(所有日志)

查询日志中记录了客户端的所有操作语句,而二进制日志不包含查询数据的SQL语句。默认情况下,查询日志是未开启的

show variables like '%general%';

打开该选项:

// /etc/my.cnf
general_log=1
general_log_file=/var/lib/mysql/mysql_query.log

慢查询日志

在这里插入图片描述

主从复制

在这里插入图片描述
在这里插入图片描述

主库配置

首先关闭主机和从机的防火墙

在这里插入图片描述

从库配置

在这里插入图片描述

分库分表

在这里插入图片描述

拆分策略

垂直拆分:

  • 垂直分库:根据业务将不同的表拆分到不同的库中
  • 垂直分表:根据字段属性将不同的字段拆分到不同的表中(例如将占用空间较大而又不重要的字段拆分出去)

水平拆分:

  • 水平分库:对库中的每个表做水平拆分
  • 水平分表:将表按照水平方向“剪开”,每张表的结构都一样

在这里插入图片描述
在这里插入图片描述

MyCat

Mycat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言

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

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

相关文章

20241027_北京郊游香山公园

这次是第二次去香山公园&#xff0c;天气不是很晴朗&#xff0c;有雾。 乘坐地铁到的时候&#xff0c;第一趟车&#xff0c;我这么聪明&#xff0c;那肯定是不会坐的&#xff0c;因为没有座位&#xff0c;路程30多分钟&#xff0c;我都20多岁了&#xff0c;身体那肯定顶不住。 …

从0到1,AI我来了- (7)AI应用-ComfyUI-III-Flux模型

上篇我们利用已有的工作流&#xff0c;完成了一个已有comfyUi workflow 的美女制作&#xff0c;有美女&#xff0c;需要野兽来配&#xff0c;最近黑森林的Flux 很火&#xff0c;我们也来实践一下生成野猴子黑悟空。 这篇文章主要内容分三部分&#xff1a; 一、认识Flux&#…

【Linux系统编程】线程深入运用

目录 一&#xff0c;C线程与系统线程 二&#xff0c;分离线程 三&#xff0c;线程结构 四&#xff0c;__thread关键字 五&#xff0c;Linux线程互斥 1&#xff0c;线程互斥相关的背景概念 2&#xff0c;互斥锁 3&#xff0c;死锁 4&#xff0c;互斥锁的弊端 六&#…

【 C++ 】C++11的初步学习

目录 ​编辑 一、列表初始化 1.支持内置类型 2.支持自定义类型 二、initializer-list 三、右值引用和移动语义 1.左值和右值 a&#xff09;左值 b&#xff09;右值 2.左值引用和右值引用 a&#xff09;左值引用 b&#xff09;右值引用 c&#xff09;总结 d&#xf…

云计算平台上的DevOps实践

文章目录 什么是DevOps云计算平台上的DevOps优势自动化部署弹性伸缩地理分布 实施DevOps的关键组件版本控制系统持续集成/持续交付工具配置管理工具监控和日志管理 实践案例使用AWS CodePipeline进行持续集成/持续交付利用AWS Auto Scaling实现弹性使用AWS CloudFormation进行基…

C++引用类型变量

引用变量的主要用途是用作函数的形参。这样函数将使用原始数据&#xff0c;而不是副本。除指针之外&#xff0c;引用也为处理大型结构提供了一种非常方便的途径。 在C中使用&符号标识引用。也就是说C给&符号赋予了另一个含义&#xff0c;将其用来声明引用。 引用的声…

「C/C++」C/C++预处理 之 X宏(X Macro)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

数据结构————map,set详解

今天带来map和set的详解&#xff0c;保证大家分清楚 一&#xff0c;概念 map和set是一种专门用来搜索的容器或数据结构 map能存储两个数据类型&#xff0c;我们称之为<key-value>模型 set只能存储一个数据类型&#xff0c;我们称之为纯<key>模型 它们的效率都非…

APISQL企业版离线部署教程

针对政务、国企、医院、军工等内网物理隔离的客户&#xff0c;有时需要多次摆渡才能到达要安装软件的服务器。本教程将指导您使用Linux和Docker Compose编排服务&#xff0c;实现APISQL的离线部署。 准备 准备一台Linux(x86_64)服务器。 安装Docker Engine&#xff08;推荐版本…

DC-1渗透测试

DC1 五个flag的拿取&#xff08;截图是五个flag里面的内容&#xff09; 注意事项&#xff1a;kali的用户名&#xff1a;root 密码&#xff1a;kali 注意&#xff1a;DC1 只要开机服务就起来了 思路&#xff1a;信息收集—> 寻找漏洞—> 利用漏洞(sql注入,文件上传漏洞…

uniapp的IOS证书申请(测试和正式环境)及UDID配置流程

1.说明 本教程只提供uniapp在ios端的证书文件申请&#xff08;包含正式环境和开发环境&#xff09;、UDID配置说明&#xff0c;请勿用文档中的账号和其他隐私数据进行测试&#xff0c;请勿侵权&#xff01; 2.申请前准备 证书生成网站&#xff1a;苹果应用上传、解析&#x…

vxe-table 表格中使用输入框、整数限制、小数限制,单元格渲染数值输入框

Vxe UI vue vxe-table 表格中使用输入框、整数限制、小数限制&#xff0c;单元格渲染数值输入框 在 vxe-table v4.7 单元格中渲染有非常多的方式&#xff0c;可以使用自带的组件&#xff0c;也可以已使用第三方的组件 element ui 之类的。本章介绍如果使用自带的输入框&#x…

Negative Sampling in Recommendation: A Survey and Future Directions

目录 Introduction分类&#xff1a;静态负采样策略动态负采样策略对抗负采样策略重要性重加权策略知识增强负采样策略多种推荐场景的负采样 Introduction 传统的推荐算法通常关注用户的正面历史行为&#xff0c;而忽视了负面反馈在理解用户兴趣中的重要作用。负面采样是推荐系…

Java IO 模型

I/O 何为 I/O? I/O&#xff08;Input/Output&#xff09; 即输入&#xff0f;输出 。 我们先从计算机结构的角度来解读一下 I/O。 根据冯.诺依曼结构&#xff0c;计算机结构分为 5 大部分&#xff1a;运算器、控制器、存储器、输入设备、输出设备。 输入设备&#xff08;比…

MFC实现以不规则PNG图片作为窗口背景

效果图 显示的不规则PNG图片 头文件 #pragma once #include <gdiplus.h> #pragma comment (lib,"Gdiplus.lib")// CShowBack 对话框class CShowBack : public CDialogEx {DECLARE_DYNAMIC(CShowBack) public:CShowBack(CWnd* pParent nullptr); // 标准构…

数字IC开发:布局布线

数字IC开发&#xff1a;布局布线 前端经过DFT&#xff0c;综合后输出网表文件给后端&#xff0c;由后端通过布局布线&#xff0c;将网表转换为GDSII文件&#xff1b;网表文件只包含单元器件及其连接等信息&#xff0c;GDS文件则包含其物理位置&#xff0c;具体的走线&#xff1…

HarmonyOS 5.0应用开发——Navigation实现页面路由

【高心星出品】 文章目录 Navigation实现页面路由完整的Navigation入口页面子页面 页面跳转路由拦截其他的 Navigation实现页面路由 Navigation&#xff1a;路由导航的根视图容器&#xff0c;一般作为页面&#xff08;Entry&#xff09;的根容器去使用&#xff0c;包括单页面&…

Flink CDC系列之:学习理解核心概念——Data Pipeline

Flink CDC系列之&#xff1a;学习理解核心概念——Data Pipeline 数据管道sourcesink管道配置Table IDroutetransform案例 数据管道 由于 Flink CDC 中的事件以管道方式从上游流向下游&#xff0c;因此整个 ETL 任务被称为数据管道。 管道对应于 Flink 中的一系列操作。 要描…

25届电信保研经验贴(清华大学电子工程系,工程硕博)

个人背景 学校&#xff1a;中九 专业&#xff1a;电子信息工程 加权&#xff1a;92.89 绩点&#xff1a;3.91/4.0 rank&#xff1a;前五学期rank2/95&#xff0c;综合排名rank1&#xff08;前六学期和综合排名出的晚&#xff0c;实际上只用到了前五学期&#xff09; 科研…

安卓取消触摸屏幕的指针效果

在安卓系统中&#xff0c;取消触摸屏幕的指针效果&#xff08;通常指开发者模式下的屏幕点按反馈显示或指针位置显示&#xff09;可以通过ADB&#xff08;Android Debug Bridge&#xff09;命令来实现。以下是具体的步骤和命令&#xff1a; 使用ADB命令取消触摸屏幕的指针效果 …