返回

Java开发知识点简单总结-数据库

MySQL

数据库三范式

第一范式 1NF:表中字段的数据,不可以再拆分

这张表因为姓名字段可再拆分所以不符合第一范式

ID姓名年龄
1销售部小张28

而这张表符合

ID部门姓名年龄
1销售部小张28

第二范式 2NF:在满足第一范式的情况下,遵循唯一性,消除部分依赖。
即:表中任意一个主键或任意一组联合主键,可以确定除该主键外的所有的非主键值
通俗来讲就是一个表只能描述一件事情

学号姓名年龄课程名称成绩学分
001小张28语文903
001小张28数学902

学号做主键,可确定姓名,但不能确定课程与成绩。所以不符合第二范式

需要拆分为学生表、课程表、成绩表,才符合第二范式

第三范式 3NF:在满足第二范式的情况下,消除传递依赖。 即:在任一主键都可以确定所有非主键字段值的情况下,不能存在某非主键字段 A 可以获取 某非主键字段 B

学号姓名班级班主任
001小黄一年级(1)班高老师

学号作为主键,可确定姓名、班级、班主任,但通过班级也可确定班主任。所以不符合第三范式

需要拆分为学生表、班级表,才符合第三范式

什么是事务

事务是逻辑上的一组操作,要么都执行,要么都不执行

什么是索引

数据库索引是一种用于提高数据库查询性能的数据结构。索引由一个或多个列组成,并按照特定的排序规则进行存储。

索引的优缺点

优点

  • 大大加快数据检索的速度。
  • 将随机I/O变成顺序I/O(因为B+树的叶子节点是连接在一起的)
  • 加速表与表之间的连接

缺点

  • 建立索引需要占用物理空间。
  • 当对表进行插入、更新和删除时,索引也需要进行相应的更新。会增加写操作的代价,特别是对于频繁进行数据修改的表。
  • 创建和维护索引都需要花费精力时间。

索引的数据结构

索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎默认的索引类型为B+树索引。

Hash表和B+树的区别

Hash索引一般用于精确的等值查找,大多数情况下使用B+索引

  • 哈希索引不支持排序,因为哈希表是无序的。
  • 哈希索引不支持范围查找。
  • 哈希索引不支持模糊查询及多列索引的最左前缀匹配。
  • 因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的。
  • B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点

为什么使用B+树而不是B树

  • B树适用于随机检索,而B+树适用于随机检索和顺序检索
  • B+树的空间利用率更高,因为B树每个节点要存储键和值,而B+树的内部节点只存储键,这样B+树的一个节点就可以存储更多的索引,从而使树的高度变低,减少了I/O次数,使得数据检索速度更快。
  • B+树的叶子节点都是连接在一起的,所以范围查找,顺序查找更加方便
  • B+树的性能更加稳定,因为在B+树中,每次查询都是从根节点到叶子节点,而在B树中,要查询的值可能不在叶子节点,在内部节点就已经找到。

索引的类型有哪些

  • FULLTEXT: 全文索引,MyISAM存储引擎和InnoDB存储引擎在MySQL5.6.4以上版本支持全文索引,一般用于查找文本中的关键字,而不是直接比较是否相等,主要是用来解决WHERE name LIKE “%zhang%“等针对文本的模糊查询效率低的问题。- HASH:哈希索引,哈希索引多用于等值查询,时间复杂夫为o(1),效率非常高,但不支持排序、范围查询及模糊查询等。- BTREE:B+树索引,INnoDB存储引擎默认的索引,支持排序、分组、范围查询、模糊查询等,并且性能稳定。
  • RTREE:空间数据索引,多用于地理数据的存储,相比于其他索引,空间数据索引的优势在于范围查找

索引的种类有哪些

  • 主键索引:数据列不允许重复,不能为NULL,一个表只能有一个主键索引
  • 组合索引:由多个列值组成的索引。
  • 唯一索引:数据列不允许重复,可以为NULL,索引列的值必须唯一的,如果是组合索引,则列值的组合必须唯一。
  • 全文索引:对文本的内容进行搜索。
  • 普通索引:基本的索引类型,可以为NULL

什么是聚簇索引,什么是非聚簇索引

  • 聚簇索引:将数据和索引放到一起存储,索引结构的叶子节点保留了数据行。
  • 非聚簇索引:将数据进和索引分开存储,索引叶子节点存储的是指向数据行的地址。

索引的使用场景有哪些

  • 对于中大型表建立索引非常有效,对于非常小的表,一般全部表扫描速度更快些。
  • 对于超大型的表,建立和维护索引的代价也会变高,这时可以考虑分区技术。
  • 如何表的增删改非常多,而查询需求非常少的话,那就没有必要建立索引了,因为维护索引也是需要代价的。
  • 一般不会出现再where条件中的字段就没有必要建立索引了。
  • 多个字段经常被查询的话可以考虑联合索引。
  • 字段多且字段值没有重复的时候考虑唯一索引。
  • 字段多且有重复的时候考虑普通索引。

索引的设计原则

  • 最适合索引的列是在where后面出现的列或者连接句子中指定的列,而不是出现在SELECT关键字后面的选择列表中的列。
  • 索引列的基数越大,索引的效果越好,换句话说就是索引列的区分度越高,索引的效果越好。比如使用性别这种区分度很低的列作为索引,效果就会很差,因为列的基数最多也就是三种,大多不是男性就是女性。
  • 尽量使用短索引,对于较长的字符串进行索引时应该指定一个较短的前缀长度,因为较小的索引涉及到的磁盘I/O较少,并且索引高速缓存中的块可以容纳更多的键值,会使得查询速度更快。
  • 尽量利用最左前缀。
  • 不要过度索引,每个索引都需要额外的物理空间,维护也需要花费时间,所以索引不是越多越好。

什么是最左匹配原则

最左匹配原则:从最左边为起点开始连续匹配,遇到范围查询(<、>、between、like)会停止匹配。

索引在什么情况下会失效

  • 条件中有or,例如select * from table_name where a = 1 or b = 3
  • 在索引上进行计算会导致索引失效,例如select * from table_name where a + 1 = 2
  • 在索引的类型上进行数据类型的隐形转换,会导致索引失效,例如字符串一定要加引号,假设 select * from table_name where a = '1'会使用到索引,如果写成select * from table_name where a = 1则会导致索引失效。
  • 在索引中使用函数会导致索引失效,例如select * from table_name where abs(a) = 1
  • 在使用like查询时以%开头会导致索引失效
  • 索引上使用!、=、<>进行判断时会导致索引失效,例如select * from table_name where a != 1
  • 索引字段上使用 is null/is not null判断时会导致索引失效,例如select * from table_name where a is null

SQL优化

  • 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by涉及的列上建立索引。
  • 避免在 where 子句中使用 !=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
  • 避免在 where 子句中对字段进行 null 值 判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
  • 避免在 where 子句中对字段进行表达式操作,否则将导致全表扫描,如where num/2=10应该为where num=10*2
  • 避免在where子句中对字段进行函数操作,否则将导致全表扫描
  • 在使用索引字段作为条件时,如果该索引是"联合索引”,那么必须使用到该索引中的第一个字段作为条件才能保证系统使用该索引,否则该索引将不会被使用。并且应尽可能的让字段顺序与索引顺序相一致
  • 任何地方都不要使用 select * from t ,用具体的字段列表代替”*"

行锁、表锁是什么

  • 表级锁:是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
  • 行级锁:是针对索引字段加的锁,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

行锁的注意事项

InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁,所以当使用行锁但where条件中没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁

共享锁和排他锁

表锁与行锁,都存在共享锁(ShareLock/读锁/S锁)和排他锁(ExclusiveLock/写锁/独占锁/X锁):

  • 共享锁(S锁):事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁(X锁):事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。

排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。

S 锁X 锁
S 锁不冲突冲突
X 锁冲突冲突

脏读、幻读、不可重复读是什么

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
  • 不可重复读(Unrepeatable read): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。

SQL 标准定义了哪些事务隔离级别?

  • READ-UNCOMMITTED(读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读) : (默认级别)对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
隔离级别脏读不可重复读幻读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××

什么是E-R图

E-R图 也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。

为什么不推荐使用级联与外键

  1. 数据库性能问题:使用级联和外键可能会对数据库的性能产生负面影响。当执行插入、更新或删除操作时,级联操作会引发额外的查询和操作,可能导致性能下降,特别是在处理大型数据集时。
  2. 复杂性和可维护性:使用级联和外键可能增加数据库的复杂性,使其更难以理解和维护。级联操作可以在数据库中引入复杂的依赖关系,导致难以预测和处理的情况。此外,如果数据库结构需要进行更改,可能需要修改和管理多个级联关系,增加了开发和维护的困难。
  3. 数据完整性问题:虽然外键可以确保引用完整性,即确保关系中的引用值存在于关联表中,但它们并不能解决所有数据完整性问题。外键无法处理复杂的业务规则和约束,这可能需要在应用程序级别进行处理。
  4. 级联效应的潜在问题:级联操作可能导致意外的结果,特别是在删除操作时。如果不小心使用级联删除操作,可能会删除不应该被删除的数据。这可能会导致数据丢失或破坏数据的完整性。

数据库设计通常分为哪几个阶段

  • 系统需求分析阶段;
  • 概念结构设计阶段;
  • 逻辑结构设计阶段;
  • 物理结构设计阶段;
  • 数据库实施阶段;
  • 数据库运行与维护阶段;

Mybatis

什么是ORM

ORM即对象关系映射,是一种数据持久化技术。它在对象模型和关系型数据库直接建立起对应关系,并且提供一种机制,通过JavaBean对象去操作数据库表的数据。

什么是数据持久化

数据持久化是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称。例如,文件的存储、数据的读取等都是数据持久化操作。数据模型可以是任何数据结构或对象的模型、XML、二进制流等。

MyBatis与Hibernate有哪些不同

Mybatis直接编写原生sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。

Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用hibernate开发可以节省很多代码,提高效率。

mybatis分页插件的实现 如何写一个mybatis分页插件 mybatis的原理 mybatis如何代理sql语句 mybatis的xml标签

#{}和${}的区别是什么

  1. #{} 是预编译处理,${}是字符串替换。
  2. Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
  3. Mybatis在处理${}时,就是把${}替换成变量的值。
  4. 使用#{}可以有效的防止SQL注入,提高系统安全性。

Mybatis的一级、二级缓存

  • 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
  • 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;
  • 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

Dao 接口的工作原理是什么

通常一个 xml 映射文件,都会写一个 Dao 接口与之对应。Dao 接口就是人们常说的 Mapper 接口;接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 MappedStatement 的 id 值;接口方法内的参数,就是传递给 sql 的参数。 Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MappedStatement ,举例: com.mybatis3.mappers. StudentDao.findStudentById ,可以唯一找到 namespace 为 com.mybatis3.mappers. StudentDao 下面 id = findStudentByIdMappedStatement 。在 MyBatis 中,每一个 <select><insert><update><delete> 标签,都会被解析为一个 MappedStatement 对象。

Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。

Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行 MappedStatement 所代表的 sql,然后将 sql 执行结果返回。

Mybatis是如何进行分页的?分页插件的原理是什么?

Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用 <resultMap> 标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME

Mybatis动态sql有什么用?执行原理?有哪些动态sql?

MyBatis 动态 sql 可以让我们在 xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

MyBatis 提供了 9 种动态 sql 标签: if, where, trim, set, choose, when, otherwise, foreach, bind

MyBatis 的 xml 映射文件中,不同的 xml 映射文件,id 是否可以重复

不同的 xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复。

MyBatis实现一对一有几种方式?具体怎么操作的?

  • 有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的 外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

MyBatis实现一对多有几种方式,怎么操作的?

  • 有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

  • Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。 它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。

MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么?

  • SimpleExecutor 每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
  • ReuseExecutor 执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement>内,供下一次使用。简言之,就是重复使用 Statement 对象。
  • BatchExecutor 执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同。

MyBatis 中如何指定使用哪一种 Executor 执行器?

在 MyBatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。

MyBatis 是否可以映射 Enum 枚举类?

可以。通过自定义一个 TypeHandler,实现 TypeHandlersetParameter()getResult() 接口方法。即可映射大部分类型

Redis

缓存模式

  • Cache-Aside

    • 读的时候,先读缓存,缓存命中的话,直接返回数据
    • 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应
    • 更新的时候,先更新数据库,然后再删除缓存
  • Read/Write Through

    • 从缓存读取数据,读到直接返回
    • 如果读取不到的话,从数据库加载,写入缓存后,再返回响应。
    • 先更新数据源,再更新缓存
  • Write behind

    • 与Read/Write Through类似,但是不直接更新数据源,只更新缓存,之后通过异步批量写入数据源
Licensed under CC BY-NC-SA 4.0