数据库mysql详细教学

目录

mysql的第一组基本操作:数据库操作

1、查看当前数据库

2、创建数据库

3、选中数据库

4、删除数据库

5、表操作

5.1查看数据库中的表

 ​编辑

5.2创建表

5.2.1数据类型

5.3 查看指定表的表结构

5.4删除表

 5.5 MySQL表的增删改查

5.5.1新增 / 插入数据

5.5.2 查找操作

 eg1:全列查找

eg 2:原来基础上+条件

eg 3:查询每个同学的总成绩

eg 4:查询的时候指定别名(相当于起了个小名,更方便理解含义)

eg 5:去重查询

eg 6:条件查询

eg7:between and 约定一个前闭后闭 区间(包括两侧边界)

eg8:查询数学成绩是 87 或 98 或 56的同学

eg9:模糊查询 like

eg10:NULL / null 表示表格这一项为空   

eg11:分页查询 limit

5.5.3修改操作

 5.5.4 删除操作

数据库约束

1、not null

2、unique

3、default

4、primary key

5、foreign key 外键

表的设计

把查询和新增联合起来

更复杂的查询

01聚合查询

02联合查询

03内连接

04外连接 

05自连接

06子查询 

07合并查询

索引

索引的数据结构 

事务

事务四个特性:

并发事务可能产生的问题 

四种隔离级别​​​​​​​

1024 byte 构成 1 kb

1024 KB => 1MB

1024 MB => 1GB

1024 GB => 1TB

1024 TB => 1PB

内存的数据,断电后会丢失。外存的数据,断电后数据还在~

“持久化” 这样的次,意思就是把数据写到硬盘上。


mysql的第一组基本操作:数据库操作

1、查看当前数据库

show databases:

1)show 和 databases 之间有一个或者多个空格

2)注意是databases,不是database。

3)使用 英文分号 结尾。(客户端里的任何一个sql都需要使用 分号 来结尾) 

2、创建数据库

可以指定字符集。

补充:

计算机里,不同的字符集下,表示一个汉字所需要的字节不同。

平时常用的:

1、gbk windows 简体中文版,默认字符集。 2个字节表示一个汉字

2、utf8 更通用的字符集。不仅仅能表示中文 通常是3个字节白哦是一个汉字的

create database 数据库名 charset utf8(字符集)

3、选中数据库

use 数据库名;

选中数据库,选中之后,会有个提示~ 

4、删除数据库

drop database 数据库名

5、表操作

5.1查看数据库中的表

 

一定要先选中数据库再操作!

5.2创建表

5.2.1数据类型

  • 字符:char      varchar(可变长度字符串)
  • 时间:data     datatime
  • 整数:tinyint(相当于Byte)    smallint(相当于Short)     int(相当于Integer)    bigint(相当于Long)
  • 浮点:float    double
  • BLOB:存储的是2进制串,和bit[]不一样,只能存最多 64 bit。blob可以存更长,比如要存一个小的图片,存一个小的音频文件。
  • datetime / timestamp:都能表示年月日,时分秒。timestamp:4个字节的时间戳,不太够用了。

double 和 float 有一个很严重的问题!

表示有些数据的时候,不能精确表示!(存在误差)

0.1 + 0.2 = 0.3 => false

 主要市和内存存储结构相关。

这个表示方式带来的好处,存储空间小,计算速度块,但是可能存在误差。

使用decimal,是使用了类似于字符串的方式来保存的~ 更精确存储

但是存储空间更大,计算速度更慢了

上述类型都是有符号(带有正负的)无符号的不建议使用 

create table 表名(列名 类型, 列名 类型……);

 

5.3 查看指定表的表结构

desc 表名;
desc -> describe 的缩写。(描述一个表是啥样的)

5.4删除表

drop table 表名;

 创建数据库也有一个 IF [NOT] EXISTS


eg:单价使用什么类型?

double (3,1)  不精确!!

decimal 是更好的选择!!(更精确)

此处更好的选择是使用  


 性别不止 男 和 女~

有些字段,看起来是数字,实际上是字符串~

像身份证,虽然是由数字构成的,但是这个数字不能够参与 +-*/ 算数运算

 5.5 MySQL表的增删改查

CRUD 即增加(create), 查询(Retrieve), 更新(update),删除(Delete)四个单词的首字母缩写。

5.5.1新增 / 插入数据

insert into 表名 values(值,值……);

values 后这里的个数,类型,顺序要和表头结构匹配~~

SQL没有字符类型,‘ 和 ‘’ 都可以表示字符串。

此处还有一种错误数据库字符集没有正确配置引起的~~

数据库不做任何修改,默认情况下创建的数据库字符集是“拉丁文”字符集,不能表示中文。

在插入的时候,指定某个 / 某些列来插入 此时 values 后面的内容。就是和values前面的 () 的内容匹配的~

一次插入 N 个记录,比一次插入一个记录,分N次插入,效率要高一些~~ 

插入时间的时候,是通过特定格式的字符串来表示时间日期的。形如:‘2023-11-14 02:41:00’ 

如果想把时间设置为当前时刻,sql 提供了一个特殊的函数,now() 

5.5.2 查找操作

 eg1:全列查找

查找整个表,所有行的所有列。

select * from 表名;

如果数据量几亿,几十亿执行 select *  操作,肯定会非常危险!!

降低危险程度,一个简单有效的办法——当你需要操作生产环境的数据库的时候,一定要拉上一个人一起操作。

eg 2:原来基础上+条件

查询的时候,针对分数进行变换。比如让查询的math成绩都在原来基础上+10‘

select name, math+10 from 表名;

上述查询,数据库服务器硬盘的数据没有发生改变,再次查询,还是+10之前。


eg 3:查询每个同学的总成绩

表达式查询,是让列和列之间进行运算。


eg 4:查询的时候指定别名(相当于起了个小名,更方便理解含义)

使用 as 关键字


eg 5:去重查询

distinct 针对指定列进行去重(把重复行去掉)


eg 6:按照查询结果排序

order by 子句,指定某些列进行排序~ 

排序可能是升序,也可能是降序

对于mysql来说,如果sql没有指定order by,此时查询结果集的数据顺序,是不可预期的。

order by 可以根据多个列来排序

desc 降序(descend)

asc 表示升序,但如果省略不写,默认就是升序。


eg 6:条件查询

在查询的时候,指定筛选条件  符号条件的数据留下,不符合的直接 pass

需要先描述条件。

通过 where 子句,搭配上条件表达式,就可以完成条件查询~

可以直接拿两个列比较~

条件查询使用表达式作为条件

查询总分在200分以下的同学

查询语文成绩大于60分,且英语成绩大于50分的同学。 and

查询语文成绩大于60,或英语成绩大于50的同学。  or

and 和 or 的优先级

一个where 中即存在 and 又存在 or,先执行 and 后执行 or。后边写代码,记得多加()。


eg7:between and 约定一个前闭后闭 区间(包括两侧边界)

String 这里的有些方法,就是按照区间来指定的 substr……

List 这里的有些方法也是按照区间 subList……


eg8:查询数学成绩是 87 或 98 或 56的同学

in

以上写法效率比较低。


eg9:模糊查询 like

模糊匹配,不要求元素完全相同,只要满足一定的规则,就可以了~

like支持两个用法:

1、使用%代表任意 0 个字符或者N个字符~

2、使用_代表任意1个字符~

like ’%藏‘  查询藏结尾的

like ’%孙%‘ 查询包含孙的

mysql 效率比较低~

很容易成为性能瓶颈,模糊匹配更是比较低效的写法,如果这里支持的功能更复杂,反而更拖慢数据库的效率。


eg10:NULL / null 表示表格这一项为空   

sql不区分大小写的

null 和其他数值进行运算,结果还是null

null结果在条件中,相当于false 了

<=> 使用这个比较相对运算,就可以处理 null 的比较!

null <=> null  =》 true

或者


eg11:分页查询 limit

比如一共5000页,一共这么多数据,全都显示出来,一方面,用户看不过来;一方面对于系统的压力也比较大~~

一页显示10个数据~~

limit 还可以搭配 offset,声明从哪一条开始查询(从0开始计数)

limit 3 offset 6;      等价于    limit 6,3;(不太推荐这么写)

查询总分前三名的同学信息~~

1、计算每个同学的总成绩

2、按照成绩排序(降序)

3、取前三条记录~

order by 这里可以使用别名,where不能使用

5.5.3修改操作

update关键字进行

update 表名 set 列名 = 值,列名 = 值……where条件;

order by / limit 等也是可以使用的~

超过范围了,此时修改不会生效

修改所有人的成绩为0.5倍。 

update操作非常危险!

 5.5.4 删除操作

delete 删除记录(行)

delete from 表名 where 条件;

delete from exam_result where name like '孙%';

不写 where 也没有 limit 就是全部了!这个操作基本相当于删表了(drop table)

delete from 表还在,里面的数据没了。  l.clear();

drop table 表和数据都没了。        l=null;

数据库约束

约束,就是数据库针对里面的数据能写啥,给出的一组“检验规则”。

  • NOT NULL  必填项
  • UNIQUE   让列的值是唯一的(不和别的行重复,eg:学号,手机号,身份号)
  • DEFAULT   默认值
  • PRIMARY KEY  主键
  • FOREIGN KEY  外键
  • CHECK  在mysql 5 版本中,不支持 

1、not null

 

此时表中可以随意插入空值!

给一个现有的表加约束,也可以,比较麻烦,使用 alter table

2、unique

 插入/修改数据的时候,先会查询,先看看数据是否已经存在,如果不存在,就能够插入/修改成功。如果存在,则插入/修改失败。

此时再插入同样的数据报错。

entry:入口,条目

补充:数据库在插入前先查询了一下~(只不过查询操作,咱们感知不到)

因此,这里是多了个查找数据的成本。

本质上,程序运行 vs 程序员开发效率。

3、default

默认值是insert指定列插入的时候,其他未被指定到列就是按照默认值来填充~~ 

 create table 表名(id int, name varchar(20) default '无名氏');

4、primary key

 主键,一条记录 在表中的“身份标识”        手机号码,身份证号码,学号……

也是要求唯一,并且不能为空~~

主键 = unique + not null

mysql 要求一个表里,只能有一个主键!!

创建主键的时候,可以使用一个列作为主键,也可以使用多个列作为主键(复合主键)

 create table 表名(id int primary key, name varchar(20));

一个重要的问题:

如何给这个记录安排一个主键呢?

mysql自身只是能够检查是否重复,设置的时候还是靠程序员来设置。

此处,由很多办法,mysql提供了一个简单粗暴的做法,自增主键~~

auto_increment

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

给自增主键插入数据的时候,可以手动指定一个值,也可以让mysql 自己分配。

如果让他自己分配,就在insert语句的时候,把 id 设为空即可

此时可以手动分配,也可以让mysql自己指定~

但是再次插入的时候,就从序号最大值+1开始了~

5、foreign key 外键

create table student (studentId int primary key auto_increment, name varchar(20),classId int,foreign key(classId) references class(classId));

此时就要求 student 表中的每个记录的 classId 得在 class 表的 classId 中存在! 

student受到class的约束,就把class叫做student的父表(parent),

student就是class的子表(child) 

子类对父类也有约束,被孩子依赖的数据不能删除掉 

eg:电商类网站

商品表(id, name, price……)

订单表(orderId, userId, goodsId, time ……)

订单表中的goodsId得在 商品表中存在外键约束~

有一种情况,商品要下架,如何实现?

给商品表加一列,表示是否下架,此时实现下架并非是delete记录,而是update把是否下架字段进行修改~~   逻辑删除

表的设计

 设计表,分两步走

1、梳理清楚需求中的“实体”

2、梳理清楚实体之间的关系

实体之间的关系,主要由三种(严格的说是四种)

1、一对一

   一个学生,只能有一个账号。

   一个账号,只能供一个学生所有

如何设计表

        1)搞一个大表,包含学生信息 + 账号信息

        account-student(accountId, username, password, studentName……)

        2)搞两个表,相互关联

        account(accountId, username, password);

        student(studentId, name……);

        3)搞两个表

        account(accountId, username, password);

        student(studentId, studentName, accountId);

        后续可以搞一些教师表等,也能和account关联

2、一对多

    一个班级 可以包含多个学生

    一个学生只能处于一个班级

两种典型表示方式:

        1)student(studnetId, name);              mysql没有数组类型,不能这么搞,但是redis,是由数组类型的,此时就可以考虑这样的设计

              class(classId, className, studentIdList)

        2)class(classId, className);

                         1             2001

                         2             2002

              student(studentId, name, classId);

                             1              张三       1

                             2              李四       2

3、多对多

    一个学生可以选择多个课程,

    一个课程也可以提供给多个学生。

方式:

        1)student(studentId, name)

                                1           张三

                                2           李四

             course(courseId,name)

                               1         语文

                               2         数学 

        需要搞一个 关联表

        student_course(studentId, courseId)

                                        1               1

                                        1               2                张三选了 语文 和 数学

                                        2               1                语文这个课程就提供给了张三和李四~


把查询和新增联合起来

把查询结果作为新增的数据~ 

要求:查询结果得到的列数、类型需要和插入的表的列数、类型匹配。列名不一样没事。


更复杂的查询

01聚合查询

把查询过程中,表的行和行之间进行一定的运算~

select 指定的列,要么是带有聚合函数的,要么就是得指定的group by的列……

不能指定一个 非聚合 非 group by 的列

依赖聚合函数。这些是SQL提供的库函数

  • COUNT 计数

这个操作,可以理解成,先执行select *

然后去计算select* 查询结果有多少行~

有一行名字是空

  • SUM 求和

sum操作会自动跳过结果为null的行。

  • AVG 平均数

  • MAX
  • MIN

聚合函数还能搭配表达式使用。求总分的平均分。 


以上聚合函数默认都是针对这个表的所有类进行聚合~

有时候,还需要分组聚合(按照制定的字段,把记录分成若干组,每一组分别使用聚合函数)

员工表

求每个岗位的平均薪资~就需要使用分组查询 group by 指定一个列,就会把列里的值,相同的分到同一个组

分组的时候,可以指定条件筛选~~

先搞清楚,筛选条件是分组前,还是分组后~~

1、分组前,筛选,使用where条件,求每个岗位的平均薪资,但是抛出 wangwu 同学。

group by

2、分组后,筛选,使用having条件~求出每个岗位的平均薪资,刨除老板~~

having

3、既抛出wangwu,有抛出老板

02联合查询

把多个表联合到一起进行查询~

笛卡尔积(一种运算),联合查询就是基于这个运算。

笛卡尔积,其实就是一种排列组合,把两张表的记录 尽可能的排列出N种情况~

     班级表(classId,className)

                        1           2001

                        2           2002

     学生表(studentId,name,classId)

                        1          张三       1

                        2          李四       1

                        3          王五       2

                        4          赵六       2


学生表和班级表的笛卡尔积结果     

 (studentId,name,classId)          (classId,className)

         1        张三       1                    1        2001

           1        张三       1                    2        2002

         2        李四       1                    1        2001

           2        李四       1                    2        2002

           3        王五       2                    1        2001

         3        王五       2                    2        2002

           4        赵六       2                    1        2001

         4        赵六       2                    2        2002

有效数据:classId 相同,两个class对的上,就可以使用 where 子句的条件来描述。

去掉无效数据之后,笛卡尔积里剩余的数据,就是每个同学在哪个班级里这样的信息了。

基于 笛卡尔积 + 条件 进行查询,此时就是 联合查询 / 多表查询

select * from student,score where student.id = score.student_id;

03内连接

语法:

inner join……on……

select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其他条件;

多表查询一般实现步骤

1、分析清除需求种,涉及到的信息都在哪些表里~

2、针对这多个表进行笛卡尔积

3、筛选出其中有效数据(此处使用学生id作为关联条件)

4、结合需求中的条件,进一步加强条件

5、针对列进行精简

四张表 student, classes, course,  score

三个实体:学生, 班级, 课程

学生和班级, 一对多关系

学生和课程, 多对多关系   score表相当于是关联表

班级和课程,没关系


1) 查询“许仙”同学的成绩

学生表和成绩表进行联合查询。

select sco.score from student stu inner join score sco on stu.id=sco.student_id
and stu.name='许仙';
-- 或者
select sco.score from student stu, score sco where stu.id=sco.student_id and
stu.name='许仙';

2)查询所有同学的总成绩,及同学的个人信息

-- 成绩表对学生表是多对1关系,查询总成绩是根据成绩表的同学id来进行分组的
SELECTstu.sn,stu.NAME,stu.qq_mail,sum( sco.score ) 
FROMstudent stuJOIN score sco ON stu.id = sco.student_id
GROUP BYsco.student_id;

3)查询所有同学的成绩,及同学的个人信息

-- 查询出来的都是有成绩的同学,“老外学中文”同学 没有显示
select * from student stu join score sco on stu.id=sco.student_id;
-- 学生表、成绩表、课程表3张表关联查询
SELECTstu.id,stu.sn,stu.NAME,stu.qq_mail,sco.score,sco.course_id,cou.NAME
FROMstudent stuJOIN score sco ON stu.id = sco.student_idJOIN course cou ON sco.course_id = cou.id
ORDER BYstu.id;

04外连接 

内连接和外连接都是进行笛卡尔积。但是细节上有所差别~

内连接:

外连接加 left / right 关键字

select name,score from student left join score on student.id = score.student_id;

当前情况下,这两个表的数据是一一对应的~

第一个表的每个记录,在第二个表里都有体现。第二个表里的每个记录,在第一个表里也都有体现

此时内连接和外连接,查询结果相同!

左外连接:就是以左侧的表为准,左侧表中的所有数据都能体现出来

 

右外链接,就是以右侧表为准。右侧表中的所有数据都体现出来。

05自连接

这个操作本质上是把“行”转成“列”

SQL中进行条件查询,都是指定 某一列 / 多个列 之间进行关系运算~

无法行和行之间关系运算~

有点时候为了实现这种行之间的比较,就需要把行关系转成列关系~

子连接需要指定表的别名。

SQL的逻辑表达能力是有限。很多时候不是做了,而是代码大。

相比之下,使用Java这样的语言来描述复杂逻辑是更好的选择 !!


06子查询 

子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询(套娃~~把多个查询语句合并一个)

子查询一旦嵌套的层次多了,对于代码的可读性是毁灭性的打击!

  • 单行子查询:返回一行记录的子查询

       查询与“不想毕业” 同学的同班同学:

select * from student where classes_id=(select classes_id from student where
name='不想毕业');
  • 多行子查询:返回多行记录的子查询

       案例:查询“语文”或“英文”课程的成绩信息

       1、[NOT] IN关键字

-- 使用IN
select * from score where course_id in (select id from course where name = '语文' or name = '英文');--使用 NOT IN
select * from score where course_id not in (select id from course where name != '语文' and name != '英文');

       可以使用多列包含:

-- 插入重复的分数:score, student_id, course_id列重复
insert into score(score, student_id, course_id) values
-- 黑旋风李逵
(70.5, 1, 1), (98.5, 1 3),
--菩提老祖
(60, 2, 1);--查询重复的分数
select * from score 
where (score, student_id, course_id) in (select score, student_id,
course_id from score group by score, atudent_id, course_id having
count(0) > 1);

        2、[NOT]EXISTS关键字

-- 使用EXISTS
select * from score sco where exists (select sco.id from course cou
where(name = '语文' or name='英文') and cou.id = sco.course_id);--使用 NOT EXISTS
select * from score sco where not exists (select sco.id from course cou
where(name != '语文' and name != '英语') and (cou.id = sco.course_id));
  • 在from子句中使用子查询:子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用

        查询所有比“中文系2019级3班”的平均分高的成绩信息:

-- 获取“中文系2019级3班”的平均分,将其看作临时表
select avg(sco.score) score
fromscore scojoin student stu on sco.student_id = stu.idjoin classes cls on stu.classes_id = cls.id
wherecls.name = "中文系2019级3班";

        查询成绩表中,比以上临时表平均分高的成绩:

select * from score sco,
(select avg(sco.score) score
fromscore scojoin student stu on sco.student_id = stu.idjoin classes cls on stu.classes_id = cls.id
wherecls.name  = '中文系2019班3级') tmp
wheresco.score > tmp.score;

        查询成绩表中,比以上临时表平均分高的成绩:

select *
fromscore sco,(selectavg(sco.score) scorefromscore scojoin student stu on sco.student_id = stu.idjoin classes cls on stu.classes_id = cls.idwherecls.name = '中文系2019级3班') tmp
wheresco.score > tmp.score;

07合并查询

 在实际应用中,为了合并多个select的执行结构,可以使用集合操作符union,union all。使用union 和 union all时,前后查询的结果集中,字段需要一致。

  • union

该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行

案例:查询id小于3,或者名字为‘英文’的课程:

select * from course where id < 3
union 
select * from course where name = '英文';--或者使用or 来实现
select * from course where id<3 or name='英文';

or 只能针对一个表,union可以把多个表的查询结果合并。(要求多个结果列得对应)

  • union all

该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行

案例:查询id小于3,或者名字为‘Java’的课程

-- 可以看到结果集中出现重复数据Java
select * from course where id < 3
union all
select * from course where name = '英文';

索引

索引 index = 》 目录

索引存在的意义就是为了加速查找速度!(省略了遍历的过程)

但是付出了一定的代价

  • 需要付出额外的空间代价来保存索引数据
  • 索引可能会拖慢新增、删除、修改的速度

主键 会自带索引~     unique也会自带~

整体来说,还是认为索引是利大于弊的~

实际开发中,查询场景一般要比增删改频率高很多~

查看索引

show index from 表名;

创建索引

create index 索引名 on 表名(列名);

把表中的内容,根据name搞了一份目录出来~

创建索引操作,可能很危险!

如果表里的数据很大,这个建立索引的开销也会很大!!

好的做法:是创建表之初,就把索引设定好~

如果表里已经有很多数据,索引就别动了~

删除索引

drop index 索引名 on 表名;

索引的数据结构 

B+树:为了数据库索引,量身定做的数据结构

  • 二叉搜索树 : 如果元素高了,树的的高度就会比较高,树的高度 相当于 比较的次数。对于数据库来说。则是IO访问次数。数据库里的数据在硬盘上。
  • 哈希表:虽然查询的快,但是不能支持范围查询不能支持模糊匹配~
  • B+树:
  1. 当前一个节点保存更多的key,最终树的高度是相对更矮的。查询的时候减少了 IO 访问次数(和 B树 一样的)
  2. 所有的查询都会落到叶子节点上(查询任何一个数据,经过IO的访问次数是一样的)这个稳定是很关键的~稳定能够让程序员对于程序的执行效率有一个更准确的评估~
  3. B+树的所有叶子节点,构成链表,此时比较方便查询~~(比如查询学号>5并且<11的同学~~只需要先找到5所在的位置,再找到11所在位置,从5沿着链表访问到11,中间结果即为所求)
  4. 由于数据都在叶子节点上,非叶子节点,只存储key,导致非叶子节点,占用空间是比较小的~~这些非叶子节点就可能在内存中缓存(或者是缓存的一部分)

补充

B树(B-树)‘-’是连接符,不是减号,不能念成B-树

 B 树可以认为是一个N叉搜索树

树的度:所有节点的度的最大值~

节点的度是有几个孩子~

节点的子树多了节点上保存的 key 多了,意味着在同样 key 的个数的前提下,B树的高度就要比 二叉搜索树 低很多!! 

B+树,在B树的基础上有做出了改进

整个树的所有数据都是包含在 叶子节点中 的!!

上述这个结构是默认id是表的主键了。如果这个表里有多个索引呢?

针对 id 有主键索引

针对 name 又有一个索引~

  • 表的数据还是按照 id 为主键,构建出B+树,通过叶子节点组织所有的数据行~
  • 其次,针对name这一列会构建另一个B+树,但是这个B+树的叶子节点就不再存储这一行的完整数据,而是存主键 id 是啥~
  • 此时,如果你根据name来查询,查到叶子节点得到的只是 主键 id,还需要再通过主键 id 去主键的 B+ 树里在查一次~~(查两次B+树)

上述过程称为“回表”,这个过程,都是mysql自动完成的,用户感知不到

特点:

  1. 一个节点,可以存储N个key,N个key划分出了N个区间(而不是N+1个区间)
  2. 每个节点中key的值,都会在子节点中也存在(同时该key是子节点的最大值)
  3. B+树的叶子节点,是首尾相连,类似一个链表~
  4. 由于叶子节点,是完整的数据集合,只在叶子节点这里存储数据表的每一行的数据~~而非叶子节点,只存key值本身即可~

事务

比如经典场景:转账~ 淘宝买东西,下单

account(id, balance)

               1     1000

               2       0 

1给2转账 500

1)update account set balance = balance-500 where id =1;

2)update account set balance = balance+500 where id = 2;

假设,在执行转账过程中,执行完 1 之后,数据库崩溃了/主机宕机

此时这个转账就僵硬了!

1 的钱扣了,但是2 的钱没到账!

 事务就是为了解决上述问题~~

事务的本质就是把多个 sql 语句给打包成一个整体原子性atom),要么全部执行成功,要么就一个都不执行~~而不会出现“执行一半”这样的中间状态!

(不是真的没执行,而是看起来好像没执行一样,执行了,执行一般出错了,出错之后,选择恢复现场,把数据还原成未执行之前的状态了~) 这个恢复数据的操作,称为“回滚”(rollback)

此处是需要额外的部分来记录事务中的操作步骤。(数据库里专门有个用来记录事务的日志)

正因为如此,使用事务的时候,执行sql的开销是更大的,效率是更低的~

start transaction;
-- 阿里巴巴账号减少2000
update account set money = money-2000 where name = '阿里巴巴';
-- 四十大盗账户增加2000
uodate account set money = money+2000 where name = '四十大盗'
commit;

事务四个特性

  • 原子性:(最核心的特性)初心~
  • 一致性:事务执行前后,数据得是靠谱的~
  • 持久性 :事务修改的内容是写在硬盘上的,持久存在的,重启也不丢失
  • 隔离性:这个隔离性是为了解决“并发”执行事务,引起的问题~

并发事务:一个餐馆(服务器),同一时刻要给多个顾客(客户端)提供服务

此时,服务器同时处理多个客户端的请求,就称为“并发”。

引起问题:如果并发的这些事务,是修改不同的表/不同的数据,没啥事~~

如果修改的是同一个表/同一个数据~~可能会带来一系列问题!

比如多个客户端一起尝试对同一个账户进行转账操作,此时就可能会把这个数据给搞乱了~

事务的隔离性,存在的意义就是为了 在数据库并发处理事务的时候不会有问题(即使有问题,问题也不大)

并发事务可能产生的问题 

  •  脏读问题

        eg:我写代码的时候,同学路过,看了眼我的代码,他走后,我可能把代码修改了。

        考试的时候,有的同学瞄旁边同学的卷子~~有的人就故意使坏,故意写个错误答案,让别人抄走,然后自己再偷偷改过来。

        一个事务A正在对数据进行修改的过程,还没提交之前,另外一个事务B,也对同一个数据进行了读取。此时B的读数据就成为“脏读”,读到的数据也就成为“脏数据”。因为A数据很可能把数据给改了。

解决:引入了“写操作加锁”这样的机制。

就比如和别人商量好,我写代码的过程中,你别来看,等我改完,提交到码云上,你再通过我的码云来看。

当我写到时候,同学没法读,意味着我的“写操作”和同学的“读操作”不能并发了~

这个给写加锁操作,就降低了并发程度(降低了效率),提高了隔离性(提高了数据的准确性)

  • 不可重复读

        eg:还是写代码,同学想看。约定好,我写的时候,不许看,等我提交了,再通过码云来看(约定好写加锁了)

        我写代码,提交了版本1,此时就有同学开始都这个代码了~于是我又打开代码,继续修改代码~~然后又提交版本2.

        这个同学开始读的过程中,读到的是版本1 的代码,读着读着,我提交了版本2,此时这个同学读的代码,刷的一下变样了!

        这个问题,叫做“不可重复读”

        事务1 已经提交了数据,此时事务2 开始去读取数据。在读取过程中,事务 3又提交了新的数据。此时意味着同一个事务2 之内,多次读数据,读出来的结果是不相同的~(预期是一个事务中,多次读取结果的一样)

解决:引入了“读操作加锁”这样的机制

同学发现这个问题,就知道是在他读到过程中,我又改代码了,于是来找我,和我约定~他读代码的时候,我也不能修改!

        通过这个读加锁,又进一步的降低了事务的并发处理能力(处理效率也降低),提高了事务的隔离性(数据的准确性)

  • 幻读

        eg:由于约定了读加锁,同学读的时候,我不能该代码~我干等有点无聊,所以我想办法,同学读student.java我就创建一个teacher.java。这样的情况,大多数情况下都没事,少数情况下,个别同学发现了,读代码读着读着突然冒出个Teacher.java,有点同学就巨大接受不了了~~

        在读解锁和写加锁的前提下,一个事务两次读取同一个数据,发现读取的数据值是一样的,但是结果集不一样~(Student.java 代码内容不变,但是第一次看到是只有student.java这个文件,第二次看到的是student.java和teacher.java了)这就叫做幻读

解决:数据库使用“串行化”这样的方式来解决幻读。彻底放弃并发处理事务,一个接一个的串行的处理事务。这样做,并发程度是最低的(效率最低的),隔离性是最高的(准确性也是最高的)

        相当于同学要求我在她们读我的代码的时候,我不要摸电脑,必须强制摸鱼~ 

四种隔离级别

对应上述问题,Mysql提供了4种隔离级别,就对应上面的几个情况~

  •  read uncommitted 没有进行任何锁限制,并发最高(效率最高),隔离性最低(准确性最低)
  • read committed 给写加锁,并发程度降低,隔离性提高了
  • repeatable read 给写和读都加锁,并发程度又降低,隔离性又提高了
  • serializable 串行化,并发程度最低,隔离性最高

以上四种隔离级别可以通过修改mysql的配置文件,来设置当前mysql工作在哪种状态下~ 

隔离级别的选择

eg:转账的时候,一分钱都不能差,哪怕慢点,也得转对!!

准确性要拉满,效率不关键~~

抖音,点赞,一个视频有多少赞,要求快,赞的数量差个十个八个,都没事~追求效率,准确性都不关键~

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

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

相关文章

uview-plus中二级菜单左右联动更改为uni-app+vue3+vite写法

uview-plus3.0重磅发布&#xff0c;全面的Vue3移动组件库 该插件使用的vue2写法&#xff0c;但支持vue3引用&#xff0c;在此基础上修改为uni-appvue3vite; <template><view class"u-wrap mainClass"><!-- <back-header :title"pageTitle&quo…

太累了,是时候让AI数字人来帮我干活了(走,上教程)

阿酷TONY&#xff0c;原创文章&#xff0c;长沙&#xff0c;2023.11.21 关 键 词&#xff1a;AI数字人&#xff0c;生成式AI&#xff0c;智能数字分身适用场景&#xff1a;培训数字人&#xff0c;演讲授课数字人&#xff0c;直播带货数字人特别说明&#xff1a;教程用的是国内…

捷诚管理信息系统 SQL注入漏洞复现

0x01 产品简介 捷诚管理信息系统是一款功能全面&#xff0c;可以支持自营、联营到外柜租赁的管理&#xff0c;其自身带工作流管理工具&#xff0c;能够帮助企业有效的开展内部审批工作。 0x02 漏洞概述 捷诚管理信息系统CWSFinanceCommon.asmx接口存在SQL注入漏洞。未经身份认…

【UE】线框材质

一、方式1 新建一个材质&#xff0c;混合模式设置为“已遮罩”&#xff0c;勾选“双面” 勾选“线框” 然后可以随便给一个自发光颜色&#xff0c;这样最基本的线框材质就完成了 二、方式2 新建一个材质&#xff0c;混合模式设置为“已遮罩”&#xff0c;勾选“双面”&#x…

Idear 中签出git项目分支为灰色

--签出git上的项目 git clone git项目地址 --查看目录 $ dir --查看分支 $ git branch -a --签出分支 $ git checkout origin/v1.0 签出后&#xff0c;使用idear打开项目&#xff0c;项目关联git信息

uniapp中swiper 轮播带左右箭头,点击切换轮播效果demo(整理)

可以点击箭头左右切换-进行轮播 <template><view class"swiper-container"><swiper class"swiper" :current"currentIndex" :autoplay"true" interval"9000" circular indicator-dotschange"handleSw…

Java语言的特点||运算符

Java语言的特点||运算符 1&#xff1a;2&#xff1a;JDK, JRE&#xff0c;JVM知识&#xff1a;3&#xff1a;注释4&#xff1a;标识符5&#xff1a; Java编译过程&#xff1a;6&#xff1a;赋值7&#xff1a;switch8:布尔表达式9&#xff1a;判定素数10&#xff1a;打印 1 - 10…

【Linux网络】搭建内外网的网关服务器,实现DNS分离解析与DHCP自动分配

一、实验要求&#xff1a; 二、实验思路剖析&#xff1a; 网关服务器&#xff1a; 客户端准备&#xff1a; 实操&#xff1a; 第一步先安装dhcp服务和bind服务 第二步双网卡&#xff0c;配置网卡的ip地址 第三步&#xff1a;开始配置dhcp 第四步&#xff1a;做dns分离解析…

单线圈无刷直流电机的电机驱动器芯片GC1298R/S,可替代EUM6898/德信,应用于车用信息娱,HUD 投影仪调整等产品中

GC1298R/S 是单线圈无刷直流电机的电机驱动器。它具有高效的直接 PWM控制方式&#xff0c; 它可以控制无刷直流电机转速。它集成了最低速 度限制模式、可调速度斜率控制模式、软启动模 式、风扇转速计、锁保护、自动重启、TSD、OCP 和噪声控制模式&#xff0c;噪声控制模式根据…

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…

使用Arrays.asList与不使用的区别

在写算法的时候&#xff0c;遇到了有的题解使用的是Arrays.asList&#xff0c;也有的是直接新建一个List集合将元素加进去的。 看了一下算法的时间&#xff0c;两者居然相差了9秒。 算法原地址&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长…

类和对象学习笔记

类和对象 类的定义this指针类的6个默认成员函数构造函数析构函数拷贝构造函数赋值运算符重载赋值运算符重载运算符重载const成员 取地址操作符重载const取地址操作符重载 初始化列表explicit关键字static成员匿名对象友元内部类拷贝对象时编译器的优化 类的定义 c类的定义形式…

基于Bagging集成学习方法的情绪分类预测模型研究(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【每日OJ —— 232.用栈实现队列(栈)】

每日OJ —— 232.用栈实现队列&#xff08;栈&#xff09; 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09;2.解法2.1.方法讲解2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09; 2.解法 2.1.方法…

windows上 adb devices有设备 wsl上没有

终于解决了&#xff01;&#xff01;&#xff01;&#xff01; TAT&#xff0c;尝试了很多种办法。 比如WSL中的adb和Windows中的adb版本必须一致&#xff0c;一致也没用&#xff0c;比如使用 ln 建立链接也没用。 这个解决办法的前提是windows中的abd是好用的。 ●在windows…

简单php反序列化实现执行代码

简单php反序列化实现执行代码 反序列化举例 首先定义类和对象&#xff0c;然后输出序列化和反序列化结果看看这是个什么东西 <?phpclass Stu{public $name;public $age;public $sex;public $score;}$stu1 new Stu();$stu1->name "order";$stu1->age …

部署Kubernetes Dashboard

Dashboard简介 Dashboard 是基于网页的 Kubernetes 用户界面。 可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。 Dashboard创建 #创建pods kubectl apply -f https://raw.githubusercontent.com/kub…

北邮22级信通院数电:Verilog-FPGA(10)第十周实验 实现移位寄存器74LS595(仿真方法验证)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 使用FPGA开发板验证的教程&#xff0c;请参考 北邮…

MySql分区

一、什么是分区 MySQL分区是一种数据库设计和管理技术&#xff0c;它允许你将表分割成独立的、具有特定规则的存储单元。每个分区可以独立地进行管理&#xff0c;包括备份、恢复和优化。分区的主要目的是提高查询性能、简化维护以及实现数据的更有效管理。 以下是MySQL分区的…

高速数据时代的引领者:ETU-LINK 100G DAC全系列技术简介

伴随科技的不断进步&#xff0c;我们正迅速迈向一个高速数据时代。在这个时代&#xff0c;数据的传输速度已经成为发展的重要因素之一。ETU-LINK推出的100G DAC全系列产品&#xff0c;助力高速数据传输领域的新一轮发展。 一、100G DAC全系列产品解析 100G QSFP28 DAC无源高速…