MySQL数据库知识总结

MySQL数据库知识总结

  • 一、基本概念及其介绍
  • 二、数据库中的数据类型
    • (一)数值类型
    • (二)字符串类型
    • (三)日期类型
  • 三、数据库基础语法
    • (一)数据库的常用操作
    • (二)数据表的常用操作
      • 1.创建、查看、新增、删除表
      • 2.select查找表数据
        • (1)全列查询
        • (2)指定列查询
        • (3)查询列加工后的结果
        • (4) 查询字段指定别名
        • (5)针对查询结果去重
        • (6)对查询结果进行排序
      • 3.update 修改表数据
      • 4.delete 删除表数据
  • 四、 数据库的约束
    • (一)约束类型
    • (二)主键约束
    • (三)外键约束
  • 五、表的设计
    • (一)一对一的关系
    • (二)一对多的关系
    • (三)多对多的关系
  • 六、复杂语法
    • (一)select + insert
    • (二)复杂查询
      • 1.聚合查询
      • 2.分组查询
      • 3.联合查询
        • (1)笛卡尔积
        • (2)内链接
        • (3)外链接
        • (4)自链接
        • (5)子查询
        • (6)合并查询
  • 七、索引和事务
    • (一)索引
      • 1.基本介绍
      • 2.B树索引
      • 3.B+ 树索引
    • (二)事务
      • 1.基本介绍
      • 2.事务的四个基本属性(ACID)
      • 3.并发执行可能存在的问题
        • (1)丢失修改
        • (2)读脏数据
        • (3)不可重复读
        • (4)幻读问题
      • 4.MySQL数据库四种隔离级别
      • 5.封锁粒度
  • 八、JDBC
    • (一)JDBC链接MySQL
      • 1.创建数据源
      • 2.建立连接
      • 3.构造SQL语句
      • 4.执行SQL
      • 5.释放资源
    • (二)数据库操作
      • 1.增加、修改、删除
      • 2.查询数据


一、基本概念及其介绍

  • 大多数数据库中的数据保存在硬盘(外存)上,例如 MySQL,但也有少部分数据库是把数据存储在内存中的,例如Redis、Tair
  • 关系型数据库按照表格的方式来组织,例如MySQL、Oracle、SQL Server
  • 非关系型数据库按照文档/键值对的方式组织,例如Redis、HBase、MongoDB

二、数据库中的数据类型

(一)数值类型

数据类型大小java对应C对应
BIT[M]M指定位数,默认1Booleanchar[]
TINYINT1字节Bytesigned char
SMALLINT2字节Shortshort int
INT4字节Intergerint
BIGINT8字节Longlong long int
FLOAT(M,D)4字节Floatfloat
DOUBLE(M,D)8字节Doubledouble
DECIMAL(M,D)M/D最大值+2BigDecimalchar[]
NUMERIC(M,D)M/D最大值+2BigDecimalcahr[]
  • 上表中的 BIT[M] 代表二进制数,M范围从 1 到 64,存储数值范围从 0 到 2^M-1。数据类型中的 FLOAT(M,D) 和 DOUBLE(M,D),M指定长度,D指定小数位数,但是在实际使用时可能会发生精度丢失,于是便使用 DECIMAL(M,D) 和 NUMERIC(M,D) 以表示精确数值。
  • 注意 JAVA 中没有 unsigned 类型,因此为了防止溢出,在 MySQL 中尽量不使用 unsigned 类型。

(二)字符串类型

数据类型大小说明java对应C对应
VARCHAR(SIZE)0-65535字节可变长度字符串Stringchar[]
TEXT0-65535字节长文本数据Stringchar[]
MEDIUMTEXT0-16 777 215字节中等长文本数据Stringchar[]
BLOB0-65535字节二进制形式的长文本数据byte[]char[]
  • 其中 VARCHAR(SIZE) 可以指定字符串的大小,SIZE 的单位为字符;BLOB 用于表示二进制数据,例如mp3、jpg、class等二进制文件。在 SQL 中表示字符串,使用单引号或双引号都可,因为 SQL 没有单独的字符类型。

(三)日期类型

数据类型大小说明java对应C对应
DATATIME8字节从1000年到9999年,不会进行时区的检索及转换java.Date、java.sql.TimesstampMYSQL TIME
TIMESTAMP4字节从1970年到2038年,自动检索当前时区并进行转换java.Date、java.sql.TimesstampMYSQL TIME

三、数据库基础语法

在MySQL数据库中,大小写不进行区分

(一)数据库的常用操作

  • 显示当前服务器上有哪些数据库:show databases;
  • 创建数据库:create database 数据库名;
  • 选中/使用数据库:use 数据库名;
  • 删除数据库:drop database;

(二)数据表的常用操作

1.创建、查看、新增、删除表

(1)创建表 :creat table 表名(列名 类型,列名 类型…);

  • 进行表操作之前,需要先选中数据库(use),创建表后面可以加comment '说明’表示给某列加上注释

(2)查看所有表信息 :show tables;

(3)查看表结构,查看某表中有几列,每一列的名字、类型、补充信息 :desc 表名;

(4)删除表:drop table 表名;

(5)新增表中的一行:insert into 表名 values(列的值)

  • values后的括号内容要和列头匹配
  • 如果某一列是 datetime 类型,可以通过指定格式字符串进行插入,例如 2024-12-12 或 2024-12-12 21:21:00;也可通过 now() 函数来插入一个当前的系统时间,例如 insert into test value(1,now());
  • insert进行插入时,可以只插入其中的某一列或某几列,其余列将采用默认值
  • 如果需要插入中文,需要把数据库配置成 utf-8 编码方式(MySQL默认拉丁文编码)
  • insert也可以一次插入多行,()之间用逗号隔开,例如:insert into student values(1,‘zhang’,19),(2,‘aaa’,20); 且一次插入N条数据比一次插入一条数据分成N次要快很多倍。

2.select查找表数据

 select 查找的结果是一个“临时表”,这个“临时表”不存储在硬盘上,而是在内存中,输出时候,数据也被释放了,即select不会影响到服务器这边硬盘上保存的数据。

(1)全列查询
  • 展示表中所有信息: select * from 表名;  

  • 这个操作是一个危险操作,当生产环境保存的数据量达到 TB 级别时,此时 MySQL 服务器就会疯狂的读取硬盘数据,但硬盘的读取速度存在上线,此时就会瞬间把硬盘的 IO 给吃满;同时 MySQL 服务器又会立即返回响应数据,但返回的数据也很大,则会把网卡的带宽吃满,而生产环境的服务器无时不刻需要给普通用户提供相应,一旦服务器的硬盘和网络被吃满,此时数据库服务器就难以对其他客户端的请求做出相应。

(2)指定列查询
  • 展示指定列的所有信息 :select 列名, 列名…from 表名;
  • 查询时会显式的告诉数据库要查的是哪些列,数据库就会有针对性的返回数据了,相比于全列查询,这种查询效率要高很多。
(3)查询列加工后的结果
  • select 列操作…from 表名;
  • select可以返回对指定列操作后的结果,例如select name, chinese+10 from exam;原始数据库服务器上的数据并没有发生改变,只是把加工后的结果作为临时表显示出来了
  • 也可以返回各个列相加的总和例如:select name,chinese+math+english from exam,若对应的成绩类型设定保留三位有效数字,例如设定chinese 数据类型为decimal(3,1),保留3位有效数据精度为1,而总和得到的结果变为四位有效数据,是因为得到的临时表的类型会自动适应,以保证计算结果的正确。
(4) 查询字段指定别名
  • select 列名, 列名 as 别名…from 表名;  给列操作后的结果起一个别名,例如select name,chinese+math+english as sum from exam,其中的as可以省略,但省略之后容易混淆。
(5)针对查询结果去重
  • 去掉重复的记录select distinct 列名, 列名…from 表名;  针对多个列进行去重,需要多个列的值都相同才视为重复
(6)对查询结果进行排序
  • 对查询结果(临时表)进行排序,不会影响到数据库服务器上的原始数据 select 列名… from 表名 order by 列名 asc/desc; 
  • asc为升序,desc为降序,省略不写时默认为升序asc。有的数据记录中是带有NULL值的,此时默认NULL是最小的。
  • 排序可以按照表达式或者别名来进行排序,例如:select name,chinese+math+english as sum from exam order by sum;
  • 排序也可以指定多个列进行排序,先按照第一列排序,如果第一列相同,相同结果之间按照第二个列进行排序:select name,math chinese from exam order by matn,chinese;

(7)条件查询:select 列名 from 表名 where 条件;

  • 比较运算符
运算符说明
>、>=、<、<=基本比较
=等于,特殊的当NULL=NULL时结果为NULL
<=>比较相等,和 ’=‘ 一样,但是比较两个NULL值的时候结果为真
!=、<>不等于
BETWEEN a and b匹配范围[a,b]
IN {option,…}如果是option中的任意一个返回TRUE(1)
IS NULL是NULL
IS NOT NULL不是NULL
LIKE模糊匹配,%表示任意多个(包括0个)任意字符,_表示任意一个字符
  • 逻辑运算符
运算符说明
AND逻辑与
OR逻辑或
NOT逻辑非
  • where语句不能使用别名进行查询,例如:select name,chinese+math+english as sum from exam where sum<200 会产生错误
  • 查询时 AND 的优先级高于 OR ,同时使用时候如果想要打破优先级,需要用小括号 () 包裹优先执行的部分,例如select * from exam where chinese>80 or math>70 and english>70;&nbsp 语句等价于select * from exam where chinese>80 or (math>70 and english>70);
  • 在模糊查询中,语句:select * from exam where name like ‘王%’; 表示以王开头的所有字符串,当表示为%王%时则表示含有 ‘王’ 的任意字符串
  • 对于NULL值的判断,一般来说使用 IS NULL 语句,因为 NULL=NULL 的结果为 false,NULL<=>NULL 的结果为true

(8)分页查询,SQL中使用limit来实现分页查询:select 列名 from 表名 limit 查找数量 offset 查找下标;

  • 举例来说,select * from exam limit 3; 表示只取出结果中的三条,select * from exam limit 3 offset 3; 表示从下标为3 的记录开始,再向后找三条(下标从0开始)

3.update 修改表数据

 使用 update 进行修改,修改会改变数据库服务器上面的原始数据:update 表名 set 列名 =数值, 列名 =数值 … where 条件; 其中 set 不能省略,where 后加的条件表示对那几行进行修改,省略 where 的话就是对所有记录进行修改。

  • update 修改操作可以和 order by 排序和 limit 分页查询等操作一起使用,例如:update math=math+10 order by chinese+math+english limit 3;表示对总成绩倒数后三位的同学,其数学成绩加10

4.delete 删除表数据

delete from 表名 where 条件

  • 注意区分drop和delete操作,drop一般用作删除整个表,而delete操作用于删除表中的每行数据,即使删除了表中所有的数据delete from 表名;表还在但内容为空。

四、 数据库的约束

(一)约束类型

约束针对各个列之间,包含以下类型

类型说明
NOT NULL某列数据不能为空 ,插入空值会报错
UNIQUE表示数据唯一,不能插入数值重复的数据
DEFAULT规定没有给列赋值时的默认值
PRIMARY KEY主键约束,设定数据的唯一身份标识,相当于是 NOT NULL 和 UNIQUE 的结合
FOREIGN KEY外键约束,保证一个表中的数据匹配另一个表中的值的参照完整性
CHECK确保列中的值符合指定的条件,但MySQL并不支持

(二)主键约束

  • 主键约束对于一个表来说,只能指定一个列
  • 可以设定主键为一个自增的序列(1、2、3 …)语法为:create table student (id int primary key auto_increment,name varchar(20)); 设定好自增主键后就可以不指定自增主键的值了,直接交给MySQL分配即可,直接插入insert into student values(null,‘张三’); 每次新增一个记录,都会产生一个新的id
  • 自增主键也可以手动指定,但是一旦指定过后,后续新插入的数据就会从当前id开始自增,例如表中已有 id 为1,2,3,此时insert into students values(10,‘张三’);&nbnsp;后面插入的数据会从10之后开始排

(三)外键约束

  • 外键约束描述的是两张表的两个列之间的’依赖关系‘,子表依赖于父表,要求子表中对应的记录在父表中存在,假设列名1设定的外键,为语法格式为:creat table 子表名 (列名1 类型,列名2 类型… foreign key 子表名(列名1) references 父表名(列名1));
  • 外键约束写在表的最后,例如表 student 中的 classId 和 class 表中的 classId 需要对应,即当前 student 表中的 classId 需要依赖于 class 表中的 classId ,语法为:creat table student (studentId int primary key auto_increment,name varchar(20),classId int,foreign key(classId) references class(classId)); 再新增/修改记录之前,MySQL会拿着新增/修改的对应值在父表中先查询,查询到之后才能执行操作,否则会报错
  • 外键约束同样约束着父表,当父表中的某个记录被子表依赖的时候,进行删除操作会报错,没有被依赖的数据可以删除成功
  • 在父表中先查询,查询后新增/修改,这里的查询操作可能是一个成本较高的操作,外键约束其实要求父表中被依赖的这一列必须要有索引,通过索引大大提高查询速度

五、表的设计

 实体与实体之间并不是孤立存的,而是存在对应关系,这样的对应关系也需要体现在表当中,实体间的关系包含:一对一、一对多、多对多

(一)一对一的关系

  • 对于一对一的关系,在数据库设计中可以通过以下两种方式进行表示:1.两个实体用一张表来表示2.用两张表来表示,其中一张表包含了另一个表的id

(二)一对多的关系

  • 对于一对多的关系,在数据库设计中同样可以通过以下两种方式进行表示,以下面的学生表和教室表为例
  • 例如一个学生处于一个班级当中,一个班级可以包含多个学生。此时可以在班级表中新增一列,表示这个班级里的所有的学生id,或者班级表不变,在学生表中增加一列表示classId,通过这两种方式,能够知道一个班级对应哪些同学
  • 对于一对多的关系,由于 MySQL 没有提供数组类型,因此只能采用方法2。但是 Redis 这样的数据库有数组类型,可以考虑方法1

(三)多对多的关系

  • 对于多对多的关系,在数据库设计中使用建立关联表的方式来表示两个实体之间的关系
  • 例如,一个学生可以选多个课程,一个课程可以包含多个学生,学生表student(studentId,name)、课程表course(courseId,name),此时创建一个关联表学生-课程表student_course(studentId,courseId),通过两个列之间的对应能够知道每个同学选了哪些课程,每个课上有哪些同学

六、复杂语法

(一)select + insert

  • 从上一个表中查询得到的结果,作为下一个表要插入的数据
  • 例如,需要把A的记录插入到B中,insert into B select * from A; 在这个语句中,会先执行查找操作,针对查找到的每个结果执行插入操作,需要保证从 A 中查询结果的列数和类型和 B 表匹配
  • 对于两个表 A 和 B,只需要保证列数和类型相同,顺序不匹配时也能进行插入,例如表B(name varchar(20),id int),表A(id int,name varchar(20)),此时也能插入成功
  • 另外,还可以给后面的 select 指定一些其他的条件/排序/去重,插入的结果是select 执行结果的临时表

(二)复杂查询

1.聚合查询

 聚合查询把多行的数据进行了关联操作,MySQL内置了许多聚合函数

函数说明
COUNT( [DISDINCT] expr)返回查询到的数据的数量
SUM( [DISDINCT] expr)返回查询到的数据的总和,不是数字没有意义
AVG( [DISDINCT] expr)返回查询到的数据的平均值,不是数字没有意义
MAX( [DISDINCT] expr)返回查询到的数据的最大值,不是数字没有意义
MIN( [DISDINCT] expr)返回查询到的数据的最小值,不是数字没有意义
  • ( [DISDINCT] expr) 中的DISDINCT表示去重,expr表示列名或者表达式
  • 需要注意,当指定返回某列的count总和时,NULL值不会进行记录,例如 select count(chinese) from exam; 返回的结果没有计算其中 ( name=‘张三’ , chinese=NULL ) 的这条数据,但是select count(*) from exam; 语句返回的结果为表的行数,会计算该条数据
  • 聚合查询 还可以搭配where子句进行使用,首先基于条件进行筛选,然后对筛选结果进行聚合,例如select sum(english) from exam where english>70;

2.分组查询

 SELECT 中使用GROUP BY自己可以对指定列进行分组查询,针对分组之后的结果,可以使用 HAVING 进行条件过滤,而where 则是在分组之前执行

  • 使用GROUP BY语句进行查询时,select 指定的字段必须是"分组依据字段",其他字段若想出现在select中必须包含在聚合函数中
  • select role,max(salary),min(salary),arg(salary) from emp group by role;求得不同角色的最高、最底、平均工资,先进行分组,再根据分组分别来执行每个组的聚合函数
  • 例如select role,arg(salary) from emp where name!=‘张’ group by role; 这个语句会先把姓名不是张的行筛选出来,再进行分组
  • select role,avg(salary) from emp group by role having avg(salsry)<10000; 这个语句先执行分组,分组后再根据平均工资进行筛选,选择得到平均工资小于10000的结果

3.联合查询

 联合查询即把多个表记录合并在一起再进行查询

(1)笛卡尔积
  • 可以理解为两张表之间的排列组合,针对两张表 A,B 笛卡尔积的列数就是 A 的列数 + B 的列数,行数就是 A 的行数* B 的行数
  • 在MySQL中进行笛卡尔积,直接select 列名 from 表名1,表名2 from 后面可以跟多个表名,表明之间用逗号隔开
  • 笛卡尔积是一个单纯无脑的排列组合,这些组合结果并不一定是有意义的,例如对于两张表:学生表student(studentId int,name varchar(20),classId int),教室表class(classId int,name varchar(20)),两张表中都有classId,classId相等的记录就是需要保留的记录,此时的classId相等这样的条件,就称为"链接条件"
  • 依据上一个例子,可以通过链接条件保留需要的数据,select student.name, class.name from student,class where student.classId = class.classId;
  • 笛卡尔积也相当于一个临时表
(2)内链接

 内连接的语法格式为:select 列名 from 表1 inner join 表2 on 条件 其中inner join表示内链接,inner可以省略,或者写成select 列名 from 表1,表2 from where 条件

 现在创建下面的表,涉及三个实体:学生、班级、课程。学生和班级是一对多的关系,学生和课程之间是多对多的关系,分数表实际上是学生表和课程表之间的一个关联表。表中添加了若干信息

--班级表,包括班级id、班级名
creat table class(id int primary key auto_increment,name varchar(20),'desc' varchar(20)
);
--学生表,包括学生id、学号、姓名、qq邮箱
creat table student(studentId int primary key auto_increment,sn varchar(20),name varchar(20),qq_mail varchar(20)
);
--课程表,包括课程id、课程名
creat table course(id  int primary key auto_increment,name varchar(20)
);
--分数表,包括成绩、学生id、课程id
creat table score(score decimal(3,1),student_id int,course_id int
);
  • 获取张三学生的成绩,需要在student表中获取学生信息,再从score表中获取分数信息,需要针对student表和score表进行笛卡尔积,语法为:select student.name,score.score from student,score where student.id = score.student_id and student.name=‘张三’;
    或者写成:select student.name,score.score from student join score on student.id = score.student_id and student.name=‘张三’;

  • 查询所有同学的总成绩,及同学的个人信息:select student.name,sum(score.score) from student,score where student.id = score.student_id group by student.id;

  • 查询所有同学的单科成绩,及同学的个人信息涉及三张表student、score、course,语法为:select student.name, corse.name, score.score from student,score,course on where student.id = score.student_id and course.id = score.course_id;
    或写成:select student.name, corse.name, score.score from student join score on student.id = score.student_id join course on course.id = score.course_id;

  • 对于三张表之间的内连接,语法格式为:from 表1,表2,表3 from where 条件 或者 from 表1 join 表2 on 条件 join 表3 on 条件

(3)外链接

 左外连接的语法为: select 列名 from 表1 left join 表2 on 条件
 右外连接的语法为: select 列名 from 表1 right join 表2 on 条件

 举例来说,对于这样两张表:student 表和class表。student 表,包含学生编号、姓名、课程编号,其具体信息如下,

idnameclassId
1张三1
2李四1
3王五2
4赵六3

 class表,包含课程编号、课程名,其具体信息如下,

idname
1C语言
2高等数学

 若设置内连接 select * from student inner join class on student .classId = class.id; 查询得到的结果如下,可以发现赵六所对应的数据消失了

idnameclassIdidname
1张三11C语言
2李四11C语言
3王五22高等数学

 此时设置左外连接select * from student left join class on student .classId = class.id;得到的结果表如下,赵六所对应的数据显示出来了,但由于class表中没有对应数据,因此右侧为NULL

idnameclassIdidname
1张三11C语言
2李四11C语言
3王五22高等数学
4赵六3NULLNULL
  • 总的来说 :左外链接是以左侧的表为主,左侧表中的每个记录在左外连接中都有体现,右侧的列可以为NULL
    右外链接是以右侧的表为主,右侧表中的每个记录在右外连接中都有体现,左侧的列可以为NULL
(4)自链接

 自链接指在同一张表连接自身进行查询,即自己和自己进行笛卡尔积。自链接的本质是把行和行之间的比较转化成列和列之间的比较
 以内连接创建的四个表为例,包含班级表、学生表、课程表、分数表。现在需要寻找语文分数大于数学分数的成绩信息,举例有下面的一些信息,首先需要在课程表corse中找到两门课对应的编号,分别为1,2

idname
1语文
2数学
3英语

 再从分数表score中寻找student_id相同,corse_id=1的成绩 > corse_id=2的成绩的结果,此时需要每行之间进行比较

scorestudent_idcourse_id
8011
79.512
9913
9021
9122
9023
8031
7633

 此时可以通过自链接将行之间的比较转化成列的,首先执行语句 select * from score as s1,score as s2 where s1.student_id=s2.student_id; 得到的笛卡尔积如下(仅展示了student_id=1笛卡尔积得到的结果,其余不进行显示),此时s1.course_id=1和s2.course_id=2就可以进行列之间的比较

scorestudent_idcourse_idscorestudent_idcourse_id
80118011
801179.512
80119913
79.5128011
79.51279.512
79.5129913
99138011
991379.512
99139913

 为了减少数据量,可以指定s1表只保留s1.course_id=1的记录,s2表只保留s2.course_id=2的记录,执行语句为:select * from score as s1,score as s2 where s1.student_id=s2.student_id and s1.course_id=1 and s2.course_id=2; 得到的笛卡尔积如下所示,得到的数据大大减少

scorestudent_idcourse_idscorestudent_idcourse_id
801179.512
90219122

 最后只需再添加条件s1.course>s2.course,最终的sql语句为:select s1.student_id,s1.course,s2.course from score as s1,score as s2 where s1.student_id=s2.student_id and s1.course_id=1 and s2.course_id=2 and s1.course>s2.course;

(5)子查询

 子查询是指嵌套在其他sql语句中的select语句,也叫嵌套查询

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

  • 同样采用上述例子,查找与"张三"同班的同学,此时需要先知道张三同学的班级id,再从学生表student中查找id相同的学生,此时的查询语句为:select name from student where class_id=(select class_id from student where name=‘张三’);

  • 多行子查询:返回多行记录的子查询

  • 有的时候子查询可能会查询出多条记录,就不能使用 = ,可能要用到 in、exists 等操作。

  • 查找语文或者英语课程的成绩信息,需要先查询语文和英语的课程id,再根据id找到对应的成绩信息,语句为 select * from score where course_id in (select id from course where name=‘语文’ or name=‘英语’)

  • 或使用exists操作: select * from score as sco where exists (select sco.id from course as cou where (name=‘语文’ or name=‘英语’) and cou.id = sco .id)

  • 使用not exists操作:select * from score as sco where not exists (select sco.id from course as cou where (name=‘语文’ and name=‘英语’) and cou.id = sco .id)

(6)合并查询

 合并查询即把多个查询语句的结果合并到一起,主要用到 union 、union all关键字,union操作会自动进行去重,而union all不会进行去重

  • 通过union把两个sql的查询结果合并到一起,合并的前提是两个sql语句查询的列是相同的
  • 例如,查询id小于3,或者名字为“英文”的课程,sql语句为:select * from course where name =‘英文’ union select * form course where id<3
  • 也可以用or来替换,但是在使用or时,必须保证针对的是同一个表来指定的多个条件查询,而union则不一定针对同一张表

七、索引和事务

(一)索引

1.基本介绍

 索引 (index)的主要作用就是进行查找,通过索引来提高查找的效率,创建索引语句为: creat index 索引名 on 表名(列名); ,删除索引的语句为:drop index 索引名 on 表名;

  • 索引也是需要一定的额外存储空间的,数据量越大,索引消耗的额外空间也就越多。此外,进行增删改查时往往也要同步的调整索引的结构。因此 索引提高了查找的效率,但是也相应占用了更多的空间,降低了增删改的速度
  • 数据库中的查找可以遍历表,但是遍历表的效率比较低,尤其是当数据量达到千万级别时,且MySQL中的数据存放在硬盘中,而硬盘IO的速度比内存的速度慢3-4个数量级,这样遍历表的效率就会非常低。此时就可以通过特殊的数据结构,来表示一些记录的特征,通过这些特征来减少比较次数,加快比较的速率
  • 主键约束primary和数据唯一约束unique自带索引
  • 当表里面已经有很多数据时,创建和删除索引是非常低效的,因此我们最好在创建表的时候把索引规划好,针对线上的数据库不要贸然去创建索引
  • 对于索引的数据结构,使用二叉树时,当元素的数量较多,二叉树的 高度越高,对应的比较次数会变大;而对于哈希表来说,虽然哈希查找的速度很快,但是只能针对相等的情况进行判定,不能对指定范围内的数据进行查找。使用多叉搜索树做索引的结构最为合适,在数据库索引中B+树是最常见的索引结构

2.B树索引

 B树的每个节点上都会存储N个key值,N个key值划分出了N+1个区间,在B树中查找元素,过程和二叉树非常相似,从根节点出发,根据待比较的元素确定下一个区间
 数据是以节点为单位进行磁盘IO比较的,因此每次比较的次数可能相比于二叉树并不会减少,但是整体的速率会提升
在这里插入图片描述

3.B+ 树索引

 B+树也是一个N叉搜索树,每个节点上都包含了多个key值,节点有N个key值,就会被分成N个区间,父节点的值都会 在叶子结点中体现出来,父结点中的值会作为子结点中的最大值(最小值),最下面的叶子节点,就使用链表按顺序排列
 下图为最大值的情况
在这里插入图片描述

  • B+树非常适用于数据库的索引,使用B+树进行索引时,整体的IO次数是比较少的
  • 所有的查询都会落到叶子节点上,即每次查询的IO次数差不多查询速度稳定
  • 叶子节点用链表链接后,非常适合于范围的查找,例如查找3到11范围内的数据,只需要找到3、11在叶子节点中的位置,然后顺着链表即可找到
  • 所有的数据存储(载荷)都是放到叶子节点上的(例如查找学生的id=1,找到叶子节点1,其中叶子节点包含了id=1的学生信息例如姓名年龄等数据),非叶子节点只保存key值即可,因此非叶子节点整体占用的空间较小,甚至可以缓存到内存中,若可以缓存到内存中,此时磁盘IO几乎就没了

(二)事务

1.基本介绍

 事务是指逻辑上的一组操作,组成这个操作的各个单元,要么全部成功,要么全部失败,其目的就是把若干个独立的操作打包成一个整体

  • 在SQL中,有的复杂的任务需要多个SQL来执行,有时也需要打包在一起,即前一个SQL为后一个SQL提供支持,如果后一个SQL不执行了或者执行出问题了,前一个SQL也就失去意义了

  • 事务保证了原子性:即要么全部执行完,要么一个都不执行,即任务不能在被细分了

  • 当出现执行失败后,由数据库自动执行一些“还原”性的工作,来消除前面的SQL带来的影响,看起来好像一个都没执行一样,这样的“还原”性的工作就是“回滚”

  • 数据库先执行第一个SQL语句,接下来执行第二个SQL语句的时候出现了意外导致无法执行,那么接下来数据库就会还原之前的操作,即把之前改动过的数据给还原回去

  • 数据库会把执行的过程给记录下来,方便回滚,但是数据库要想记录详细的操作,也是需要消耗大量的时间和空间的,所以一般只保存最近几天的记录,不会保存全部记录

 开启事务语句为:start transaction; 后接多条SQL语句,最后回滚或者提交:rollback/commit,rollback即全部失败,commit即全部成功

start transaction:
update accout set money = money - 200 where name = '张三';
update accout set money = money + 200 where name = '李四';
commit;

2.事务的四个基本属性(ACID)

  • 原子性(Atomicity):一个事务要么全部执行完,要么一个都不执行,即任务不能在被细分了
  • 一致性(Consistency):在事务执行之前和执行之后,数据库中的数据都必须是合理合法的,即从一个一致状态转化到另一个一致状态(例如,转账后不能出现账户余额是负数的情况、从A用户中减少转账后必须有其他账户的余额增加)
  • 隔离性(Isolation):一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability):一个事务一旦提交,其操作的数据就持久化存储起来了,即数据就写入到硬盘中了

3.并发执行可能存在的问题

 隔离性描述的是事务并发执行时,多个事务在尝试修改/读取同一份数据时容易出现一些问题,例如“读脏数据”(读取了修改之前的数据),此时可以通过给写操作加锁来避免读脏数据,这样事务之间的隔离性就变高了,并发性就降低了

 并发性和隔离性是不可兼得的

(1)丢失修改
  • 丢失修改(写-写冲突):多个事务在尝试修改同一份数据时容易出现的问题,两个事务在并发下同时进行数据修改,后一个事务的更新覆盖了前一个事务的操作,就好像前一个事务没有进行操作一样,即同时进行两个写操作,第一个进行的写操作被覆盖了
  • 例如,事务T1、T2同时对A进行操作,T1先读取A的内容,随后T2读取A的内容,T1将A的值减一返回到A,之后T2同样进行此操作,得到的A值为减1,并不是减2,导致事务T1的修改被丢失

请添加图片描述

(2)读脏数据
  • 读脏数据(读-写冲突):修改事务更新了数据,之后读取事务在此时读取了同一份数据,由于某些原因,修改事务RollBack了操作,导致读取事务所读取的数据就会是不正确的,即在写的过程中读,导致读取了未修改的数据,通过给写操作加锁,让事务在写的过程中不能读来解决读脏数据
  • 例如,事务T1对数据C进行修改,之后事务T2读取C=200,之后事务T1回滚了之前的操作,C的值恢复为100,导致T2事务读取到了错误的‘脏数据’

请添加图片描述

(3)不可重复读
  • 不可重复读(读-写冲突):一个事务包含多次的读操作,读操作过程中插入了写操作,多次读操作得出的结果并不一致,即在读的过程中写,导致两次读的内容不一致,通过给读操作加锁,让读的时候不能写来解决不可重复读的问题
  • 例如,事务T1读数据A、B进行了求和,之后事务T2对数据B进行了修改,事务T1重复读取数据A、B进行求和验算得到的结果不正确

请添加图片描述

(4)幻读问题
  • 幻读问题,一个事务执行过程中进行多次查询,多次查询的结果不一样(这里特指多数据或少数据)是一种特殊的不可重复读
  • 要解决幻读问题,就需要彻底串行化执行,这样的隔离性最高,并发程度最低,数据最可靠,速度最慢

4.MySQL数据库四种隔离级别

 根据实际需要来调整数据库的隔离级别,通过不同的隔离级别控制事物之间的隔离性/并发程度,MySQL数据库的隔离级别可以分为以下四种,我们可以通过修改my.ini这个配置文件来设置当前的隔离级别

  • read uncommitted; 允许读取未提交的数据,并发程度最高,隔离性最低,会引入脏读+不可重复读+幻读+丢失修改问题
  • read committed; 只允许读另一个事务提交之后的数据,相当于写加锁,并发程度降低了,隔离程度提高了,可以解决脏读问题,但会带来不可重复读+幻读+丢失修改问题
  • repeatable read; 开始读取数据时,不允许对数据进行修改,相当于给读和写都加锁,解决了脏读+不可重复读问题,但会带来幻读+丢失修改问题
  • serializable;串行化,并发程度最低,隔离程度最高,解决了脏读+不可重复读+幻读+丢失修改问题,但是执行速度最慢

5.封锁粒度

 封锁对象的大小称为封锁的粒度。封锁对象可以对整个数据库进行加锁,可也可对某个属性值进行加锁。

  • 封锁粒度与系统的并发度和并发控制的开销密切相关 ,封锁的对象越大,能够封锁的数据单元越少,并发度越小,隔离性越高,系统开销也就越小
封锁粒度被封锁的对象个数并发度系统开销
  • 封锁粒度的一般原则:需要处理大量元组的用户事务,以关系为封锁单元;需要处理多个关系的大量元组的用户事务,以数据库为封锁单位;只处理少量元组的用户事务,以元组为封锁单位
  • 事务虽然为了保证隔离性要进行一系列加锁,但是也不是整个数据库都锁定了,还可以进行其他表或数据的修改

八、JDBC

 JAVA 约定了一组API,即为JDBC,这里的API就包含一些类和方法,通过这些类和方法来实现数据库的基本操作。再由各种厂商提供各自的 “数据库驱动包” 来和IDBC的API对接,这样只需要掌握这一套JDBC API就可以操作各种数据库

(一)JDBC链接MySQL

 JDBC链接MySQL首先要在下载JDBC的驱动,点击进入下载地址,注意下载需要对应的自己的MySQL版本,下载后解压,将jar包放入到项目的lib文件目录中,然后右键lib文件夹Add as Library

JDBC链接MySQL包含下面的几个步骤

1.创建数据源

  //1.创建好数据源DataSource dataSource = new MysqlDataSource();//设置数据库所在的地址((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/databaseName?characterEncoding=utf8&useSSL=false");//设置登录数据库的用户名((MysqlDataSource) dataSource).setUser("root");//设置登录数据库的密码((MysqlDataSource) dataSource).setPassword("123");

 (1)首先要创建DataSource对象,这个对象描述了数据库服务器在哪里。

  • DataSource 是 JDBC 自带的接口,MysqlDataSource来自第三方数据包
  • 第一步操作通过先向上转型,在向下转型的操作,主要是为了后续其他代码中如果有需要用到DataSource,相关的参数类型也是DataSource,数据库进行切换后不需要改动代码
  • DataSource 内置了连接池,在频繁创建/断开链接的时候,这里的 DataSource 会比 DriverManager 的方式更加高效

 (2) 设置数据库所在的地址

  • 设置URL地址时 " jdbc:mysql " 表示这个URL是用于JDBC MySQ的;
  • " 127.0.0.1 “表示IP地址,或MySQL服务器主机的IP地址,一般用于确定网络上主机的位置,127.0.0.1默认为自己的主机,与” localhost "等价
  • " 3306 "表示端口号,即访问主机上的哪个程序,安装MySQL时设置了其默认端口号为3306
  • " databaseName? "表示数据库名,即要链接的MySQL中的哪个数据库
  • " characterEncoding=utf8 "表示字符编码,此时 设定为utf-8
  • " useSSL=false "表示是否在传输的过程中加密,此处设置为不加密

 (3) 设置数据库的用户名和密码

  • " root "是MySQL默认自带的管理员用户
  • 密码是安装MySQL时候设置的密码

2.建立连接

 public static void main(String[] args) throws SQLException {...//2.让代码和数据库服务器建立连接Connection connection = dataSource.getConnection();...}
  • 对于getConnection()方法,由于建立连接可能会失败,由此抛出异常,因此需要显式(受查异常)

3.构造SQL语句

 //3.对数据库进行操作(构造SQL语句)String sql = "insert into customer values(1,'z','','','','')";PreparedStatement statement = connection.prepareStatement(sql);
  • JDBC中的SQL不需要带上分号’ ; ', ’ ; ’ 只在命令行中用来区分不同的语句,在代码中直接操作中并不需要
  • 将String包装成一个" 语句对象 ",即将字符串风格的SQL转化成了JDBC里面的一个对象

4.执行SQL

 //4.执行SQLint ret = statement.executeUpdate();
  • SQL里如果是 insert,update,delete 都使用 executeUpdate() 方法
  • SQL里如果是 select 就使用 executeQuery() 方法
  • 返回值表示这个操作影响到了几行,相当于在控制台输入SQL后得到的数字

5.释放资源

//5.SQL执行完毕 释放资源statement.close();connection.close();
  • 当创建好相关的连接后,JVM就会从系统中申请到一些硬件资源,这些资源不用时需要及时释放
  • 释放资源时遵循先创建的后释放

完整的代码如下

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;public class TestJDBC {public static void main(String[] args) throws SQLException {//1.创建好数据源DataSource dataSource = new MysqlDataSource();//((MysqlDataSource) dataSource).setURL("jdbc:mysql://127.0.0.1:3306/databaseName?characterEncoding=utf8&useSSL=false");((MysqlDataSource) dataSource).setUser("root");((MysqlDataSource) dataSource).setPassword("123");//2.让代码和数据库服务器建立连接Connection connection = dataSource.getConnection();//3.对数据库进行操作(构造SQL语句)String sql = "insert into student values(1,'z')";PreparedStatement statement = connection.prepareStatement(sql);//4.执行SQLint ret = statement.executeUpdate();//5.SQL执行完毕 释放资源statement.close();connection.close();}
}

(二)数据库操作

1.增加、修改、删除

  • prepareStatement 除了能够用于描述一个SQL外,还能辅助进行动态拼接,其具体的语句如下
  • 通过setInt(1,id); 把第一个’ ? ’ 替换成用户输入的 id,把第二个’ ? ’ 替换成name
  • 增加、修改、删除操作模板相同,只需修改对应的SQL语句
public class TestJDBC {public static void main(String[] args) throws SQLException {Scanner scanner = new Scanner(System.in);//用户输入一个待插入的数据System.out.println("请输入学号:");int id = scanner.nextInt();System.out.println("请输入姓名:");String name = scanner.next();//1.创建好数据源//2.让代码和数据库服务器建立连接//3.对数据库进行操作(构造SQL语句)String sql = "insert into student values(?,?)";PreparedStatement statement = connection.prepareStatement(sql);//这里的下标从1开始排列statement.setInt(1,id);//把第一个?替换成idstatement.setString(2,name);//把第二个?替换成name//4.执行SQLint ret = statement.executeUpdate();//5.SQL执行完毕 释放资源}
}

2.查询数据

  • 对于查询数据来说,需要注意使用statement.executeQuery()方法,此时查找操作返回的不是一个int数据了,而是一个"临时表",使用ResultSet表示这个表
  • 对于ResultSet 接收的临时表,通过遍历结果集合,先获取到每一行,再获取到这一行中的若干列, next方法表示获取到一行记录,同时把光标往后移动,如果遍历到表的结束为止,此处的nex t就返回 false 了
  • 通过while循环搭配resultSet.next()来依次获取到表中的每一行,针对当前这一行来获取到其中的列,通过列的名字来对应数据库中的具体数据
  • 最后注意释放 ResultSet 的资源
public class TestJDBCSelect {public static void main(String[] args) throws SQLException {//1.创建好数据源//2.让代码和数据库服务器建立连接//3.对数据库进行操作(构造SQL语句)String sql = "select * from student ";PreparedStatement statement = connection.prepareStatement(sql);//4.执行SQLResultSet resultSet = statement.executeQuery();//遍历结果集合while(resultSet.next()){int id = resultSet.getInt("id");String name = resultSet.getString("name");System.out.println("id=" + id + ",name=" + name);}//5.SQL执行完毕 释放资源resultSet.close();statement.close();connection.close();}
}

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

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

相关文章

SpaceSync智能排班:重构未来办公空间的神经中枢

文心智能体平台可免费使用DeepSeek 满血版啦&#xff0c;使用DeepSeek模型创建并提交智能体&#xff0c;即有机会瓜分万元奖金&#xff01;有这等好事还不快冲&#xff01; 文心智能体官网&#xff1a;文心智能体平台AgentBuilder | 想象即现实 本片文章为作者参加文心智能体平…

Blender-MCP服务源码3-插件开发

Blender-MCP服务源码3-插件开发 Blender-MCP服务源码解读-如何进行Blender插件开发 1-核心知识点 1&#xff09;使用Blender开发框架学习如何进行Blender调试2&#xff09;学习目标1-移除所有的Blender业务-了解如何MCP到底做了什么&#xff1f;3&#xff09;学习目标2-模拟MC…

每日一题---dd爱框框(Java中输入数据过多)

dd爱框框 实例&#xff1a; 输入&#xff1a; 10 20 1 1 6 10 9 3 3 5 3 7 输出&#xff1a; 3 5 这道题要解决Java中输入的数过多时&#xff0c;时间不足的的问题。 应用这个输入模板即可解决&#xff1a; Java中输入大量数据 import java.util.*; import java.io.*;pu…

Qlik Sense New Install with Restore

Background In case you meet the upgrade issue like us , you can follow the below step to recover the existing data to new installed Qlik Sense . Powered by Moshow郑锴-CSDN博客 please follow below steps: pgsql dump backupbackup table into sql by DBeaverst…

大数据-spark3.5安装部署之standalone模式

真实工作中还是要将应用提交到集群中去执行&#xff0c;Standalone模式就是使用Spark自身节点运行的集群模式&#xff0c;体现了经典的master-slave模式。集群共三台机器&#xff0c;具体如下 u22server4spark&#xff1a; master worker u22server4spark2&#xff1a; worke…

Uniapp 开发 App 端上架用户隐私协议实现指南

文章目录 引言一、为什么需要用户隐私协议&#xff1f;二、Uniapp 中实现用户隐私协议的步骤2.1 编写隐私协议内容2.2 在 Uniapp 中集成隐私协议2.3 DCloud数据采集说明2.4 配置方式3.1 Apple App Store3.2 Google Play Store 四、常见问题与解决方案4.1 隐私协议内容不完整4.2…

【C++】 —— 笔试刷题day_5

刷题day_5 一、游游的you 题目链接&#xff1a;游游的you 题目解析 题目要求&#xff1a; 输入a&#xff0c;b&#xff0c;c表示y、o、u三个字母的个数&#xff1b; 将这些字母连成字符串&#xff0c;并且这里you三个字母相邻获得2分&#xff0c;两个o字母相邻获得1分。 让我…

78. Harmonyos NEXT 懒加载数据源实现解析:BasicDataSource与CommonLazyDataSourceModel详解

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; Harmonyos NEXT 懒加载数据源实现解析&#xff1a;BasicDataSource与CommonLazyDataSourceModel详解 文章目录 Harmonyos NEXT 懒加载数据源实现解…

如何打包数据库mysql数据,并上传到虚拟机上进行部署?

1.连接数据库&#xff0c;使得我们能看到数据库信息&#xff0c;才能进行打包上传 2. 3. 导出结果如下&#xff0c;是xml文件 4.可以查询每个xml文件的属性&#xff0c;确保有大小&#xff0c;这样才是真实导出 5跟着黑马&#xff0c;新建文件夹&#xff0c;并且把对应的东西放…

Springboot+mabatis增删改查,设置不可重复字段

今天又学会了一个操作&#xff0c;我们数据库中&#xff0c;可能要求一个字段名字不可以重复&#xff0c;我们就进行这样的操作&#xff01;设计表&#xff0c;然后点击索引&#xff0c;选择字段&#xff0c;加入索引类型和索引方法&#xff0c;然后ctrlS保存!即可 如果一旦还…

C# NX二次开发:矩形阵列和线性阵列等多种方法讲解

大家好&#xff0c;今天讲一些关于阵列相关的UFUN函数。 UF_MODL_create_linear_iset (view source)&#xff1a;这个函数为创建矩形阵列。 intmethodInputMethod: 0 General 1 Simple 2 Identicalchar *number_in_xInputNumber in XC direction.char *distance_xInputSpac…

嵌入式硬件: GPIO与二极管基础知识详解

1. 前言 在嵌入式系统和硬件开发中&#xff0c;GPIO&#xff08;通用输入输出&#xff09;是至关重要的控制方式&#xff0c;而二极管作为基础电子元件&#xff0c;广泛应用于信号整流、保护电路等。本文将从基础原理出发&#xff0c;深入解析GPIO的输入输出模式&#xff0c;包…

CTF--Web安全--SQL注入之报错注入

CTF–Web安全–SQL注入之报错注入 一、报错注入的概念 用户使用数据库查询语句&#xff0c;向数据库发送错误指令&#xff0c;数据库返回报错信息&#xff0c;报错信息中参杂着我们想要获取的隐私数据。通常在我们在页面显示中找不到回显位的时候&#xff0c;使用报错注入。 二…

matlab 模糊pid实现温度控制

1、内容简介 matlab162-模糊pid实现温度控制 可以交流、咨询、答疑 2、内容说明 略基于PID电加热炉温度控制系统设计 摘要 电加热炉随着科学技术的发展和工业生产水平的提高&#xff0c;已经在冶金、化工、 机械等各类工业控制中得到了广泛应用&#xff0c;并且在国民经济中占…

RabbitMq C++客户端的使用

1.RabbitMq介绍 RabbitMQ 是一款开源的消息队列中间件&#xff0c;基于 AMQP&#xff08;高级消息队列协议&#xff09;实现&#xff0c;支持多种编程语言和平台。以下是其核心特点和介绍&#xff1a; 核心特点 多语言支持 提供 Java、Python、C#、Go、JavaScript 等语言的客…

星越L_备胎更换/千斤顶使用讲解

目录 1.车辆停靠在坚实平坦的路面上。 2.打开危险警示灯、 3.设立三角指示牌 4.取出备胎及随车工具 5.使用螺栓扳手对每个螺母进行松动 6使用千斤顶抬升 7、其他 轮胎漏气或爆胎的情况,需要使用千斤顶更换备胎 1.车辆停靠在坚实平坦的路面上。 2.打开危险警示灯、

【Python 数据结构 15.哈希表】

目录 一、哈希表的基本概念 1.哈希表的概念 2.键值对的概念 3.哈希函数的概念 4.哈希冲突的概念 5.常用的哈希函数 Ⅰ、直接定址法 Ⅱ、平方取中法 Ⅲ、折叠法 Ⅳ、除留余数法 Ⅴ、位与法 6.哈希冲突的解决方案 Ⅰ、开放定址法 Ⅱ、链地址法 7.哈希表的初始化 8.哈希表的元素插…

软件测试之测试分类

1. 为什么要对软件测试进行分类 软件测试是软件⽣命周期中的⼀个重要环节&#xff0c;具有较⾼的复杂性&#xff0c;对于软件测试&#xff0c;可以从不同的⻆度 加以分类&#xff0c;使开发者在软件开发过程中的不同层次、不同阶段对测试⼯作进⾏更好的执⾏和管理测试 的分类⽅…

Devops CI/CD

Devops CI/CD DevOps 中的 CI/CD&#xff1a;持续集成与持续部署的深度解析一、CI/CD 基本概念&#xff08;一&#xff09;持续集成&#xff08;二&#xff09;持续部署 二、CI/CD 实施步骤&#xff08;一&#xff09;版本控制&#xff08;二&#xff09;自动化构建&#xff08…

leetcode105为什么可以root.left可以截取到前序遍历二叉树的(0,index),而不是(1,index+1)

这里以105前序和中序遍历构造二叉树为例&#xff0c;106同理 原因在于preoder.shift()会改变原数组&#xff0c;已经把preoder的第一个队头元素已经排除出去了&#xff01;&#xff01;&#xff01; 306题中的截取后续遍历中用pop&#xff08;&#xff09;同理