JAVA学习笔记
Java学习笔记
一、 Springboot、Spring学习笔记
Yaml配置文件
--------------------
优先级 properties>yml>yaml
默认配置文件名称为application
--------------------
yml注入数据方式:
1.@value("${name}")
2.Environment
3.@configuration
4.@ConfigurationProperties注解注入数据,这种方式可以导入configuratuon包,可以在配置文件中有提示。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
-------------------------------
condition配置
@ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。
注解定义类
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
IOC容器解释
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置和可执行的系统或应用程序。
IOC 容器具有依赖注入功能的容器,它可以创建对象,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。
自动配置和监听机制
@Enable 动态加载某些配置,其他项目的资源
1.@componentScan
2.@import
@EnableConfiguration+配置文件
----------------------------
监听器
ApplicationContextInitializer
SpringApplicationRunListener
CommandLineRunner
ApplicationRunner
-----------------------------------
开启actuator监控,可以查看健康、bean容器各种资源情况
-----------------------------------
spring-boot-admin图形界面使用情况
Mabtis-plus框架
- service类中选用接口而不选用类
l 为了基于SPringBoot 框架下还可以再扩展,如果只用类,则无从下口
l 2 两个同事一块工作 定义一个接口就相当于一个占位符 让他去写就行了 保证工作并行
l 3 适配器或者说是一个简单的工厂类如果没有定义接口 那么面对众多的实现类无法统一操作
l 规范!!想让人符合但又不可能看着别人写代码就先出一套接口 让别人看着办
l java没有多继承但可以实现多接口说是面向对象实际上最好还是要面向接口编程
- MP的分页对象
Page<OrderInfo> pageParam = new Page<>(page,limit);
IPage<OrderInfo> pages = baseMapper.selectPage(pageParam, wrapper);
和spirng自带的mongodb的分页对象
Pageable pageable = new PageRequest(page-1,size);
Basemapper.selePage(pageable);
- wrapper条件构造器
eq(R column, Object val); // 等价于 =,例: eq("name", "老王") ---> name = '老王'
ne(R column, Object val); // 等价于 <>,例: ne("name", "老王") ---> name <> '老王'
gt(R column, Object val); // 等价于 >,例: gt("name", "老王") ---> name > '老王'
ge(R column, Object val); // 等价于 >=,例: ge("name", "老王") ---> name >= '老王'
lt(R column, Object val); // 等价于 <,例: lt("name", "老王") ---> name < '老王'
le(R column, Object val); // 等价于 <=,例: le("name", "老王") ---> name <= '老王'
Spring-cloud
- Spring Cloud 服务熔断与降级 Hstrix的优点:
Hytrix是一款优秀的服务容错与保护组件,它提供了熔断器功能,能够有效地阻止分布式微服务系统中出现联动故障,以提高微服务系统的弹性。Spring Cloud Hystrix 具有服务降级、服务熔断、线程隔离、请求缓存、请求合并以及实时故障监控等强大功能。
l 保护线程资源:防止单个服务的故障耗尽系统中的所有线程资源。
l 快速失败机制:当某个服务发生了故障,不让服务调用方一直等待,而是直接返回请求失败。
l 提供降级(FallBack)方案:在请求失败后,提供一个设计好的降级方案,通常是一个兜底方法,当请求失败后即调用该方法。
l 防止故障扩散:使用熔断机制,防止故障扩散到其他服务。
l 监控功能:提供熔断器故障监控组件 Hystrix Dashboard,随时监控熔断器的状态。
- Spring Cloud是什么
二、 Java 基础
- JDBC 3个接口不同的使用方式
Servlet
- Servlet和CGI
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
优势:
性能更好
独立于平台,在虚拟机运行,不依赖系统环境
安全性能更好
可以使用java类库的功能
Servlet在web服务器中运行,每个线程处理一个请求,无需单独创建一个进程来处理客户端请求,大大加快了处理效率。
-----------------------------------
Servlet生命周期:
Init()-> service() 调用doGet、doPost方法 ->destroy()方法
service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。每次服务器接收到一个Servlet请求时,服务器会产生一个新的线程并调用服务。
在生命结束时被调用destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让Servlet关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
- 读取HTTP头的方法
- Strut1和Struts2
Struts工作原理:
- AWT和Swing
AWT和Swing都是基于图像界面的工具包,awt是基于操作系统的图形操作界面
- Sleep和wait
- Error和RunTimeException
异常处理 EXCEPTION
- 加载mysql驱动三种方法:
集合
13. 不安全的集合类
平常用到的都是非线程安全的集合类,因为要使线程安全,是要加锁来实现的,势必造成性能降低。
如hashset、hashmap、arraylist、linkedlist、treemap、treeset、stringbulider等。
像stringbuhffer、vector、hashtable这些都是专用于多线程的,再者以concurrent(意为:同时发生的)为前缀的集合类大多是线程安全的。
HashSet他不是线程安全的,set类型的特征就是无序,不允许存储相同的对象,即是存储的值对象可以重复,键对象不可重复。
Collection接口是LIST和Set接口的父接口,通常情况下不直接使用。
JAVA 8 新增特性
14. JAVA8 新增hashmap中的merge函数
源码如下:
这个函数可以让相同的键值对的元素进行聚合操作
例子:
最后结果如下:
{1=zhagsan-liss, 2=lisi-wangwu}
{1=liss, 2=wangwu}
这种方式也可以实现map的合并。
15. Stream流
运算符优先级、基本类型装换
- 16. JAVA运算符优先级
+= 可以隐式强转自动转化,但+号要显示强转,手动 byte + byte ->int(byte) + int(byte)
三元运算符转换规则:
数值之间:
三元操作符遇到可以转换为数字的类型都会自动类型提升
17. JAVA基本类型
Char 在short 后面
18. Finally不会执行情况
Final关键字
1.Final修饰成员变量必须赋值,而局部变量可以暂且不赋值。
2.final修饰的变量(无论成员变量还是局部变量),在使用前必须进行赋值,而且只能赋值一次,如果赋值多次赋值或者赋值进行使用时,编译会提示错误。
3.final 指向的是内存中的一块地址,但是地址内的内容是可以修改的。
19. 前台线程和后台线程
正则表达式
链接:https://www.nowcoder.com/questionTerminal/4294440c29e749e3ba6d7a6b2dfe25a3
1. 任意一个字符表示匹配任意对应的字符,如a匹配a,7匹配7,-匹配-。
2. []代表匹配中括号中其中任一个字符,如[abc]匹配a或b或c。
3. -在中括号里面和外面代表含义不同,如在外时,就匹配-,如果在中括号内[a-b]表示匹配26个小写字母中的任一个;[a-zA-Z]匹配大小写共52个字母中任一个;[0-9]匹配十个数字中任一个。
4. ^在中括号里面和外面含义不同,如在外时,就表示开头,如^7[0-9]表示匹配开头是7的,且第二位是任一数字的字符串;如果在中括号里面,表示除了这个字符之外的任意字符(包括数字,特殊字符),如[^abc]表示匹配出去abc之外的其他任一字符。
5. .表示匹配任意的字符。
6. \d表示数字。
7. \D表示非数字。
8. \s表示由空字符组成,[ \t\n\r\x\f]。
9. \S表示由非空字符组成,[^\s]。
10. \w表示字母、数字、下划线,[a-zA-Z0-9_]。
11. \W表示不是由字母、数字、下划线组成。
12. ?: 表示出现0次或1次。
13. +表示出现1次或多次。
14. *表示出现0次、1次或多次。
15. {n}表示出现n次。
16. {n,m}表示出现n~m次。
17. {n,}表示出现n次或n次以上。
18. XY表示X后面跟着Y,这里X和Y分别是正则表达式的一部分。
19. X|Y表示X或Y,比如"food|f"匹配的是foo(d或f),而"(food)|f"匹配的是food或f。
20. (X)子表达式,将X看做是一个整体
21. \\b代表边界,空格或者开头, \\bcat\\b 能匹配 cat cat catcat
20. 正则表达式、捕获
分组嵌套
\\1对应第一个括号 ((\\d)3) 23 \\2对应第二个括号(\\d) 2
21. Mysql连接步骤
栈、队列、双端队列
String
22. Char转为String
1. String s = String.valueOf('c'); //效率最高的方法
2. String s = String.valueOf(new char[]{'c'}); //将一个char数组转换成String
3. String s = Character.toString('c');
// Character.toString(char)方法实际上直接返回String.valueOf(char)
4. String s = new Character('c').toString();
5. String s = "" + 'c';
Stirng 转为char[]
Char[] temp = String.tocharArray()
- String.equals()和“==”的区别
==是2个对象比较在内存地址是否相同(引用类),基本类,相等数值
Equals是比较2个对象的字符串是否相同
多态、构造代码块、权限符、重载、重写、构造函数、接口
24. Java构造函数
25. 接口和抽象类的区别:
接口的变量必须要初始化。
接口里定义的变量只能是公共的静态的常量 public static final,不允许修改,可以用。这和成员变量不能被重写一个道理。
抽象类中的方法都是puiblic abstract,因为要让子类具体化,也可以不含抽象方法。
接口也可以有方法的实现,用Default实现
26. 重写、重载
声明static的方法不能被重写,但是可以重载(增加参数),也可以重新定义。
重写变量不会覆盖,成员变量不会被重写。
重载和重写都是多态性。
重载可分为纵向(重写)或者横向,重写需要返回类型,参数列表,方法名完全相同,重载是参数列表不同,其他无限制!
构造代码块插入到构造函数里面执行
27. 对于内部类需要先是实例化外部类才能实例内部类。
28. 各个控制符的访问权限
更正: protected可以被同一包中被访问,可以被其他包的他的子类访问,也就是eXtends这个类后就可以用他的构造函数
29. 静态块和静态域
静态块在实例化中只执行一次。
构造代码块会插入到构造函数的前端。
30. 面向字符的流和面向字节的流
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
31.
三、 Java八股文 面试
一、 分布式
- 大型网站的特点
高并发、高可用、海量数据、用户分布广泛,网络情况复杂、安全环境恶劣、需求快速变更 可维护性渐进式发展
- 分布式网站结构
初级网站结构,应用程序和文件和数据全部放在一台服务器上。
分布式网站结构:
应用和数据分离后,不同特性的服务器承担不同的服务角色,网站的并发能力的得到提升,但由于用户的逐渐增多,网站又一次面临挑战,数据库的压力影响网站的性能。
使用缓存改善网站的性能,网站使用的缓存可以分为两种:缓存在本地和专门的分布式的服务器上远程缓存
- 文件描述符
内核中,对应于每个进程都有一个文件描述符表,表示这个进程打开的所有文件。文件描述表中每一项都是一个指针,指向一个用 于描述打开的文件的数据块———file对象,file对象中描述了文件的打开模式,读写位置等重要信息,当进程打开一个文件时,内核就会创建一个新的file对象。需要注意的是,file对象不是专属于某个进程的,不同进程的文件描述符表中的指针可以指向相同的file对象,从而共享这个打开的文件。file对象有引用计数,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同一个file对象的进程。
JAVA对象的存储、分配、定位
- Java类的加载机制
加载、验证、准备、解析、初始化、使用、卸载。验证、准备、解析叫连接过程。
加载:由4个加载器用双亲委托机制加载,查找此类字节码文件,利用字节码文件创建对象。
验证:确保class文件符合当前虚拟机的要求,包括对文件格式、元数据、字节码、符号引用的验证;
准备:进行内存分配,给static类变量分配内存,设置初始值;
解析:将常量池中的符号引用转为为直接引用,符号引用是一个字符串,它给出一些信息去标识一个类,直接引用为直接指向目标的指针或者相对偏移量或者句柄信息。
- Java对象所占字节,存储布局
8字节的markword 4字节的类型指针
修改markword给对象上锁
Mark Word记录了对象和锁有关的信息
- 对象怎么定位,怎么找
采用句柄方式定位,因为直接指针在对象移动位置时需要重新定位,垃圾回收机制,新生代区中,每次存活对象都会移动位置,所以采用句柄方式。
使用句柄访问对象,会在堆中开辟一块内存作为句柄池,句柄中储存了对象实例数据(属性值结构体)的内存地址,访问类型数据的内存地址(类信息,方法类型信息),
对象实例数据一般也在heap中开辟,类型数据一般储存在方法区中。使用句柄访问的好处是句柄中储存的是稳定的对象地址,当对象被移动时候,只需要更新句柄中的对象实例部分的值即可,句柄本身不用被移动修改。
- 三色标记算法, 想要解决或者降低用户线程的停顿的问题。根据GC root链标记垃圾,使用白黑灰三个颜色代表是否被垃圾收集器访问过
缺陷:有多标(读屏障,记录新的引用)和漏标(错杀)
多标会造成浮动垃圾,但是在下轮仍然会清楚
漏标则必须要处理,否则会有应用错误
解决方案:增量更新、SATB
记录新的引用记录或者保留原始快照,按照原来的引用图来
- 对象怎么分配
jvm是允许将一个对象分配到栈上的,只要能够确定该对象的作用范围不会超过方法生命周期。好处是对象可以跟着当前栈帧出栈而回收。当然对象是被打散存放的,比如不同参数作为不同的局部变量存储。逃逸分析判断对象作用只存在函数体内。
为什么会有TLAB?通常情况下对象是分配在堆上的,因为堆是线程共享的,所以同一时间可能会有很多线程申请空间分配,在这种情况下要加锁处理,如此一来就会造成分配效率下降。而TLAB是每个线程独有的,它可以避免这种开销,直接分配空间,效率几乎等同于C。
- 对象的创建过程
申请空间-》初始化-》构造函数初始化-》引用分配
JVM内存、GC机制
- JVM内存配置参数
求最小内存和survivor区大小
-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个, 即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
-Xms初始堆大小即最小内存值为10240m
10. JVM内存区域
分为方法区、堆、程序计数器、虚拟机栈、本地方法栈(c栈),其中方法区和堆是线程共享的,后三个事非线程共享的区域(每个线程自己的,相互隔离),线程共享区域与JAVA程序运行的生命周期相同,所以这也是垃圾回收机制发生在堆区。方法区中保存了每个类的信息、静态变量、常量、编译后的代码
在继承中对象的内存分配:
方法区存储静态变量和常量、编译好的代码块
方法区也叫静态区,存放不在改变的变量。
对存放对象实例,成员变量存放常量的地址指向方法区,方法指向编译好的代码块
父类的private变量存放在方法区(和子类的方法区放在一起)。其实我们在创建一个子类的时候,子类的所向指向的堆区中存放了子类自有的成员变量,同时还存放了父类所拥有的成员变量。
在创建一个子类的时候,我们把创建的时候的内存划分为两个区域,其中有一个super区,在这个区域中,存放的是父类的成员属性。另一个就是this区域,在这个区域存放的是子类自己的成员属性。
在方法区中,同样包括了两个部分,一个部分是super的方法区:super的方法区,只能指向父类的成员属性和方法,子类的是无法访问的。另一个是this的方法区,this的权限就相对大一些,可以访问子类自己的属性和方法,同时还可以访问父类的public的属性和方法(可继承的方法和属性)。
1.8静态变量池和内部strings移动到堆区
可以看到第三句话是错的,方法区是共享的。
11. 为什么新生代内存需要有两个Survivor区?
方法区(method area)只是JVM规范中定义的一个概念,用于存储类信息、常量池、静态变量、JIT编译后的代码等数据,具体放在哪里,不同的实现可以放在不同的地方。而永久代是Hotspot虚拟机特有的概念,是方法区的一种实现,别的JVM都没有这个东西。
上图是JVM内存的分布
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
新生代的算法:复制算法
老年代:标记-整理算法,会产生较多的碎片。
可以发现以上方法都有不足点。
设置两个survivor是为了减少碎片化内存的产生。
上图是一个survivor区,下图是2个,2个survivor分别是s0,s1
12. GC判定对象为死亡对象的算法
2)可达性算法
13. 垃圾回收器
https://www.jianshu.com/p/5b2721b891c0
Collection集合
14. Hashmap实现原理
l Hashmap是无序不安全的,key值唯一且可为空,value值可重复
l Jdk8添加了红黑树,链表长度大于等于8的时候链表变为红黑树
l Hash算法简化 resize逻辑修改
l Hashmap容量和扩容机制
负载因子是0.75因为在hash表中为了解决冲突,设置了负载音质,作用就是计算扩容阈值用,无参构造方法创建的hashmap初始长度默认为16,也是就当超过了目前空间的75%就开始扩容。
l HashMap处理冲突采用拉链法,在1.8中将链表替换成了红黑树增加查询效率
l B+树(红黑树实现原理)
多线程并发编程
15. 哲学家进餐问题
进餐需要2把筷子,每人拿一把筷子导致死锁。
解决方案:
1.锁定所有筷子对象,其他人不能进餐(导致粗粒度锁)
2.锁定每把筷子对象,当能同时拿到左右筷子时才开始拿
3.左撇子算法
16. 生产者消费者问题
线程需要同步,生产者生产完后才能消费,缓存区有上限,满了则不能生产。
生产者中需要判断队列是否为满,采用NotifyAll需要queue.awit()和while条件配合使用,队列需要加锁
17. 可重入锁
这里的对象锁只有一个,就是 child 对象的锁,当执行 child.doSomething 时,该线程获得 child 对象的锁,在 doSomething 方法内执行 doAnotherThing 时再次请求child对象的锁,因为synchronized 是重入锁,所以可以得到该锁,继续在 doAnotherThing 里执行父类的 doSomething 方法时第三次请求 child 对象的锁,同样可得到。如果不是重入锁的话,那这后面这两次请求锁将会被一直阻塞,从而导致死锁。
所以在 java 内部,同一线程在调用自己类中其他 synchronized 方法/块或调用父类的 synchronized 方法/块都不会阻碍该线程的执行。就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。因为java线程是基于“每线程(per-thread)”,而不是基于“每调用(per-invocation)”的(java中线程获得对象锁的操作是以线程为粒度的,per-invocation 互斥体获得对象锁的操作是以每调用作为粒度的)。
重入锁实现可重入性原理或机制是:每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。
18. Synchronized 和 lock 的区别
Synchronized同步的实现:
19. sychronized锁的升级:
指向栈中锁记录的指针lock record记录持有displaced word和锁住对象的元数据,告诉其他线程该object monitor已被占用。指向重量级锁的指针,就是object monitor的地址。
20. CAS原理:
Thread的join方法:
Object.wait() thread.join()释放的是子对象的锁,因为未调用前,子对象是不能自己执行的,只能当调用join时才能执行。
21. AQS详解:
Mysql数据库
22. 事务属性的种类:传播行为、隔离级别、只读和事务超时
23. 事务的4个特性
- 索引的结构
Mysql主要是用hash索引和b+树索引
- 索引的类型
聚集索引和数据放在一起,非聚集只能查找到主键的值。
- 优化模糊查询
模糊查询在数据量太大时会失效
1.添加全文索引,对中文不友好 ,索引类型设置为fulltext,查询使用match against,修改配置文件
2.用覆盖索引
3.尽量少用_和%开头查询,会让索引失效,半模糊查询
4.一般使用全文索引,第三方elasticsearch之类的没用过
5.创建表的时候创建 fulltile(name,title),或者直接create fulltext index
- SQL语句执行流程
语法分析、语义检查、查询重写(优化)、优化访问计划、生成代码、执行计划
- 创建索引3种方式
- alter table table_name ADD INDEX index_name;
- create index 不能创建primary key
- create table {
fulltex key(__)
unique key(__)
}
- JWT的认证流程
- JWT如何解决session的跨域问题
生成token后,加密放在返回的数据中,拿到token后,存入cookie缓存,设置httponly 防止xss攻击。
- JWT如何解决不同设备重复登录
每个设备与用户生成唯一的key加入到token中,这样就可以保证token的唯一性
- 单点登录,验证token即可,验证token中的姓名或者其他主键信息
Redis
- Redis数据结构
l String最大存储512MB, set key value
字符串对象的内部编码有3种方式,int、raw、embstr,长度大于32字节用raw
用法:热点数据、存储二进制数据
l Hash最大存储2^32-1个键值对,hmset field1 value1 ````
当键值对较少或者键值对字符串长度较小时(字节小于64字节,元素小于512个)采用ziplist(双向向链表),较多时采用第二种方式dict字典结构。
l List 采用quicklist作为数据结构,结合了双向链表和压缩列表的特点
l Set使用数组集合和hashtable的方式,在数据较少时使用整数集合
l Sorted set使用压缩列表和跳表的数据结构
- Ziplist压缩列表
Zlbytes 列表所占长度,4字节
Zltail 偏移量 指向最后一个元素,4字节
Zllength entry节点数量,2字节,2^16 =256
Entrys集合包含
l Prev_length 保存前驱节点长度,会扩充
l Encoding 保存当前数据长度和编码方式
l Conten 存储内容
Zlend 结尾
特点:
l 采用连续、紧凑的存储,节省内存,修改操作会导致内存重新分配
l 可能出现连锁更新的现象
l 双向链表,时间复杂度0(1)获取头和尾部数据
- Quicklist 快速列表
数据类型list的一种底层实现,是一种双向链表,也是一直复合结构体,包含ziplist,解决了每个节点都是单独分配内存,加剧内存的碎片化。3.2结合压缩、双向链表的优点。
数据结构:
由quicklist和节点组成,中间的节点数据是保持ziplist压缩特点,采用LZF压缩算法
- SDS简单动态字符串(len free char*)
优点:
l 防止缓冲区溢出
l 内存重分配
l 直接获取字符串长度
l 二进制安全,不以\0为结束符或其他特殊符号来处理
SDS分配空间策略:大于1MB直接分配1MB,更新free,小于则分配此时len大小相等的空间。
l 空间预分配
l 惰性空间释放,不直接释放而是用free记录起来
- ZSet实现原理
数据少时使用ziplist,每项元素都是数据+socre的方式,查找需要顺序遍历,省内存但是查找效率低。
当元素大于等于128,长度大于等于64字节后,采用跳表方式。
是基于多指针的有序链表实现的,给原始链表建立多层索引,查询时最上层索引开始查找,每层缩小范围,直到在原始链表中找到元素。
SkipList 分为两部分,dict 部分是由字典实现,Zset 部分使用跳跃表实现,从图中可以看出,dict 和跳跃表都存储的数据,实际上 dict 和跳跃表最终使用指针都指向了同一份数据,即数据是被两部分共享的,为了方便表达将同一份数据展示在两个地方。
使用跳表而不是红黑树的原因:
l 插入删除只需调整少数几个节点,红黑树需要重涂和旋转,开销较大。
l 区间查找速度更快
l 代码更加容易实现、灵活
利用时间换空间,时间复杂度为O(log(n)),每K个节点取一个索引,空间复杂度为O(n).
- Redis持久化机制
Redis是一个支持持久化的内存数据库,通过持久化机制将内存的数据同步到硬盘中,保证数据的的持久化。当redis重启后,将磁盘的数据加载到内存,技能达到恢复数据的目的。
持久化方式:AOF和RDB方式,2种同时开启优先使用AOF
l RDB默认的持久化方式,按照一定的时间周期把内存的数据以快照的方式写入磁盘,文件格式为rdb二进制文件。
l AOF redis会将每一个收到的写命令通过write函数追加到日志文件最后面,redis重启后会通过重新执行文件中保存的写命令来在内存中重建整个redis数据库内容。
- 缓存穿透
缓存穿透指用户查询数据,在数据库没有,自然在缓存中也不会有,每次需要查询两次返回空,缓存命中率问题。
解决办法是:布隆过滤器、将空结果缓存(无意义,查询key随机),则不会访问数据库。
布隆过滤器:采用K个hash函数,映射到k个点,如果K个点都存在,则证明数据存在。因为hash冲突的缘故,所以不存在则一定不存在,存在有可能存在。
- 缓存击穿‘’
热点数据到期,导致大并发的数据访问加到数据库上,数据库压力大增。
解决办法:使用互斥锁,缓存失效时只允许拿到锁才可以访问数据库。设置永不过 期。
- 缓存雪崩
Redis服务器宕机或者大片缓存到期,采用加锁、队列的方式访问数据库或者将到期时间分散开来。增强数据库或者redis容灾能力,对数据库分表,搭建redis集群。
- Redis事务
Watch命令监控key,如果在事务执行之前这个key被其他命令所改动则打断事务,返回nil,事务回滚或者执行完后会取消监控,在事务结束后取消。
- 常用命令
Flushdb /flushall 删除当前/所有数据库中的key
Keys * 获取所有的key
- Redis如何存储数据
l Redis典型的k-v数据库,key为字符类型,value为5种类型
l RedisObject核心对象,包括type,encoding,vm
- Redis与memcached的区别
l Redis不仅支持简单的k/v类型的数据,同时还提供五种数据结构的存储
l Redis支持数据的备份
l Redis支持数据的持久化
l 实现主从复制,实现故障恢复
l 实现分布式,sharding
- 单线程Redis为什么性能快?
l 单线程处理,避免上下文切换和竞争
l 使用I/O多路复用模型,单线程处理多个连接请求(多路复用)
l 基于内存
- Redis 过期策略和淘汰机制
定时删除+惰性删除
每隔100ms检查随机key是否过期
获取某个key时,reids会检查一下,这个key如果设置了过期时间那么是否过期,过期了此时就会删除。
这种过期策略是有问题的,于是引入淘汰机制。
淘汰策略:
Maxmemory-policy volatile-lru
Volatile-lru 从已设置过期时间的数据集筛选最近最少使用的数据集
Volatile-ttl 将要过期的数据集
Volatile-random 任意选择数据淘汰
Allkeys-lru 从所有数据集选择
Allkeys-random
四、docker命令
更换yum源
下载docker
Yum install docker
service docker start
容器基本命令:
docker search rabbitmq:management 检索mq镜像
docker pull rabbitmq:management 拉取镜像
docker images 查看镜像列表
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:management 启动mq
docker run -d -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=power -e RABBITMQ_DEFAULT_PASS=power --name rabbitmq --hostname=rabbitmqhostone rabbitmq:management 设置账户密码
# 查看所有的容器用命令docker ps -a
docker ps
启动容器
# eg: docker start 9781cb2e64bd
docker start CONTAINERID[容器ID]
stop容器
docker stop CONTAINERID[容器ID]
删除一个容器
docker rm CONTAINERID[容器ID]
查看Docker容器日志
1.创建mongo
docker run -itd --name mongo -p 27017:27017 mongo --auth 创建容器mongo 操作的是镜像
$ docker exec -it mongo mongo admin 操作的是容器,进入容器内发出命令
# 创建一个名为 admin,密码为 123456 的用户。
> db.createUser({ user:'admin',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});
# 尝试使用上面创建的用户信息进行连接。
> db.auth('admin', '123456')
dropUser("name")
docker exec -it mongo_db mongo /etc/init.d/mongodb restart 重启服务
2.redis
docker run -itd --name redis-test -p 6379:6379 redis
docer exec -it redis-test redis-cli
3.Mysql
# docker 中下载 mysql
docker pull mysql
#启动
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=zz1141 -d mysql
#进入容器
docker exec -it mysql-test /bin/bash
docker exec -it mysql-test mysql -u zhangzhe -p
#登录mysql
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Lzslov123!'; //修改密码
#添加远程登录用户
CREATE USER 'uesr'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
给所有权限
GRANT ALL PRIVILEGES ON *.* TO 'user'@'%';
添加最大连接数
select VARIABLE_VALUE from information_schema.GLOBAL_VARIABLES where VARIABLE_NAME='MAX_CONNECTIONS'
vim 编辑 /etc/mysql/conf.d/mysql.cnf(Ubuntu)
max_connections = 10000
default-time-zone=+08:00
character-set-server=utf8
重启服务
/etc/init.d/mysqld restart
#mysql
select user from mysql.user查看所有用户
drop user liaozesong;删除用户
show grants for 'zhangzhe';查看权限
show variables like "%time_zone%";查看时区
set global time_zone = '+8:00' 和=‘system’等同;
更改文件/etc/mysql my.cnf 添加
apt-get update 更新apt插件后才可以安装离线包
yum库安装:
更换apt-get源 则可以下载yum库
docker run -d -p 3306:3306 -v mysqldata:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql
容器的恢复,找到卷 docker inspect 找到卷
"Mounts": [
{
"Type": "volume",
"Name": "65d2a7a71b6f871e43f4c6fff8872b390d51eea4b821639f15f7cbde942e8491",
"Source": "/var/lib/docker/volumes/65d2a7a71b6f871e43f4c6fff8872b390d51eea4b821639f15f7cbde942e8491/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
创建mysqldata 把卷的内容 复制到这个文件
docker -v 挂载 到这个文件夹完成恢复
-- 修改SQL_MODE
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
1、增加、选择数据库
create\use\drop __ ;
show databases;
2、删除表
drop table _;(有外键约束删除不了)加上cascade级联删除
3、添加外键(必须是另一个表的主键)
alter table user
add foreign key(pid) references province(pId);
alter table 子表的数据表名
add foreign key(子表的外键名称)
references 父表的数据表名称(父表的主键名称)
4、查看列:desc 表名;
修改表名:alter table t_book rename to bbb;
添加列:alter table 表名 add column 列名 varchar(30);
删除列:alter table 表名 drop column 列名;
修改列名MySQL: alter table bbb change nnnnn hh int;
修改列名SQLServer:exec sp_rename't_student.name','nn','column';
修改列名Oracle:lter table bbb rename column nnnnn to hh int;
修改列属性:alter table t_book modify name varchar(22);
增删改查
select * from student;
delete student where id = 1;
insert into student values(id, name,classid);
5、修改数据
UPDATE cs_user SET gender = '男' WHERE id = 4;
6、先删除子表中的信息,才能删除主表的信息。(外键约束)
7、非全列插入insert into images(`id`,`content`) values( 0,'wu');注意键名要用``括起。插值是 insert into 表名 (参数1,参数n···) values ('参数1','参数n'),要对应列数插入。要不然会出现don't match row的错误。默认为所有行,不给值的行要注入NULL。
8、添加约束格式:
alter table 表名
add constraint 约束名
forergn key (`参数`) references 参照表名 (`参数`);
可直接删除约束,方便管理(推荐)
也可以使
alter table ch09.student1
add foreign key (Sno) references ch09.sc(Sno);
9、mysql不支持check 但是oracle支持,mysql一般用触发器解决检查问题。
10、cannot update or add a child row 是 因为参照表的外码,和被参照表的外码属性不一致,插入的值不一致,引擎不一致(主键与外键不一致)。
12、update , select, delete,insert都无需加table
create、alter 创建和修改表的结构需要加表名。
insert into R();
update R set _;
delete from R where;
13.授权给用户修改sc的grade属性
GRANT UPDATE(GRADE) ON SC TO ZHAO;
注意表中属性要接在uodate后
14.模式:数据的逻辑结构和关系的描述;
外模式:又称子模式或用户模式,用户可见的局部数据的逻辑结构和特征的描述,是模式的逻辑子集;
内模式:是数据的物理结构和存储方式的描述
15.关系完备的系统是指支持关系数据结构和所有关系代数操作。
16、筛选前10个元素
SQL语句的写法是select TOP 10 * from TABLE_NAME
如果使用的MySQL数据库,则是 select * from TABLE_NAME limit 0,10
五、其他
- 定位bug
复现问题->快速定位->定位接口->定位代码->修复问题->总结经验避免问题
查看日志,日志有错误信息则可以快速定位到bug代码。
快速定位,查看3端哪个出现问题
定位接口,定位代码 聚焦,debug逐行观察结果
六、计算机网络
- HTTP状态码
100 continue
200 成功
3xx重定向
-301 永久重定向
-302 临时重定向
4xx 客户端请求错误
-400 报文语法错误
-401 not auth未授权
-403 请求被拒绝 forbidden
-404 资源不存在
5xx 服务器内部错误
-503 服务器处于超负荷或停机状态,服务不可用
- 请求报文和响应报文
请求和响应都包括4个部分:
请求行 请求头部 空行 请求体
状态行 响应头部 空行 响应数据
- Get和Post方法
Get方法是幂等的
Get 请求不安全
Post请求不能被缓存
Get方法可以传参
- HTTPS和HTTP的区别
HTTP端口:80 HTTPS:443
HTTP存在的问题:窃听、伪装(身份不确定)、篡改
HTTPS就是为了解决这个几个问题,运行在SSL之上,通过SSL采用对称加密和非对称加密的混合加密的方式。
l 采用对称和非对称加密,在握手阶段,使用非对称加密传输对称加密的密钥,之后通信采用对称加密的方式保证效率。对称加密:采用相同的密钥的加密算法。非对称加密算法采用不同的密钥加密,即公钥加密私钥解密。
l 非对称加密是利用公钥加密,私钥解密,证书可能被第三方伪造,所以引入CA第三方机构,来保证服务器是可靠的。
l 防止篡改,没有私钥无法破解出完整的报文。
- 输入URL后,发生了什么
l 浏览器向DNS请求IP地址
l 域名DNS解析IP地址返回(DNS域名解析系统)
l 建立TCP连接
l 发出HTTP请求
l HTTP响应返回HTML文件
l TCP连接释放
l 解释页面,渲染页面’
应用层建立http报文(DNS解析出IP地址),传输层建立套接字,网络层通过路由协议路由转发到达目的网络,链路层发送数据帧给网关路由器(ARP协议IP地址到MAC地址的映射,查询到IP地址链路层发送到路由器,目的网络路由器传输到目的主机)
ARP协议:同局域网内广播帧,目的地址为ff-ff-ff-ff,所有主机和帧中的IP地址比对,发现是自己,单播响应自己的IP地址映射,不是同一网段(根据子网掩码判断),找到路由器的MAC地址,数据发送给路由器,路由器发送到目的网络然后再执行和目的主机ARP协议。
DHCP:应用层协议,UDP传输,采用68号端口,目的地址为255.255。客户机广播DHCP发现消息,服务器广播DHCP提供消息,客户机同意后则将IP地址分配给客户机。c/s模式,租用期。
- 网络结构中每层协议(四层结构)
应用层:SMTP\POP3\HTTP\DNS\RPC\DHCP
传输层:TCP\UDP 报文
网络层:ICMP\Ip\ARP\RARP 数据包
链路层:CSMA 帧
- CDN 内容分发网络,加入一层新的网络结构,把内容发布到离用户最近的网络边缘。加入cdn服务的网站,DNS请求将最终交给负载均衡的DNS处理。全局负载均衡DNS通过预先的策略,将离用户最近的节点地址提供给用户,使用户能快速得到服务。由高速缓存和负载均衡设备组成。
- HTTP 1.0和2.0区别
l 新的二进制格式,不采用文本格式
l 多路复用
l Header压缩
l 服务器推送,请求html时,把css,js全部推送
l 1.1默认支持长连接,不需要keep-alive参数,支持只发送头部,长链接可以在一段时间内保持连接,无需终端会话
l HTTP流水线:发出HTTP请求后无需等待,继续请求资源,相比默认情况下节省时间,提升效率。
- Cookie和session的区别
Cookie主要是服务器发送到浏览器并保存在本地的一小块数据。
Session代表服务器和客户端的一次会话过程。存储在会话过程中的变量。
区别:
l 作用范围不同,cookie存在客户端,session存在服务器端
l 存取方式不同,cookie只能保存ASCLL码
l 有效期不同,cookie在浏览器上可以长期保存,session客户端关闭就被释放
l 存储大小不同
l 安全性,session更高
- 禁用cookie后如何使用session
Session的sessionID是通过cookie保存的,禁用后可以使用2种方案:
l 每次请求都携带sessionID的参数
l 使用token机制,响应返回一个token字符串,无需再次登录验证。
- 三次握手、四次释放过程
三次握手过程:
a) Client发起TCP请求连接,发送序号为x的TCP报文段,序号设置为SYN=1,seq=x
b) Server端处于监听状态,发现有人请求TCP连接,分配端口号,建立套机子,响应报文,SYN=1,ACK=1,seq=y,ack=x+1;
c) Client端接收到响应报文,进入到established状态,发送ACK=1,seq=x+1,ack=y+1的响应报文,表示自己接收到S端的响应报文。Server端接收到ack响应报文后,建立TCP连接,服务器从syn_rec接收同步状态切换为establish阶段。
为什么使用3次握手而不是2次?
这主要是防止已经丢弃的包仍能申请到tcp连接资源,例如有一个包申请TCP连接因为网络原因滞留被丢弃,在经过若干时间后,到达服务器端,利用2次握手会导致服务器端建立连接后,客户端并不知道,导致TCP连接资源一直被占用,也没人释放。
四次释放过程:
a) Client发起TCP连接释放请求,发送序号FIN=1,seq=u,进入到fin-wait1阶段(等待客户端响应确认),从establish到等待释放连接状态.
b) Serverduan 接收到连接释放报文后,发送ACK=1,seq=w,ack=u+1;此时还有数据要传输到client端,此时已处于半连接状态,服务器也进入到close-wait阶段。通知应用进程,应用进程在传输完缓存区里的数据后进行关闭阶段。客户端也进入到fin-wai2阶段(等待数据传输完毕),无需发送响应报文。
c) Server端等待进程发送完数据后,发送TCP报文,设置标志位为FIN=1,ACK=1,seq=v,ack=u+1,此时进入到last-ack状态阶段。
d) Client端接收到响应报文后,发送确认报文ACK=1,seq=u+1,ack=v+1,进入到time-wait阶段,等待2MSL后,进入到closed状态。
为什么要等待2MSL?为什么要使用4次释放?
MSL是最大报文寿命时间段,需要等待2MSL是防止极端情况下最后一次释放,如果因为网络阻塞原因没有到达目的主机,此时需要服务器端判定包丢失再次发送FIN报文,客户端再次接收到FIN包后发现丢失,重置时间段段,再次发送ACK确认报文段。
应用层 表示层 会话层 传输层 网络层 链路层 物理层
三次握手函数调用:
服务端监听连接:
Socket bind listen accept
客户端发起连接:
Socket connect
客户端调用
应用层 传输层 网络层 数据链路层
RPC框架
远程调用协议,调用远程服务,2个不同主机的应用调用。
- 解决同学,TCP连接
- 寻址问题,告知底层RPC框架如何连接到B服务器
- 序列化问题,参数序列化,二进制通过TCP发送
- 反序列化,找到对应方法本地调用。
DUBBO框架
- dubbo是二进制的传输,占用宽带会更少。
- springcloud 是http协议传输,带宽会比较多。
- dubbo开发难度较大
- springcloud注册中心可以选择ZK,REDIS,srping注册中心使用euraka.
七、意向公司
互联网大厂:
百度、腾讯、华为、美团、字节、拼多多
商汤、携程、阿里、小米、
银行:
中国银行、农业银行、工商银行、建设银行、国家开发银行
核心:
刷题和基础、自己的项目
每天5套面试题 2套编程题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?