JAVA面经准备-每日更新

122 阅读10分钟

前言

这是一篇记录笔者 搜集和整理的java面试题的文章,里面加入了不少自己的想法,希望对大家有帮助。

更新记录

序号更新时间更新内容
12023-11-08mysql原理

一、MYSQL

mysql篇共分三块内容 mysql原理、mysql应用、mysql面试题。

1、mysql原理

mysql原理主要写几个常用的引擎、数据结构等。

1.1 架构模型

image.png

  • 客户端:redis-cli、navicat等。
  • 连接器:负责mysql和客户端之间的通信,提供连接处理和身份验证功能。
  • 分析器:提供词法分析和语法分析。
  • 缓存:查询结果缓存,mysql8.0 后删除该功能。
  • 优化器:使用他认为最正确的方式执行,可使用explain分析。
  • 执行器:调用存储引擎,执行最终sql。
  • 存储引擎:可插拔式存储引擎,有innodb、myisam等
1.2 db引擎

mysql常用的存储引擎有 innodb、myisam、memory、merge,最常用的是innodb和myisam,这里做一个特征介绍。

innodb

image.png

  • innodb数据采用B+树存储数据。
  • innodb文件格式 .frm .idb (frm存储表结构数据,idb存储表数据和索引数据)

myisam

image.png

  • myisam和innodb数据结构类似,唯一区别是,叶子节点存放的是数据地址而非全量数据。
  • myisam文件格式 .frm .myi .myd (frm存储表结构数据,myi存储索引数据,myd存储表数据)

memory

  • memory索引可以是hash或B树,数据实际存放于内存中,重启会丢失(hash索引 针对精确查询速度极快,但是不支持范围查询;),适合做中间结果表或临时表;
  • memory文件格式 .frm (frm存储表结构数据,实际数据存放于内存中)

merge

  • merge表是一组myisam的组合表,实际上merge表是没有数据的,对于merge表的增删改查操作实际上是对下层 myisam表的操作。
特点innodbmyisammemorymerge
存储位置磁盘磁盘内存磁盘
索引结构B+treeB+treeBtree\hash同myisam
文件格式frm .idb.frm .myi .myd.frm同myisam
事务支持不支持不支持同myisam
外键支持不支持不支持同myisam
表压缩不支持支持不支持同myisam

tips

  • 查看所有存储引擎 show engines ;
  • 查看你的mysql当前默认的存储引擎 show variables like '%storage_engine%';
  • 显示某个引擎的参数设置 show engine innodb status;
  • 看某一个表的存储引擎,就是看该表的创建过程: show create table [tb_name]; show table status like '[tb_name]'; select table_name,engine from tables where table_name='[tb_name]' and table_schema='[db_name]';
  • 修改存储引擎:alter table city engine=MyISAM;
扩展:
  • B+树:每个节点可以有多个子节点,被称为度;每个节点的大小通常与磁盘IO的大小一致(例:4k);非叶子节点不存储数据,只存储键值区间,用于指示子节点的范围;叶子节点存储全量数据;叶子节点之间存在双向链表连接,方便范围查找;
  • B树:每个节点可以有多个子节点,被称为度;每个子节点存储键值区间和数据;平衡的多路搜索树,是二叉树的一种改进;每个节点的大小通常与磁盘IO的大小一致(例:4k);
  • 二叉树:每个节点只有两个子节点;左中右大小排序原则;每个节点要存储键值和数据;
特点二叉树B树B+树
非叶子节点数据格式键值+数据键值区间+数据键值区间
叶子节点数据格式键值+数据键值+数据键值+数据+双向链表
节点度数2磁盘IO/每份数据大小磁盘IO/每份数据大小
层高
1.3 MYSQL 事务

innodb的一大特征是支持事务,基于acid,划分事务的隔离级别解决可能出现的问题。

ACID

  • 原子性(Atomicity):整个事务中的所有操作,必须作为一个单元全部完成,借助undolog实现
  • 一致性(Consistency):在事务开始之前与结束之后,数据库都保持一致状态,一般使用业务逻辑上来保证
  • 隔离性(Isolation):一个事务不会影响其他事务的运行,使用锁机制和MVCC机制来实现
  • 持久性(Durability):在事务完成以后,该事务对数据库所作的更改将持久地保存在数据库之中,并不会被回滚

隔离级别

  • 读未提交:读未提交存在脏读问题
  • 读已提交:读已提交解决了脏读问题但是存在不可重复读和幻读问题。
  • 可重复读:隔离级别设置成为可重复,解决了不可重复读和幻读的问题
  • 串行化:隔离级别设置成为串行化实际上数据库已经是同步的了,实际使用过程中不会使用该隔离级别

数据一致性问题

  • 脏读:一个事务在提交之前,在事务过程中修改的数据,被其他事务读取到
  • 不可重复读:一个事务在提交之前,在事务过程中读取以前曾读取过的数据却发现数据发生了改变。
  • 幻读:一个事务在提交之前,在事务过程中读取以前曾读取过的区间数据发现数据量增多或减少。
隔离级别\问题脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读不可能不可能不可能
串行化不可能不可能不可能

tips

  • 查看事务隔离级别:

5.7.2之前 使用 show variables like 'tx_isolation' 或者 select @@tx_isolation;

5.7.2之后 使用 show variables like 'transaction_isolation'或者 select @@transaction_isolation;

  • 修改事务隔离级别:

5.7.2之前 使用 set [global 全局范围 /session 会话范围 ] tx_isolation = ‘隔离级别’

5.7.2之后 使用 set [global 全局范围 /session 会话范围 ] transaction_isolation = ‘隔离级别’

示例:set global tx_isolation = 'READ-COMMITTED';

1.4 mysql锁

数据库的acid特性中的隔离性实现方式是依赖MYSQL的锁机制,LBCC和MVCC。

锁分类

image.png

锁原理

  • record锁:锁住的是一条记录,有 S 锁和 X 锁之分;

示例:select * from t_test where id = 1 for update;

  • gap锁:存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。

  • 临键锁:是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

image.png

  • 行锁:行锁只对某一条数据加锁,应用算法是record锁、gap锁、临键锁。

原理:当sql执行按照索引进行数据检索时,查询条件为范围查找(between and ,<,>等)并有数据命中, 这时sql语句加的锁为next-key-locks,锁住了索引记录所在区间以及索引记录的下一个区间(都是左开右闭), 没有查到结果的话那么退化为间隙锁或记录锁。

  • 表锁:如果sql语句没有增加索引条件,就无法触发索引准确查询,从而触发表锁,需要区分S和X锁。
  • 页锁:页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级锁冲突少,但速度慢。因此,采取了这种的页级锁,一次锁定相邻的一组记录,DB 支持页级锁。
  • 共享锁:多个事务对于同一数据可以共享一把锁,都可以访问到数据,但是只能读不能修改因此又叫做读锁、s锁。 加锁方式:查询语句后使用lock in share mode

当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);

  • 排他锁:排他锁不能与其他锁共存,又叫做写锁、x锁 加锁方式:更新自动加排他锁 、查询语句使用for update

当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)

  • 意向锁:当事务准备在某条记录上加 S 锁时,需要先在表级别加上一个 IS 锁。当事务准备在某记录上加 X 锁时,需要先在表级别加上一个 IX 锁。

IS、IX 锁是表级锁,它们的提出仅仅为了在之后加表级别的 S 锁和 X 锁时可以快速判断表中的记录是否被加上锁,以避免用遍历的方式来查看表中有没有上锁的记录。就是说当对一个行加锁之后,如果有打算给行所在的表加一个表锁,必须先看看该表的行有没有被加锁,否则就会出现冲突。IS 和 IX 锁就避免了判断表中行有没有加锁对每一行的遍历。直接查看表有没有意向锁就可以知道表中有没有行锁

  • 悲观锁:关系数据库管理系统中,悲观并发控制(悲观锁)是一种并发控制的方法;悲观锁指的是采用一种持悲观消极的态度,默认数据被外界访问时,必然会产生冲突,所以在数据处理的整个过程中都采用加锁的状态,保证在同一时间、只有一个线程可以访问到数据,实现数据的排他性;通常,数据库的悲观锁是利用数据库本身提供的锁机制去实现的;

实现:1.外界要访问某条数据,那它就要首先向数据库申请改数据的锁(某种锁);2.如果获取成功,那它就可以操作该数据,在它操作期间,其它客户端就无法操作该数据了;3.如果获取失败,则代表同一时间已有其它客户端获取了该锁,那就必须等待其他用户释放锁;

优点:在写多读少的并发环境中使用,可以做到数据的安全性;

缺点:加锁会增加系统开销,降低数据的吞吐量低,不适合在读多写少的场景下使用;

  • 乐观锁: 假设认为即使在并发环境中,外界对数据的操作一般是不会造成冲突,所以并不会去加锁(所以乐观锁并不是一把锁),而是在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回冲突信息,让用户决定如何去做下一步,比如说重试,知道成功为止;数据库的乐观锁,并不是利用数据库本身的锁去实现的,可能是利用某种实现逻辑去实现做到乐观锁的思想。

实现:1.在表中增加一个字段作为该记录的版本标识,比如叫 version,每次对该记录的写操作都会让 version + 1;2.做出更新,要提交的时候,就会拿取得的 version 取跟数据库中的 version 比较是否一致,如果一致则代表这个时间段,并没有其它的县城也对这个数据进行修改、更新,同时 version + 1;如果不一致,则代表这个时间段,该记录已经被其它线程修改过了,认为是过期数据,返回冲突信息,比如重试;3. update table set num = num + 1, version = version + 1 where version = #{version} and id = #{id}

优点:读多写少的并发场景下,可以避免数据库加锁的开销,提高 DAO 层的响应性能;

缺点:写多读少的并发场景下,即在写操作的竞争激烈的情况下,会导致 CAS 多次重试,冲突频率过高,导致开销比悲观锁更高;

MVCC

// todo:当前读和快照读。。。。,可见算法,额外字段,等等待完善

2.mysql应用