黑马数据库学习笔记
课程地址
(基础篇)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表的员工姓名和工作地址,如果工作地址为北京或上海,则显示一线城市;否则显示二线城市
selectname,( case workaddress when "北京" then "一线城市"when "上海" then "一线城市"else "二线城市"end ) as "工作地点"
from emp;
根据成绩显示结果:
selectname,( casewhen 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 salaryfrom empwhere dept_id = (select idfrom deptwhere name = "财务部")
);-- 查询比研发部任意一人工资高的员工信息
select *
from emp
where salary > any( -- min( -- some(select salaryfrom empwhere dept_id = (select idfrom deptwhere name = "研发部")
);
行子查询
-- 查询与“张无忌”薪资和直属领导相同的员工信息
select *
from emp
where (salary, managerid) = ( --( tuple equalsselect salary, manageridfrom empwhere name = "张无忌"
);
表子查询
-- 查询与“鹿仗客”,“宋远桥”的职位和薪资相同的员工信息
select *
from emp
where (job, salary) in (select job, salaryfrom empwhere name = "鹿仗客" or name = "宋远桥"
);-- 查询入职日期是“2006-01-01”之后的员工信息和部门信息
select e.*, d.*
from (select *from empwhere 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
wheree.dept_id = d.id ande.salary between s.lowsal and s.highsal andd.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 e2where e1.dept_id = e2.dept_id
);-- 11 查询所有部门的信息,并统计部门的员工人数
selectd.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 filesortcreate 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 temporarycreate 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
视图的作用:
- 简单:视图不仅可以简化用户对数据的理解,也可以简化他们的操作那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部的条件
- 安全:通过视图用户只能查询和修改他们所能见到的数据数据库可以授权,但不能授权到数据库特定行和特定的列上
- 数据独立:视图可帮助用户屏蔽真实表结构变化带来的影响
视图案例
根据如下需求,定义视图:
- 为了保证数据库表的安全性,开发人员在操作
tb_user
表时,只能看到的用户的基本字段,屏蔽手机号和邮箱两个字段 - 查询每个学生所选修的课程(三张表联查),这个功能在很多的业务中都有使用到,为了简化操作,定义一个视图
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 asselect s.name as student_name,s.no as studnet_no,c.name as course_namefromstudent as s,student_course as sc,course as cwheres.id = sc.studnetid and c.id = sc.courseid;
存储过程
存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用
特点:
- 封装,复用
- 可以接受参数,也可以返回数据
- 减少网络交互,效率提升
create procedure p1()
beginselect 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()
begindeclare 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()
begindeclare score int default 58;declare result varchar(10);if score >= 85 thenset result := 'great';elseif score >= 60 thenset result := 'pass';elseset result := 'failure';end if;select result;
end;call p3();
参数
create procedure p4(in score int, out result varchar(10))
beginif score >= 85 thenset result := 'great';elseif score >= 60 thenset result := 'pass';elseset result := 'failure';end if;
end;call p4(90, @result);
select @result;-- 将 200 分制的分数转化为百分制并返回
create procedure p5(inout score double)
beginset score := score / 2;
end;set @score = 180;
call p5(@score);
select @score; -- 90
case
create procedure p6(in month int)
begindeclare result varchar(10);casewhen month >= 1 and month <= 3 thenset result := '1';when month >= 4 and month <= 6 thenset result := '2';when month >= 7 and month <= 9 thenset result := '3';when month >= 10 and month <= 12 thenset result := '4';elseset result := 'invalid';end case;select concat(month, ': ', result);
end;call p6(8);
while
create procedure p7(in n int)
begindeclare total int default 0;while n > 0 doset total := total + n;set n := n - 1;end while;select total;
end;call p7(10);
repeat
create procedure p8(in n int)
begindeclare total int default 0;repeatset 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)
begindeclare total int default 0;sum: loopif n <= 0 thenleave 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)
begindeclare total int default 0;sum: loopif n <= 0 thenleave sum;end if;if n % 2 = 1 thenset 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)
begindeclare 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 dofetch 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)
begindeclare 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 dofetch 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
begindeclare total int default 0;while n > 0 doset 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
begininsert 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
begininsert 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
begininsert 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:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言
相关文章:
黑马数据库学习笔记
课程地址 (基础篇)MySQL的启动 mysql 默认使用 3306 端口 在 centos下,启动 mysql 数据库:service mysqld start; 查看状态/启动/停止/重启:systemctl status/start/stop/restart mysqld; 登录到mysql数据库&…...
MYSQL-SQL-03-DQL(Data Query Language,数据查询语言)(单表查询)
DQL(数据查询语言) DQL英文全称是Data Query Language(数据查询语言),数据查询语言,用来查询数据库中表的记录。 查询关键字: SELECT 在一个正常的业务系统中,查询操作的频次是要远高于增删改的,当我们去访…...
【数据结构和算法】三、动态规划原理讲解与实战演练
目录 1、什么是动态规划? 2、动态规划实战演练 2.1 力扣题之爬楼梯问题 (1)解题思路1: (2)解题思路2: (3)动态规划(DP):解题思路 (4&#x…...
交叉编译 perl-5.40.0(riscv64)
交叉编译 perl-5.40.0(riscv64) https://arsv.github.io/perl-cross/usage.html https://github.com/arsv/perl-cross 借助 perl-cross 进行交叉编译 https://www.perl.org/get.html#unix_like 这里获取 perl-5.40.0 的源码 https://github.com/arsv/pe…...
Leetcode 搜索旋转排序数组
这段代码是用于解决LeetCode第33题“搜索旋转排序数组”的Java解法。以下是对该算法思想的中文解释: 算法思想 二分查找的基本思路: 由于数组是部分有序的(被旋转过),我们可以利用二分查找的思想,逐步缩小…...
Spring Task—定时任务
Spring Task 是 Spring 提供的一种轻量级定时任务调度功能,内置在 Spring 框架中。与 Quartz 等重量级调度框架相比,Spring Task 使用简便,无需额外依赖,适合在简单的调度任务场景中使用。通过注解配置方式,开发者可以…...
Spring Boot 应用开发概述
目录 Spring Boot 应用开发概述 Spring Boot 的核心特性 Spring Boot 的开发模式 Spring Boot 在企业应用开发中的优势 结论 Spring Boot 应用开发概述 Spring Boot 是由 Pivotal 团队开发的一个框架,基于 Spring 框架,旨在简化和加速基于 Spring …...
Chrome谷歌浏览器加载ActiveX控件之allWebDesktop控件介绍
背景 allWebDesktop控件是一款方便用户在线打开各类文档的OA办公控件。它设计比较轻巧,充分利用计算机程序资源打开文档,并将程序窗口嵌入到allWebDesktop控件区域内,从而实现浏览器内打开各类文档效果。 allWebPlugin中间件是一款为用户提供…...
GitHub Star 数量前 5 的开源应用程序生成器
欢迎来的 GitHub Star 数量排名系列文章的第 7 篇——最受欢迎的应用程序生成器。 之前我们已经详细探讨过:在 GitHub 上最受欢迎的——无代码工具、低代码项目、内部工具、CRUD项目、自部署项目和 Airtable 开源替代品。累计超过 50 个优质项目!&#…...
DBC文件当中新建CANFD等类型的报文
同学最近有添加CANFD报文的需求,需要用到CANFD类型报文的DBC文件,这下就难住我了,我之前用的DBC文件只有“CAN Standard”“CAN Extended”两种类型,压根没见过FD的。 后来他找到了项目之前的DBC,打开来看,…...
基于SpringBoot的房地产销售管理系统【附源码】
基于SpringBoot的房地产销售管理系统(源码L文说明文档) 目录 4 系统设计 4.1用户登录功能的详细实现 4.2管理员权限的功能实现 4.2.1客户信息管理功能的详细实现 4.2.2房产管理功能的详细实现 4.2.3预约看房功能的详细实现 4.2.4论…...
圆点虚线 Android
参考 https://blog.csdn.net/l_o_s/article/details/73550876 <com.xxx.wwww.weight.PointDividerViewandroid:layout_width"match_parent"android:layout_height"wrap_content"app:PDbackgroundColor"color/white"app:dotColor"color/…...
贵州鑫宏远农业-始终致力于推动现代农业的科技创新与发展
贵州鑫宏远农业科技有限公司,是一家在高科技农业领域深耕细作、锐意进取的企业。自成立以来,我们始终致力于推动现代农业的科技创新与发展,业务全面覆盖农业科学研发、组织培养生产、专业育苗培植、半成品及成品精细化养护、市场销售以及全方…...
程序员做销售,从代码到客户的逆袭之路
大家好,我是小悟。 在这个互联网风起云涌、技术迭代日新月异的时代,“跨界”已然成为一种新潮流。就好似那从天而降的大侠,一不小心就可能横跨了数个充满奇遇与挑战的领域。 想象一下,一个平日里只跟代码打交道的程序员…...
Flink CDC系列之:理解学习Kubernetes模式
Flink CDC系列之:理解学习Kubernetes模式 准备会话模式启动会话集群设置 Flink CDC提交 Flink CDC Job Kubernetes 是一种流行的容器编排系统,用于自动化计算机应用程序的部署、扩展和管理。Flink 的原生 Kubernetes 集成允许您直接在正在运行的 Kuberne…...
git合并相关操作详解
在使用Git进行分支管理时,合并(merge)操作是非常常见的。下面是Git合并相关的详细步骤和一些常见的场景及注意事项。 一、 基本合并操作 假设我们有两个分支:main 和 feature,希望将 feature 合并到 main 上。 切换到目标分支 首先需要切换到你想合并到的分支。例如,切…...
前端经典【面试题】持续更新HTML、CSS、JS、VUE、FLUTTER、性能优化等
HTML/CSS 面试题 什么是语义化 HTML? 说明:语义化 HTML 使用 HTML 标签来描述内容的含义,而不仅仅是其外观。使用语义化标签可以提高可读性和可访问性,并对 SEO 友好。示例: <header><h1>网站标题</h1&…...
【Linux知识】linux磁盘管理深入了解
文章目录 常见磁盘管理命令行磁盘分区NASNAS 磁盘挂载🔐 如何设置NAS设备的访问权限? Mkfs🧐 mkfs 命令支持哪些文件系统类型? Mount🔑 在Linux中,如何安全地卸载挂载的文件系统? 常见磁盘管理命…...
Qt Essential Classes
目录 QVariant QFlags QRandomGenerator 经典的Qt容器 QVector QList QMap QMultiMap QSet QHash QVariant 同std::variant是一样的,他是一个更加高级的union。在一个时间下,它虽然实际上只能是一种类型,但是一个variant可以hold住…...
小小猫棒onu替换家用光猫,薅运营商带宽羊毛,突破1000M
小小猫棒onu 一、总体步骤 1 记录原来光猫信息 主要包括SN,ploam密码,loid、loid密码、 mac、上网的vlan id等 一般gpon采用SN、ploam密码、SNploam密码三种中的一种认证方式 一般Epon采用loid(逻辑id)、mac、loid mac三种中…...
软件测试学习笔记丨Selenium学习笔记:css定位
本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/22511 本文为霍格沃兹测试开发学社的学习经历分享,写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 说明:本篇博客基于sel…...
python数据处理常用操作
数据处理是机器学习中非常重要的一步,以下是一些常用的操作和示例代码: 1. 数据清洗 处理缺失值: import pandas as pd# 读取数据 df pd.read_csv(data.csv)# 删除缺失值 df.dropna(inplaceTrue)# 用均值填充缺失值 df.fillna(df.mean(), i…...
解决minio跨域问题
MinIO 支持跨域资源共享(CORS),允许你配置跨域请求的相关策略。以下是一个基本的CORS配置示例,你可以在MinIO的配置文件(例如config.json)中设置这些策略: 在Linux中 root/.minio 目录下如果没有就新建一个 config.jso…...
python 跳过当前循环
在 Python 中,可以使用 continue 语句来跳过当前循环的剩余部分,并继续下一次循环。continue 语句用于跳过循环体中剩余的语句,并立即开始下一次迭代。 以下是一个简单的示例,演示了如何在 for 循环中使用 continue 语句…...
数据库数据恢复—Oracle ASM磁盘组掉线 ,ASM实例无法挂载的数据恢复案例
Oracle数据库数据恢复环境&故障: Oracle ASM磁盘组由4块磁盘组成。Oracle ASM磁盘组掉线 ,ASM实例不能mount。 Oracle数据库故障分析&恢复方案: 数据库数据恢复工程师对组成ASM磁盘组的磁盘进行分析。对ASM元数据进行分析发现ASM存储…...
jupyter notebook改变默认启动路径
安装好Anaconda 3以后,就可以使用Jupyter notebook了,但是我们打开Jupyter notebook后,发现界面是一个默认的目录,这个目录在哪里?如果想把自己写的程序文件保存在自己新建的一个文件夹里,修改默认目录到自…...
libevent源码剖析-基本数据结构
1 简介 前面系列文章对libevent源码的主体结构,从reactor框架实现,到evbuffer和bufferevent实现原理,及libevent的例子进行了剖析,自此,我们便可基于libevent开发app了。 从本文开始,主要来介绍下libevent源…...
往期文章汇总——射频测量+无线通信+软件无线电+6G科普
本节目录 一、射频测量系列往期链接 二、无线通信系列往期链接 三、软件无线电系列往期链接 四、6G科普系列往期链接本节内容 一、射频测量系列往期链接 射频测量 | 滤波器的关注指标 射频测量 | 射频电路中的负载与滤波器 射频测量 | 射频衰减器的功率系数 射频测量 | 衰减…...
微信小程序 - 深 / 浅拷贝实现方法,微信小程序深拷贝与浅拷贝,函数方法封装直接调用使用,深拷贝cloneDeep和浅拷贝clone(深复制和浅复制)
前言 在微信小程序中,你无法 直接使用常规浏览器环境中的深浅拷贝方法。 但可以借助 utils.js 实现,下面是方法。 创建深浅拷贝函数 依次打开小程序目录【utils】→【utils.js】,写入深拷贝函数并暴露出去。 // utils.js// 对象深拷贝函数 const deepClone = function(in…...
Log4Net配置详解及输出自定义消息类示例代码
1.简单使用实例 1.1 添加log4net.dll的引用。 在NuGet程序包中搜索log4net并添加,此次我所用版本为2.0.17。如下图: 1.2 添加配置文件 右键项目,添加新建项,搜索选择应用程序配置文件,命名为log4net.config,…...
网站开发实战项目/学开网店哪个培训机构好正规
概述 Floodlight内部定义报文格式的代码位于net.floodlightcontroller.packet,其中定义的报文类型有ARP,BPDU,BSN,BSNPROBE,DHCP,Ethernet,ICMP,IPv4,LLC,LLDP,TCP,UDP。 其中定义了一个名为IPacket的接口,该接口结构如图: 包…...
如何将vs做的网站备份出来6/软文之家
我做了一個傻事,要在Server上新增一張網卡,可是因為一直無法啟動, 所以很自然的以為CentOS 6又多了其他的設定要求, 因此查了兩天的資料,也試過很多方式,但都沒有效用。 今天早上心血來潮,想說用…...
导航栏宽度wordpress/做网站要多少钱
来源:Modis数据简介 - zerobyzero - 博客园 (cnblogs.com) 【ENVI精讲】处理专题十二:基于MODIS数据的气溶胶遥感监测 - 知乎 (zhihu.com) MODIS 二级气溶胶产品指北(with Python) - 炸鸡人博客 (zhajiman.github.io) 美国于19…...
商城网站有什么好处/关键词优化推广公司哪家好
在上一篇我们对Docker做了一个简单介绍,有了一个犹抱琵琶半遮面的认识,这篇文章就揭开这半面黑纱,让Docker安装在我们的电脑上,根据官方文档,我们去操作它,去驾驭它!我的电脑是Mac,可…...
沂源网站开发/上海推广服务
(1)第一个程序“”“Hello World” 实现python环境打印输出:Hello World 程序: print("Hello World") 数据类型转换 数据类型:int整形,str字符型,不同类性的数据不能相加、减。 正确&…...
做的比较炫的网站/域名免费注册0元注册
转载于:https://blog.51cto.com/13956693/2173332...