说说三大范式个人怎么理解的?

第一范式:每列不可再分

第二范式:在第一范式基础上,非主键列全部依赖于主键(可以接受冗余)

第三范式:在第二范式基础上,不依赖其他非主键,也就是消除冗余。比如用户表一个人的id作为主键,部门名称就违反了3NF,因为部门名称依赖于部门id,不完全依赖于id

一般满足3NF就可以了,根据业务的不同来适当进行字段冗余,减少表之间的连接,减少IO,提高查询效率

innodb的聚集索引与非聚集索引

聚集索引:表记录顺序与索引记录顺序一致。找到了索引就相当于找到了数据。所以一般来说主键就是
非聚集索引:不一致,找到索引没找到数据,根据索引上的指在回表查询

非聚集举例:select no,name from student where no = ‘test’。查到no的主键,再根据主键查一次

区别

  • 聚集索引只能有一个,非可以有多个
  • 聚集物理连续,非逻辑连续
  • 聚集在叶子存的为表中数据,非存的主键和索引列
  • 聚集插入慢,因为要排序,查询快,一步到位

b树与b+的区别

  • b树每个节点都存数据,b+只有叶子节点
  • b+降低了b的高度,减少查询的IO时间
  • b树和b+树都是平衡树,但是b树要求每个索引后面直接跟着数据,b+树则是非叶子结点会冗余到下一层,直到叶子结点层再追加数据。mysql的innodb页默认大小为16kb,如果不跟数据只存索引,一个节点可以多存更多的索引,最后达到更多索引全放内存里,加快速度
  • InnoDB存储引擎的最小存储单元是页,页可以用于存放数据也可以用于存放键值+指针,在B+树中叶子节点存放数据,非叶子节点存放键值+指针。
  • 索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而在去数据页中查找到需要的数据

为什么用b+而不是其他树比如b

b不管在叶子还是非叶子都会保存数据,导致在非叶子的指针变少,而在数据不变的情况下,只能去增加树的高度,导致IO操作变多,性能变低

innodb与myisam的区别

  • m支持表锁,i另外还支持行锁
  • m不支持事务,i支持
  • m不支持外键,i支持
  • m不支持崩溃恢复,i支持(依赖于redo log(重做日志))这也是事务持久性的体现。(undo日志保证原子性)

innodb引擎的四大特性

  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应hash(ahi)
  • 预读(read ahead)

mysql事务隔离级别(与spring一致)

由低到高

  • READ-UNCOMMITTED(读取未提交):导致脏读、不可复读、幻读
  • READ-COMMITTED(读取已提交):可阻止脏读,导致不可复读、幻读
  • REPEATABLE-READ(可重复读):对同一个字段怎么读都是一致的。可阻止脏读不可复读。导致幻读
  • SERIALIZABLE(可串行化):全部阻止。一般用于分布式事务

脏读:A事务读取数据进行修改还没提交,B事务访问该数据,这个数据是还未提交的,所以是脏数据
不可重复读:A事务多次读同一条数据,如果在此期间B事务修改了数据,会导致A事务读取到的数据不一致—重点在修改
幻读:在A事务查询过程中,B事务插入几条数据,导致A发现了一些本来不存在的记录,如同幻觉—重点在新增、删除

怎么实现隔离级别的

通过MVVC(多版本并发控制)来实现的,本质就是mysql通过undo log存储了多个版本的历使数据,根据规则去读取某一历史版本的数据,这样就可以在无锁的情况下实现读写并行,提高数据库性能

undo log如何存储修改前的记录

对于使用Innodb存储引擎的表来说,聚集索引都包含下面两个必要的隐藏列

trx_id:一个事务每次对某条聚集索引记录进行改动时,都会把该事务的id赋值给trx_id隐藏列

roll_pointter:每次对某条聚集索引记录进行改动时,都会把旧的版本写入undo日志中,这个隐藏列就相当于一个指针,通过他找到该记录修改前的信息

说说mysql(innodb)的常见锁

  • 表锁:锁整张表,不会死锁,容易锁冲突,性能相对较低
  • 行锁:锁一行,粒度最小,会死锁,并发好,mysql5.5之后innodb为默认引擎,使用了行锁。行锁有分为共享锁(S)和排他锁(X)
  • 共享锁又称读锁。若事务A加上S锁,其他事务只能读,不能修改(加X锁),直到释放S锁
  • 排他锁又称写锁。若事务A加上X锁,其他事务不能在加锁,直到释放X锁
  • 乐观锁(需自己实现)通过版本号去实现,减少上锁开销,适合于读多写少的情况

谈谈mysql的架构

20211025112150

总体上分为server层和存储引擎层

server层:跨存储引擎功能都在这一层实现,比如存储过程,触发器,视图,函数,还有bin log日志

存储引擎层:负责数据的存储和读取,采用可替换式插件架构,支持innodb,myisam等多个存储引擎,redolog是innodb的特有引擎

基本组件

连接器:主要与身份验证和权限功能相关,类似门卫角色。主要负责登录数据库,进行用户身份认证,如果认证成功,会连接权限表查询用户权限,只要链接不断开,该用户权限修改不受影响

查询缓存(mysql8移除):用来缓存select语句结果集。实际业务如果更新比较频繁,查询不建议使用缓存,因为场景比较少,所以8以后进行了移除

分析器:如果没有命中缓存,交给分析器,主要用来解析sql语句是来干嘛的,检查词法和语法

优化器:选择一个最优方案去执行。逻辑转换(化简表达式)代价优化(比如多个索引时如何选择,多表查询如何选择关联顺序)

执行器:执行优化后的语句,执行前校验用户权限,没有返回错误信息,有调用存储引擎,返回执行结果

sql语句如何执行

比如一个select语句select * from tb_student A where A.age='18' and A.name=' 张三 ';

首先检查是否有权限,如果有会查询缓存,如果没有通过分析器去进行词法分析,提取关键元素select,表名,需要查询的列,条件。然后在去检查语法,比如关键词是否正确,没有问题进入优化器

如上述语句有两个条件,先查前面在查后面,或者反过来,优化器会选择一个效率比较高的方法

然后进行权限校验,没有返回错误信息,有执行返回结果

如果是一个更新语句update tb_student A set A.age='19' where A.name=' 张三 ';

先查询到这个语句,有缓存则用缓存

拿到查询的结果,将age改为19,innodb将数据保存在内存中,同时记录redo log,此时该日志为prepare状态,然后告诉执行器执行完毕
执行器收到通知记录binlog,调用引擎,提交redo log变更为提交状态

innodb通过redo log来支持事务,如果redo log与bin log(归档,用来备份)只用一个会出现数据不一致的问题

机器异常重启,redo log用来恢复数据,bin log没有记录,在备份的时候会出现数据不一致

机器异常重启,写完bin log,机器无法恢复数据,但备份又有记录,所以也会出现不一致的情况

详情见这篇文章

谈谈mybais

是一款轻量级ORM框架,可以使用注解或xml来映射到pojo的数据库对象

mybatis

解决了什么问题?

不需要数据库频繁的创建连接,解决代码硬编码问题,创建对象映射将数据库结构映射到pojo对象

$#的区别?

#有预编译效果,替换为?号,使用#可以有效防止sql注入的问题

dao口的工作原理?

jdk动态代理,mybatis运行时使用jdk动态代理为dao口生成代理proxy对象,该对象会拦截接口方法,转而执行sql,最后将sql结果返回

一二级缓存?

  • 一级缓存,基于perpetualCache和hashmap本地缓存,存储作用域为session,当session flush或者close之后,该session的cache清空
  • 二级缓存与一级机制相同,默认也是采用perpetualCache,hashmap存储,不同在于作用域位mapper(namespace),并可以自定义存储源头如Ehcache。如果要开启二级缓存,需要在sql映射文件中添加一行<cache/>
  • 对于缓存更新机制,当某个作用域(一级缓存session/二级缓存namespace进行了C/U/D的操作后,默认作用域下所有的select中缓存将被clear)

是否支持延时加载?如果支持,实现原理是什么?

仅支持assoiation关联对象和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()方法的调用。这就是延迟加载的基本原理。

ORM之Mybatis

与Hibernate的区别

  • Hibernate是全自动ORM映射工具,Mybatis是半自动
  • Hibernate适合代码量少的代码,优化比较困难。Mybatis相反
  • Hibernate是重量级框架,适合OA系统。Mybatis是轻量级框架,适合需求频繁变化的项目

工作原理

mybatis

为什么需要预编译?

数据库在执行sql的时候就不需要在编译了,Mybatis在默认情况下会对所有sql进行编译

#和$的区别?

  • #可防止sql注入,$是字符串替换,没有预编译处理,不能防sql注入
  • #会代替?,$是原值传入

如何解决了JDBC的问题

  • 不需要频繁连接、释放资源了,使用连接池解决此问题
  • sql与代码解耦
  • 有独特的占位符
  • 可以将结果集处理为java对象

一二级缓存

  • 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,MyBatis默认打开一级缓存。

  • 二级缓存与一级缓存机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同之处在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> 标签

  • 对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)进行了C/U/D 操作后,默认该作用域下所有缓存将被清理掉

扩展

更多内容可点击参照这篇文章