操作系统

硬件

运算器、控制器、存储器、输入设备、输出设备 5 个部分也被称为冯诺依曼模型

CPU

内部有寄存器、控制单元和逻辑运算单元

总线

地址总线(用于指定 CPU 将要操作的内存地址)、数据总线(用于读写内存的数据)、控制总线(用于发送和接收信号,中断、设备复位等信号)。

一般流程为通过地址总线来指定内存的地址,控制总线控制是读或写命令,然后通过数据总线读取数据。

存储器

img

对于存储器,它的速度越快、能耗会越高、而且材料的成本也是越贵的,以至于速度快的存储器的容量都比较小。

  • 寄存器;
  • CPU Cache;
    1. L1-Cache;
    2. L2-Cache;
    3. L3-Cahce;
  • 内存;
  • SSD/HDD 硬盘

消息队列

消息队列的作用

通过异步处理提高系统性能,减少响应时间

削峰填谷,缓解瞬时系统压力、提高系统资源利用

降低系统耦合性

消息队列会带来的问题

系统复杂度提升(处理消息丢失、重复消费问题)

系统可用性降低(需要考虑消息丢失,MQ挂掉等问题)

一致性问题(异步处理问题)


线程模型

Redis 是单线程吗?

Redis单线程指的是【接收客户端请求->解析请求->对数据进行读写操作->发送数据给客户端】这个过程由主线程完成的

在2.6版本会启动处理关闭文件和AOF刷盘任务

4.0版本会增加lazyFree线程释放内存,例如unlink key 、flushdb async、 flushall async 会把删除命令提交给后台线程操作,这样不会阻塞主线程,del会在主线程删除,对于大key最好还是使用unlink

这些都是耗时操作会阻塞主线程。三种任务都有各自的任务队列,会去消费这些任务

redis线程模型


framework

Mybatis

MyBatis的$和#的区别

1
select * from user where name = #{name};

解析为:

1
select * from user where name = ?;

而${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换

sql注入user; delete user; ,那么就会删除整张表

Mybatis流程

  1. 读取配置文件
  2. 加载Mybatis映射
  3. 加载sqlSessionFactory
  4. 创建sqlSession
  5. Executor执行器
  6. 设置MapStatement
  7. 输入映射
  8. 输出映射

数据库

数据库

范式

第一范式确保每列保持原子性

第二范式确保表中的每列都和主键相关

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

Mysql

Innodb数据存放

表空间文件结构

img

Redundant:不是一种紧凑的行格式

compact:是一种紧凑的行格式,5.1之后默认

dynamic:是一种紧凑的行格式,默认Dynamic

compressed:是一种紧凑的行格式

compact格式

变长字段列表:记录变长字段长度,倒序存放,使访问真实数据的时候能尽量在一个cacheline中

null值列表:记录是否为null值,大小取决于允许为null值的个数,单位为字节,同样是逆序存放

记录头信息:包含指向下一条数据的指针

row_id:行id(如果有主键或者唯一约束id那么就没有row_id)6字节

trx_id:事务id 6字节

roll_ptr:回退指针 7字节

真实数据

varchar(n) 中 n 最大取值为多少

Mysql规定除了text和blob,其他的列(不包括记录头信息和隐藏列,包括变长字段列表和null值列表)总和不超过65535个字节

varchar(n)中的n代表的是字节数,与字符集有关

要保证所有字段的长度 + 变长字段字节数列表所占用的字节数 + NULL值列表所占用的字节数 <= 65535

65532+2+1=65535

但是与字符集有关,需要处于对应的字节数才为个数
字符集

行溢出就会指向新增页的地址

Server执行流程

  1. 先与server的连接器建立连接
  2. 发送查询语句,server会先从查询缓存(查询缓存是 server 层的,也就是 MySQL 8.0 版本移除的是 server 层的查询缓存,并不是 Innodb 存储引擎中的 buffer pool。)查看,有就直接返回结果
  3. 进入解释器,进行语法分析生成语法树
  4. 依次通过预处理器(查看表的字段和表是否存在等工作,将*号扩展成所有列),优化器(基于查询成本看使用什么索引),然后到达执行计划当中,最后执行器执行
  5. 调用存储引擎api查询数据并返回
  6. 存入查询缓存

计算机网络

https://xiaolincoding.com/network/3_tcp/tcp_interview.html

TCP

三次握手

img

  • 三次握手才可以同步双方的初始序列号,两次握手无法知道对方是否收到自己的初始序列号
  • 三次握手才可以避免资源浪费,两次握手服务端每次接受到syn报文就会建立一个连接,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立历史连接,造成资源浪费
  • 三次握手才可以阻止重复历史连接的初始化(主要原因)主要是因为在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,如旧的连接比新的连接先到达,那么就会和旧的连接进行初始化,并可能开始发送信息

Java集合

参考https://javaguide.cn/

ArrayList

构造函数

  1. 校验初始容量
  2. 如果是无参构造,那么还是赋值一个空的数组,但是插入的时候会辨别;反之则构造一个空的数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,但是下次扩容会取最小扩容量与默认初始化长度(一般为10)进行比较。

扩容过程

add函数

校验大小获取最少需要长度通常是插入数量+当前长度,

grow函数

如果不够就进行扩容,并确定扩容大小,每次扩容变成原来的1.5倍,0.5倍由无符号右移一位得到,新长度有最少需要长度与原来长度的1.5倍的最大值得到,但是不超过SOFT_MAX_ARRAY_LENGTH,该值为Int的最大值-8,是由于部分java虚拟机需要存放object header会导致堆栈溢出,Java中的对象头信息(数组长度)需要占用8个字节的空间,所以选择了个略小于最大值的值。


Java问题收集

理解原码、反码、补码、移码

  • 符号位:0位正数,1为负数

  • 原码不能进行运算

  • 正数的原码和反码一样,负数的反码是在原码的基础上,符号位不变,其余取反

  • 正数的补码和反码一样,负数的补码是在反码的基础上进行+1操作

  • 移码是在补码的基础上,把符号位取反

1 -1 1+(-1) 结果
原码 0000 0001 1000 0001 1000 0010 -2
反码 0000 0001 1111 1110 1111 1111 -0
补码 0000 0001 1111 1111 0000 0000 +0
移码 1000 0001 0111 1111 1000 0000 0

取值范围

整数 n=8
原码 -(2^(n-1)-1)~2^(n-1)-1 -127~127
反码 -(2^(n-1)-1)~2^(n-1)-1 -127~127
补码 -2^(n-1)~2^(n-1)-1 -128~127

Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* native 方法,用于创建并返回当前对象的一份拷贝。
*/
protected native Object clone() throws CloneNotSupportedException
/**
* 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
*/
public String toString()
/**
* native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
*/
public final native void notify()
/**
* native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
*/
public final native void notifyAll()
/**
* native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
*/
public final native void wait(long timeout) throws InterruptedException
/**
* 多了 nanos 参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫秒。。
*/
public final void wait(long timeout, int nanos) throws InterruptedException
/**
* 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
*/
public final void wait() throws InterruptedException
/**
* 实例被垃圾回收器回收的时候触发的操作
*/
protected void finalize() throws Throwable { }

深拷贝和浅拷贝

引用拷贝:只是复制引用地址

浅拷贝:将object对象在堆上复制一份对象,但是里面如果有引用对象的话,也只会复制地址

深拷贝:将object对象全部复制,包括内部对象。

String

Java 9 为何要将 String 的底层实现由 char[] 改成了 byte[] ?

JEP 254: 紧凑字符串 (openjdk.org)

我们建议更改类的内部表示 从 UTF-16 数组到数组加上编码标志字段。 新类将存储编码为 ISO-8859-1/Latin-1(每个字符一个字节),或 UTF-16(每个字符两个字节) 字符)