目录
JDBC
- JDBC规范 定义了数据库交互接口:DriverManager、Connection、Statement、ResultSet,后来又加了连接池DataSource–Pool
- Statement会话建立在Connection链接之上
- JDBC 是 Java 里操作数据库的核心,Java 操作数据库的各种类库,都可以看做是在 JDBC 接口上做的增强实现
上图所示,从JDBC接口开始一层层往上增强实现
数据库连接池
- 常见的数据库连接池:C3P0→DBCP–Apache CommonPool→Druid→Hikari(流行时间排序)
- Druid的功能特点:可以对SQL性能进行监控,但是MySQL自身有慢日志可以查看
- Hikari是现在默认的数据库连接池
ORM框架
- ORM(Object-Relational Mapping) 表示对象关系映射
- 常见的两种ORM框架:Hibernate(自动化)、MyBatis(半自动化)
Hibernate
- Hibernate 是一个开源的对象关系映射框架,它对JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,可以针对不同数据库的”方言”自动生成适宜 SQL 语句,自动执行,使得Java 程序员可以使用面向对象的思维来操纵数据库
Markdown将文本转换为 HTML。
- Hibernate 里需要定义实体类和 hbm 映射关系文件(Hibernate Bean Maping ,IDE中一般有工具生成)
- Hibernate 里可以使用 HQL、Criteria、Native SQL三种方式操作数据库,也可以作为 JPA 规范的适配实现,使用 JPA 接口操作(主要的使用方式)
MyBatis
- MyBatis 是支持定制化 SQL、存储过程以及高级映射
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO映射成数据库中的记录
- MyBatis-半自动化 ORM原因:需要使用映射文件 mapper.xml 定义 map 规则和 SQL、需要定义mapper/DAO接口,基于 xml 规则,操作数据库
- MyBatis使用经验:可以使用工具生成基础的 mapper.xml 和 mapper/DAO接口,可以让自定义的mapper接口继承工具生成生成的 mapper接口,而不是覆盖掉,也可以直接在 mapper/DAO 接口上用注解方式配置 SQL
MyBatis 与 Hibernate 比较
- Mybatis 优点:原生SQL(XML 语法),直观,对 DBA 友好
- Hibernate 优点:简单场景不用写 SQL(HQL、Cretiria、SQL)
- Mybatis 缺点:繁琐,可以用 MyBatis-generator、MyBatis-Plus 之类的插件
- Hibernate 缺点:对DBA 不友好,SQL都是针对不同数据库”方言”自动生成的、字段名字冗长,十分难以阅读
JPA
-
JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成(类似JDBC,但是需要底层的ORM框架去增强实现具体的数据库持久化操作)
-
核心类 EntityManager–实体管理器,提供实体类的DML、DQL方法对应于不同数据库表的DML、DQL操作
-
JPA 通过 JDK 5.0 及以上提供的注解描述对象-关系表映射关系,并将运行期的实体对象持久化到数据库中
-
JDBC与JPA的区别
1.操作的对象不一样,且使用的技术也不相同,比如JDBC通过封装JDBC接口进而通过数据库连接池等技术操作DataSource进行持久化;JPA通过增强实现JPA的ORM,操作EntityManager进行持久化
2.项目中引入的jar包不同,前者是Spring JDBC 后者是 Spring ORM
性能优化与数据库
性能优化关注
1.吞吐与延迟 :指导我们关注什么
2.没有量化就没有改进:监控与度量指标,指导我们怎么去入手
3.80/20原则:先优化性能瓶颈问题,关键的少数问题导致了大部分的性能问题,指导我们如何去优化
4.过早的优化是万恶之源:指导我们要选择优化的时机
5.脱离场景谈性能都是耍流氓:指导我们对性能要求要符合实际
性能优化与数据库的关系
- 大部分的性能问题都出现在三个方面:
1.SQL问题
2.数据库表结构问题
3.数据库自身性能问题 - 任何系统都无法绕过这三方面的原因
1.业务处理系统本身无状态,所有数据状态最终要保存到数据库
2.一般来说,DB/SQL 操作的消耗在 业务系统 的一次请求处理中占比最大,随着时间的发展,系统对数据库数据库问题三个方面的要求也会变化
Markdown将文本转换为 HTML。
关系型数据库
- 什么是关系型数据库:使用关系模型,以关系代数理论为数学基础,处理和描述数据
- 关系型数据库可以通过E-R(Entity-Relationship)图表达表与表、表与字段之间的关系
关系型数据库的设计范式(NF:Normal Form)
1.第一范式(1NF):表里面的每一个字段都不可以继续拆分成多个字段
2.第二范式(2NF):表里面的非主键字段不能不依赖于主键字段或者是只依赖于联合主键的部分字段
3.第三范式(3NF):消除传递依赖,非主键字段除了依赖主键,不能再依赖于非主键字段
4.BC 范式(BCNF):主要针对联合主键,联合主键可能导致数据插入不成功,因为关键业务数据缺少联合主键中的部分字段,这说明该表对于改业务而言可以继续拆分为多张表,减少联合主键包含的字段
5.第四范式(4NF):消除非平凡的多值依赖&第五范式(5NF):消除一些不合适的连接依赖
常见的关系型数据库
1.开源:MySQL、PostgreSQL
2.商业:Oracle,DB2,SQL Server
3.内存数据库:Redis(严格算不上数据库因为会造成数据丢失),VoltDB
4.图数据库:Neo4j,Nebula
5.时序数据库:InfluxDB、openTSDB
6.其他关系数据库:Access、Sqlite、H2、Derby、Sybase、Infomix 等
7.NewSQL/分布式数据库:TiDB、CockroachDB、NuoDB、OpenGauss、OB、TDSQL
非关系型数据库
NoSQL 数据库:MongoDB、Hbase、Cassandra、CouchDB
SQL(Structure Qquery Language)
- SQL已经成为操作关系/非关系型数据库的实施标准的语言
- 结构化查询语言包含6各部分
- 不同数据库的SQL语言仅有些许差别
数据查询语言(DQL: Data Query Language)
- 也称为“数据检索语句”,用以从表中获得数据,确定数据怎样在应用程序给出
- 保留字 SELECT 是 DQL(也是所有 SQL)用得最多的动词,其他 DQL 常用的保留字有 WHERE,ORDER BY,GROUP BY 和 HAVING,这些 DQL 保留字常与其它类型的 SQL 语句一起使用
数据操作语言(DML:Data Manipulation Language)
- 其语句包括动词 INSERT、UPDATE 和 DELETE。它们分别用于添加、修改和删除
事务控制语言(TCL)
- 它的语句能确保被 DML 语句影响的表的所有行及时得以更新,包括COMMIT(提交)命令、SAVEPOINT(保存点)命令、ROLLBACK(回滚)命令
数据控制语言(DCL)
- 它的语句通过 GRANT 或 REVOKE 实现权限控制,确定单个用户和用户组对数据库对象的访问,某些 RDBMS 可用 GRANT 或 REVOKE 控制对表单个列的访问
数据定义语言(DDL)
- 其语句包括动词 CREATE,ALTER 和 DROP。在数据库中创建新表或修改、删除表(CREAT TABLE 或 DROP TABLE)、为表加入索引等
指针控制语言(CCL)
- 操作游标的语句,像 DECLARE CURSOR、FETCH INTO 和 UPDATE WHERE CURRENT 用于对一个或多个表单独行的操作
MySQL
- MySQL数据库现在有两个版本:MariaDB 和 MySQL
- MySQL数据库发展的版本
- 使用量最大的三个版本:5.5.xxx、5.6.xxx、5.7.xxx,三者比较相近,无大改动,改动最大的是5.7.xxx升级为8.0.xxx
MySQL数据库原理
-
MySQL 架构:一个MySQL Server对客户端来说就是一整个数据库
-
MySQL的连接池(Connection Pool)是BIO的所以需要避免长连接
MySQL 存储
- MySQL 存储分为独占模式和共享模式
- MySQL 存储默认为独占模式,控制两种模式的切换通过设置
innodb_file_per_table=OFF/ON
独占模式
- 每一个数据库都有各自的如下等相关文件,按照每一个不同schema(等同于datebase)区分独立存放
1.日志组文件:ib_logfile0 和 ib_logfile1(innodb log=redo log),默认均为5M
2.表结构文件:.frm
3.表空间文件:.ibd
4.字符集和排序规则文件:db.opt
5.bin log 二进制日志文件:记录主数据库服务器的 DDL 和 DML 操作
6.二进制日志索引文件:master-bin.index
共享模式
数据库中的所有schema的相关文件都在 ibdata1中
MySQL执行流程
- 解析树:解析抽象语法树(AST)
MySQL常用引擎对比
- Memory引擎使用的是内存,其大小限制取决于内存的大小,archive引擎使用硬盘,理论上没有大小限制
- 当使用select count(*)查询表中记录数时,Myisam可以直接返回,因为使用该引擎的的表会统计并存储表中记录数,innodb需要遍历主键索引树
MySQL对SQL的执行顺序
1.from、on、join:按照次序主要是为了找到涉及到的Table
2.where、group by、having+聚合函数:按照次序主要是为了对前面找到的所有表的记录进行过滤Filter
3.select、order by、limit:按照次序主要是为了将满足过滤条件的记录在客户端展现Show
MySQL索引
- 数据是按页来分块的,一个数据块就是一个数据页,当一个数据被用到时,其附近的数据也通常会马上被使用,InnoDB 使用 B+ 树实现聚集索引
聚集索引(聚簇索引)与非聚簇索引
- 对于MySQL而言聚集索引(聚簇索引)也是主键索引,如上图B+树就是聚簇索引,每一个节点就一个数据块,叶子结点存储了多个主键索引所在行的全部记录信息,并且叶子结点之间有的关系可以看成是双向节点,这样可以方便的存取数据
- 非聚簇索引=辅助索引=二级索引=非主键索引=联合索引
- 主键索引的更新
替换更新 - 非主键索引的更新
删除+新建
唯一索引与非唯一索引
- 唯一索引包括:主键索引、唯一约束的字段(自动建立唯一索引)、唯一的联合索引(二级索引)
- 非唯一索引包括:除了以上情况的二级索引
单标数据上限=2000万左右
- 为什么一般单表数据不超过2000万
索引树一般不会超过三层,因为每次查询数据都会从B+树的根节点开始向下搜寻直至叶子节点,每向下一层(换一个数据页)就需要读一次磁盘,磁盘I/O对性能影响大 - 计算方法
1.MySQL一个数据页一般是16kB,指针一般是6B
2.假设主键索引是bigint数据类型占用8B,一条记录是1KB
3.一个数据页能放下16×1024÷(6+8)≈1170个主键指针
4.三层的B+树能存储的数据为1170²×16≈2100万
MySQL常用的SQL
1.show schemas
--显示所有数据库
2.create schema 'my_Schemas'
--创建名为my_Schemas
的数据库
3.use 'my_Schemas'
–使用/切换当前数据库为'my_Schemas'
4.show variables like '%dir%'
--查看数据库所有的目录,重点关注datadir
这一项,指向了MySQL独占存储模式下各个数据库的文件存储目录
5.show variables like '%port%'
--查看数据库使用的所有端口
6.show variables like '%version%'
–查看数据库相关所有版本号
7.create table test_1(
idint not null,primary key(
id))engine=InnoDB default charset=utf8mb4
--在当前数据库创建表名为test_1
的建表语句
8.show tables
--查看当前数据库所有的表
9.show create table 'test_1'
--查看名为test_1
的表的建表语句
10.show columns from 'test_1'
--查看表名为test_1
的所有列;
MySQL常见客户端/操作工具
mysql-cli 或 IDE(DataGrip,MySQL-WorkBench,MySQL-Front,Navicat 等)
MySQL配置优化
- MySQL配置优化主要针对MySQL的配置文件中配置参数的修改
- MySQL配置文件一般在MySQL的安装目录下,对于Windows系统,文件名是 my.ini;对于非Windows系统而言,文件名是my.cnf
- MySQL配置文件包含主要的两项配置,分别是[mysqld] 与 [mysql],一般针对数据库的配置优化主要是修改配置项[mysqld] 下面的配置参数
- [mysqld]配置参数项下面的参数主要针对于整个SQL server
- [mysql]配置参数项下面的参数主要正对每个SQL client
- 查看配置参数
1.直接打开配置文件文件查看
2.不知道具体配置参数的变量名时可以通过show variables like '%xxx%'
--模糊查询配置变量名包含xxx
的所有配置参数
3.知道参数名的情况下可以直接通过show variables like ‘xxx’
orselect @@xxx
--查看配置变量名为xxx
配置参数 - 配置参数分为全局配置参数 与 当前会话配置参数,当前会话配置参数一般与全局配置参数的变量名与值相同;有些配置参数只有全局配置参数,这些配置参数的变量名一般都以
global
开头或包含global
单词 - 查看全局配置参数可以通过
show global variables like '%xxx%'
orshow glob variables like 'xxx'
orselect @@xxx
- 修改配置参数可以通过
set xxx=aaa
orset global.xxx=aaa
,有些参数是只读的不能修改 - 对于全局配置参数的修改,在MySQL8.0之前是不会会持久化的,MySQL重启以后就需要重新设置,这个问题在8.0及以后的版本得到了修复
- 配置参数优化一般有三个方面
1.连接请求的配置变量
2.缓冲区配置变量
3.配置 Innodb 的几个配置变量 - 连接请求的配置变量包括
1.max_connections
最大连接数
2.back_log
半连接的最大连接数
3.wait_timeout
等待超时的时间 和interative_timeout
交互状态下的超时时间,对于超时的连接会被断开,早期的连接池不会自动重连和test连接,现在的连接池基本都实现了重连和test连接 - 缓冲区配置变量包括
1.key_buffer_size
这个参数只针对MyISAM存储引擎,用于设置存储索引的缓存区大小决定了索引操作的速度
2.query_cache_size
(查询缓存简称 QC)这个参数只针对server端的缓存而非存储引擎端的,MySQL8.0及以后的版本已经去掉了
3.max_connect_errors
设置连接的最大错误数
4.sort_buffer_size
设置排序缓存区大小,默认只有1M
5.max_allowed_packet=32M
设置允许向MySQL发送的数据的最大的大小
6.join_buffer_size=2M
设置连接表时用到的连接缓存区大小
7.thread_cache_size=300
设置线程的缓存大小 - 配置 Innodb 的几个配置变量包括
1.innodb_buffer_pool_size=128M
设置存储 使用索引的查询 的缓存区大小,存储内容同时包括索引和对应的记录
2.innodb_flush_log_at_trx_commit
设置提交事务的时候将 redo 日志写入磁盘中的策略3.innodb_thread_concurrency=0
设置并发线程数
4.innodb_log_buffer_size
设置日志的缓存区大小
5.innodb_log_file_size=50M
设置日志文件的大小
6.innodb_log_files_in_group=3
设置日志组中的日志文件数目,推荐是3
7.read_buffer_size=1M
设置读取数据的时候缓存区大小
8.read_rnd_buffer_size=16M
设置随机读数据的时候缓存区大小
9.bulk_insert_buffer_size=64M
设置批量插入的缓存区大小
10.binary log
设置bin log相关的参数,有很多
MySQL的表设计优化
- 选择恰当引擎
1.不需要事务且数据的操作量比较大使用MyISAM
2.需要事务等可以使用默认的Innodb
3.只需要在内存临时建个表且要求速度快、数据量小、不需要持久化使用Memory
4.当数据都需要归档的使用Archive或tuko(压缩效率高) - 库表命名
不建议使用拼音或缩写 - 合理拆分宽表
- 选择恰当数据类型:明确、尽量小
1.char、varchar 的选择,char是固定为设置长度的,varchar是可变长度的,根据实际的存储的数据长度在0到给定值之间变化,推荐使用varchar
2.对于比较大的文本数据其实不建议使用text/blob/clob,因为这会导致数据库性能下降,数据块能存储的数据变少了;实在不行也可以先将表的其他字段存储进去,最后通过update的方式将text/blob/clob类型字段的数据更新到对应记录的对应字段
3.不建议直接将文件、图片存入到数据库,可以存储在服务器的磁盘中或分布式文件系统中,然后数据库存储路径或远程访问的URL
4.时间日期的存储问题
5.数值的精度问题,需要注意的是int(3)与int(8)没有任何区别,仅仅只是展示的不同
6.字段的数据类型长度可以适当冗余,但是过多冗余会造成成本上升 - 唯一约束和索引的关系
唯一约束也是一种索引=唯一索引 - 冗余字段
可以适当将子表中经常用到的字段添加到主表中作为冗余字段 - 不建议使用游标、变量、视图、自定义函数、存储过程,因为不同数据库这些内容不相同
- 自增主键的使用问题
数据不大的情况下推荐使用自增主键,当数据量很大考虑分布式数据库的场景时比如需要分库、分表就不适用了 - 不能在线修改表结构(DDL 操作)会造成锁表
DDL 操作的危害:
1.导致索引重建
2.导致锁表
3.抢占数据库服务器资源
4.造成主从延时 - 逻辑删除还是物理删除,推荐逻辑删除
- 推荐加 create_time,update_time 时间戳
- 数据库碎片问题,优化该问题时也会造成锁表
- 不建议使用外键、触发器
MySQL快速导入导出、备份数据
- 对于导出、备份数据
一般情况下会造成锁表,如果有主从结构的数据库集群,可以直接通过从库实现,或者通过bin log实现 - 对于导入
1.只有通过不断地insert,可以通过Java代码、批量insert、脚本等方式
2.也可以通过load date 方式把一个文本的所有数据装载到MySQL中
3.最后可以考虑使用数据的迁移工具实现快速导入导出、备份数据
MySQL事务与锁
- MySQL事务可靠性模型ACID
1.Atomicity: 原子性,一次事务中的操作要么全部成功,要么全部失败
2.Consistency: 一致性,跨表、跨行、跨事务,数据库始终保持一致状态
3.Isolation: 隔离性,可见性,保护事务不会互相干扰,事务隔离是数据库的基础特征,包含4种隔离级别
4.Durability: 持久性,事务提交成功后,不会丢数据。如电源故障,系统崩溃 - 为了维持性能同时又兼顾可靠性,Innodb存储引擎常用的策略:双写缓冲区、故障恢复、操作系统、fsync() 、磁盘存储、缓存、UPS、网络、备份策略 ……
- MySQL的锁
1.表级锁
2.行级锁
3.共享锁(S)、排它锁(X)
所有的锁都是针对索引字段
表级锁
1.意向锁,上锁前会先上意向锁
2.其他,如 自增锁(AUTO-IN)、LOCK TABLES
- 意向锁,表明事务稍后要进行哪种类型的锁定,上锁前都要先加意向锁
1.共享意向锁(IS): 打算在某些行上设置共享锁(S)前会在表上加IS
2.排他意向锁(IX): 打算对某些行设置排他锁(X)前会在表上加IX
3.Insert 意向锁: Insert 操作设置的间隙锁
- 自增锁(AUTO-IN)
同时对使用自增主键的表插入多条记录时,为了保证自增主键不冲突,此时对该表加上自增锁 - LOCK TABLES 表锁
一般发生在dump、DDL时
行级锁
1.记录锁(Record): 锁定索引对应记录
2.间隙锁(Gap): 锁住一个范围
3.临键锁(Next-Key): 记录锁+间隙锁的组合
4.谓词锁(Predicat): 空间索引
- 间隙锁:锁住一个区间的索引,该区间是开区间,条件是隔离级别为可重复读
- 临键锁:锁住一个区间的索引,该区间是前开后闭区间,条件是隔离级别为可重复读
- 对于查询涉及到主键索引、唯一索引,即走到了对应的索引树
1等值查询且无记录,在包含索引值的区间加间隙锁,该区间内不能新增记录,只能修改不涉及到的索引和非索引字段
2.等值查询且有记录,包含索引值的下区间,会从临键锁退化为记录锁
3.范围查询,对于范围内命中的记录,则包含该记录的下区间临键锁变为记录锁,如果查询范围有边界则取包含这些边界值的区间的交集作为间隙锁,如果查询范围没有边界,则以包含这些边界值的区间并集作为临键锁,且涉及到记录不能DML - 对于查询涉及到非唯一索引,即走到了对应的索引树
1.等值查询且无记录,在包含索引值的区间加间隙锁,该区间内不能新增记录,只能修改不涉及到的索引和非索引字段
2.等值查询且有记录,在该值前后形成两个区间,下区间是临键锁,上区间是间隙锁,区间内的记录只能修改,不能在区间内新增记录
3.范围查询,不管有无命中记录,如果查询范围有边界则取包含这些边界值的区间的交集作为间隙锁,如果查询范围没有边界,则以包含这些边界值的区间并集作为临键锁,且涉及到记录不能DML
查看事务和锁的SQL
- 查看当前数据库的锁和事务:
SHOW ENGINE INNODB STATUS \G;
- 查看当前数据库锁的情况:
select * from performance_schema.data_locks;
死锁
- 造成死锁的原因
1.阻塞与互相等待
2.增删改与锁定读 - 解决或避免死锁的方法
- 死锁检测与自动回滚
当检测到死锁时,就kill掉其中一个事务;设置事务最长等待时长,超过这个时长自动回滚 - 锁粒度与程序设计
注意每一句SQL所造成的的锁的范围,减小锁的范围(锁粒度);程序设计时避免造成相互依赖
- 死锁检测与自动回滚
事务的隔离级别
- 事务的隔离级别
1.读未提交: READ UNCOMMITTED
2.读已提交: READ COMMITTED
3.可重复读: REPEATABLE READ
4.可串行化: SERIALIZABLE - MySQL的默认隔离级别是可重复读(历史原因):可以设置全局的默认隔离级别,也可以单独设置会话的隔离级别;其他数据库的默认隔离级别是读已提交
- 读未提交
很少使用,以非锁定方式执行,不能保证一致性会产生脏读、幻读、不可重复读 - 读已提交
1.每次查询都会设置和读取自己的新快照
2.仅支持基于行的 bin log
3.UPDATE 优化: 半一致性读(semi-consistent read)
4.会产生:不可重复读、幻读(Phantom)
5.只有记录锁