为进阿里刷面试题-日更(加油打工人)

  • 下面的题目都是自己理解的,如有漏,或者写错的,请指出来!加油!
    • JAVA基础
      • 1、JVM内存分配原理,垃圾回收原理
      • 2、ConcurrentHashMap 和 hashMap,hashTable底层结构的区别
      • 3、多线程创建的几种方式,现场池的原理和你常使用的线程池的类
      • 4、synchronized、volatile 这两个关键字的作用和区别
      • 5、Spring AOP底层实现的原理,动态代理
      • 6、设计模式,重点问下动态代理模式,职责链模式
      • 7、Spring IOC类加载的顺序
      • 8.HashMap和LinkedHashMap运用场景
      • 9.hashCode和equals什么时候需要重写
      • 10.list排序原理
    • 微服务/分布式框架
      • 讲下使用的微服务框架是什么(spring-cloud 或者 dubbo),讲下微服务框架常见的组件和作用
      • 分布式锁和分布式事务如何处理
      • Redis 是否可以持久化,Redis 集群扩容怎么做
      • 消息队列常用的是那些(MQ),消息补偿的机制是怎么做的
      • redis为什么快
      • redis集群和原理,主从同步原理,rehash
      • 网络io,epoll为什么快
      • RPC调用流程
      • cpu飙升排查
      • 状态码区别
      • 设计一个简易秒杀系统思路
    • 数据库
      • 1、mysql 查询去重如何做
      • 2、mysql select * from table for update 是什么意思(行锁)
      • 3、mysql union 和 union all 这两个关键字的区别是什么
      • 4、SQL优化的一般步骤是什么,怎么看执行计划(explain关键字),如何理解其中各个字段的含义。
      • 5、MYsql的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。
      • 事务和实现机制,binlog,redo undolog
      • 磁盘顺序写和非顺序写,随机读原理
    • 算法
      • 1.使用递归及非递归两种方式实现快速排序
      • 2.二叉树的层序遍历
      • 3.常用的排序方式有哪些,时间复杂度是多少?
      • 4.如何随机生成不重复的 10 个 100 以内的数字?
      • 5. 470. 用 Rand7() 实现 Rand10()
      • 6.LRU实现
      • 236. 二叉树的最近公共祖先
    • 网络协议
      • http 协议和 RPC 协议的区别
      • http 协议 方法 POST,GET,PUT DELETE 4中方法的区别
      • 简述 TCP 三次握手以及四次挥手的流程。为什么需要三次握手以及四次挥手?
      • HTTP 与 HTTPS 有哪些区别?
      • TCP 与 UDP 在网络协议中的哪一层,他们之间有什么区别?
      • 从URL输入到页面展现的过程
      • https过程
      • 线程和进程

下面的题目都是自己理解的,如有漏,或者写错的,请指出来!加油!

JAVA基础

1、JVM内存分配原理,垃圾回收原理


其中方法区和堆是所有线程共享的,栈,本地方法栈和程序虚拟机则为线程私有的。

在jvm运行结构中,有堆,方法区,本地方法栈,程序计数器,栈区
:所有通过new创建的对象的内存都在堆中分配,这里是主要进行垃圾回收的地方。
(jdk1.7分为新生代,老年代和永久代
jdk1.8分为新生代,老年代和元空间
元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制)
方法区:用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中

本地方法栈:存放native c++的方法供调用
程序计数器:线程私有,记录指令的地址,保证程序(在操作系统中理解为进程)能够连续地执行下去
栈:主管Java程序的运行,它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回。


垃圾回收是因为程序运行期间会创建对象,对象占内存,总有一天会内存不够,所以需要垃圾回收来管理不用的对象
垃圾回收根据对象不同的生命周期,划分了不同的内存空间,便于管理和回收

jvm分为新生代和老年代,新生代和老年代的大小比例为1:2. 新生代又分为伊甸园区和幸存区,幸存和伊甸园区的内存比例是1:1:8.
1.对象创建的时候会放在伊甸园区,慢慢的伊甸园内存会不够,此时会进行youngGC,此时不被根节点引用的对象会被销毁,其他的会放到幸存区0中,加载新对象到伊甸园区
2.当幸存区0满时,会进行垃圾回收,此时会把幸存的伊甸园区和幸存区0的对象,放到幸存区1中,然后把两个幸存区交换位置。即保持幸存区1是空着的。
3.当伊甸园区和幸存区0的对象不足以放到幸存区1的时候,会进入到老年代,每经历过一次GC的对象年龄会加1.当15GC后还存活的对象会去老年代
4.在养老区,相对悠闲。当养老区内存不足时,再次触发GC:Major GC,进行养老区的内存清理。
5.若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常

什么样的对象会被标记成垃圾
当一个对象已经不再被任何的存活对象继续引用时
1.引用计数算法
引用计数器属性。用于记录对象被引用的情况。被引用++,引用失效–
无法解决循环引用问题 也就是A引用B,B引用A

2.可达性分析算法
将对象及其引用关系看作一个图,选定活动的对象作为 GC Roots,然后跟踪引用链条,如果一个对象和GC Roots之间不可达,也就是不存在引用链条,那么即可认为是可回收对象。
Roots有哪些?
虚拟机栈中引用的对象
比如:各个线程被调用的方法中使用到的参数、局部变量等。
本地方法栈内JNI(通常说的本地方法)引用的对象
类静态属性引用的对象
比如:Java类的引用类型静态变量
方法区中常量引用的对象
比如:字符串常量池(String Table)里的引用
所有被同步锁synchronized持有的对象
Java虚拟机内部的引用。
基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutOfMemoryError),系统类加载器。
反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存

老年代和新生代用了什么算法,为什么
新生代:复制算法

老年代:标记整理算法
标记阶段和标记清理算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象

2、ConcurrentHashMap 和 hashMap,hashTable底层结构的区别

hashMap是数组加链表的结构,对数进行hash操作,然后对数组长度取模后放入对应bucket中。hashMap存放的时候允许null值,但是是线程不安全的
hashTable在方法上加了synchronized,只允许单线程进行执行操作,相对来说执行效率没有hashMap,但是能保证线程安全。
concurrentHashMap是在hashMap的基础上,加上了cas和synchronized,来实现线程安全,并且可以支持并发处理,使用分段锁的机制,每个线程负责一块区域,在进行操作的时候效率更高。

在存放数据的时候会先把数据高位与低位异或操作,计算出hashcode,然后除以数组长度,放入对应的bucket(桶)中,如果当前位置没有值直接放入,如果有值用equal比较值,如果相等覆盖,不相等用拉链法往后遍历,放到最后一个位置

扩容因子0.75 初始化capacity16,允许存放的最大threshold是12,到这个阈值后会扩容。
扩容的话会计算原始容量和当前元素的 按位与,如果为1放高位,位移的时候加上原数组长度。如果为0放低位,放原来位置。

3、多线程创建的几种方式,现场池的原理和你常使用的线程池的类

1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法
3.通过Callable和Future接口创建线程

线程池工作原理:
poolSize:

线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。

corePoolSize:

线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程

maximumPoolSize:

线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。

1.在创建线程池后,等待提交过来的请求

2.当调用execute()方法添加一个请求任务时,线程会做如下判断:

2.1如果正在运行的线程(poolSize)数量小于corePoolSize,那么会创建线程执行这个任务

2.2如果当前运行的线程数量(poolSize)大于或者等于了corePoolSize,那么会把任务放入队列

2.3如果队列满了,且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程运行这个任务

2.4如果队列满了,且正在运行的线程数量大于maximumPoolSize,启动饱和拒绝策略

3.当一个线程完成任务后,它会从队列中取下一个任务来执行

4.当一个线程无事可做一定时间(KeepAliveTime)时,线程池会判断

4.1如果当前运行线程大于corePoolSize,线程会被销毁

4.2 线程池的所有任务完成后,最终会维护大小为corePoolSize个线程数

总结:开始poolSize(线程池中的线程)小于corePoolSize(线程池的基本大小),就创建线程去执行任务,慢慢的,poolSize大于或者等于corePoolSize的时候,把任务加入队列,完成任务的线程去队列中拿任务执行。

如果还是来不及处理(队列满了),会另外创建小于maximumPoolSize的线程去执行任务,然后当任务被执行的差不多了,空闲的线程会被销毁,最终会维护大小为corePoolSize的线程个数

常用的线程池

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
生成固定线程数的线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
无限制的生成线程,但如果一个线程工作完成后会自动回收销毁,如果一个任务执行很慢,而且很多会导致线程泛滥不建议使用
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
单线程的线程池,所有的任务顺序执行,保证线程安全
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
可以延迟多少时间后执行任务

参考[https://www.jianshu.com/p/7ab4ae9443b9

](https://www.jianshu.com/p/7ab4ae9443b9)

4、synchronized、volatile 这两个关键字的作用和区别

(1)、原子性:是指线程的多个操作是一个整体,不能被分割,要么就不执行,要么就全部执行完,中间不能被打断。
(2)、可见性:是指线程之间的可见性,就是一个线程修改后的结果,其他的线程能够立马知道。
(3)、有序性:为了提高执行效率,java中的编译器和处理器可以对指令进行重新排序,重新排序会影响多线程并发的正确性,有序性就是要保证不进行重新排序(保证线程操作的执行顺序)。

synchronized是同步锁,保证多个线程间,同一时刻,只允许有一个线程执行同步代码块。相当于单线程操作(保证了线程的可见性,原子性和有序性)

volatile是保证可见性和有序性

可见性

比如修改共享变量有两步:

1.线程A修改了共享变量副本,并刷新到主内存中去

2.线程B读取主内存中被A刷新过的共享变量副本

如果一个变量未被volatile修饰,A线程修改了这个共享变量后,不一定会马上把修改结果刷新到主内存中,此时如果B线程去主内存读取的时候会读取到未被A修改的变量值

如果一个变量被volatile修饰,那么本次修改的结果会强制立刻刷新到主内存中,如果B去读取那么就是A修改过的值了

有序性

volatile禁止指令重排序,在指令优化时,在volatile变量之前的指令不能再volatile之后执行,volatile之后的指令也不能在volatile之前执行,保证了有序性

1.volatile只能修饰变量,使用范围小。synchronized可以用在变量,方法,类,同步代码块等,返回打

2.volatile只能保证可见性和有序性,不能保证原子性,而synchronize都可以保证

3.volatile不会造成线程阻塞。synchronize可能会造成线程阻塞

5、Spring AOP底层实现的原理,动态代理

AOP是面向切面编程,无侵入式的在原来功能的基础上增加自定义的功能。

基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强
切面:需要代理一些方法和增强代码

实现AOP的话3步走:

1.告诉spring目标类是哪个

2.增强的类是谁

3.增强目标的哪个方法,怎么增强(前,后,环绕)

动态代理:通过java的反射,在程序运行期间生成新的代理对象,并通过代理对象去操作控制

JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 (必须有接口)
Cglib的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理

https://www.jianshu.com/p/70a66e4c13d4

6、设计模式,重点问下动态代理模式,职责链模式

动态代理
为其对象提供一个代理以控制某个对象的访问。代理类为被代理类处理,过滤消息,并将消息转发给代理类,之后还能进行消息后续的处理。代理类本身不实现业务,而是通过调用被代理类的方法来提供服务

实现方法
JDK动态代理,针对目标对象的接口进行代理 ,动态生成接口的实现类 (必须有接口)
Cglib的引入为了解决类的直接代理问题(生成代理子类),不需要接口也可以代理

1.通过实现 InvocationHandler 接口创建自己的调用处理器;
2.通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
3.通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
4.通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

职责链模式
用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际的业务需求传递给下一个节点继续处理或者返回处理完毕

1.责任链重在责任分离处理,每个节点各司其职
2.每个节点都有机会处理请求,也可能不处理请求
3.责任链比较长,调试时可能会比较麻烦
4.责任链一般用于处理流程节点之类的实际业务场景中
5.spring拦截器链,servlet过滤器链都采用责任链设计模式

https://www.jianshu.com/p/9f7d9775bdda

7、Spring IOC类加载的顺序

配置文件的加载、解析、注册

初始化:
1、容器环境的初始化
2、Bean工厂的初始化(IoC容器启动首先会销毁旧工厂、旧Bean、创建新的工厂)
读取:通过BeanDefinitonReader读取我们项目中的配置(application.xml)
定义:通过解析xml文件内容,将里面的Bean解析成BeanDefinition(未实例化、未初始化),并放入beanFactroy中

8.HashMap和LinkedHashMap运用场景

hashmap根据计算出来的hashcode来确定对应数据存放的位置,如果哈希冲突会使用链表,把值往链表中存储,是单向链表,只能往后遍历。根据键可以直接获取值,访问速度很快。
linkedhashmap和hashmap类似,只是链表变成了双向链表,双向链表增加了成本,但是保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的;

LRU算法,可以使用LinkedHashMap实现

class LRUCache extends LinkedHashMap<Integer, Integer>{private int capacity;public LRUCache(int capacity) {super(capacity, 0.75F, true);this.capacity = capacity;}public int get(int key) {return super.getOrDefault(key, -1);}// 这个可不写public void put(int key, int value) {super.put(key, value);}@Overrideprotected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {return size() > capacity; }
}

9.hashCode和equals什么时候需要重写

hashCode是根据一个值调用c++的算法计算出一个值。
方法如果不重写equals,调用是==,比较的是对象的的地址值,如果是数值没事。

如果user这个对象,name的值都是张三,怎么判断是同一个人呢?
如果不重写hashcode和equals方法的话,首先两个对象因为传值相同,计算出来的hashcode是一样的,但是如果调用equals方法,会比较地址值,那肯定不相等。这里需要重写equals方法,让他们的名字进行比较,这样两个name一致的对象就被人物是同一个人了。

string已经自己重写了equals方法,所以字符串比较可以用equals

hashcode重写为了比较集合类或者对象比较,它们内部的值是否一致(顺序和大小)

hashCode是给java集合类的一些动作提供支撑,来判断俩对象“是否是同一个”的标准
equals是给你编码时判断用的,所以,这俩必须保持一致的逻辑。

10.list排序原理

归并排序
时间复杂度O(n log n)
空间复杂度T(n)

归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

插入排序
时间复杂度O(N^(1-2))
空间复杂度O(1)
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置(两两比较),使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序

Timesort
TimSort算法是一种起源于归并排序和插入排序的混合排序算法,设计初衷是为了在真实世界中的各种数据中可以有较好的性能。基本工作过程是:
1.扫描数组,确定其中的单调上升段和严格单调下降段,将严格下降段反转;
2.定义最小基本片段长度,短于此的单调片段通过插入排序集中为长于此的段;
3.反复归并一些相邻片段,过程中避免归并长度相差很大的片段,直至整个排序完成,所用分段选择策略可以保证O(n log n)时间复杂性。 可以看到,原则上TimSort是归并排序,但小片段的合并中用了插入排序。

https://mp.weixin.qq.com/s?__biz=MzI2MTY0OTg2Nw==&mid=2247483816&idx=1&sn=079af3d70efcb68efa7400f09decb59c&chksm=ea56650cdd21ec1ace7c8fd168d62feb636e4b32f9a4d90329fe479489d8e7a70e612df8920b&token=2074049324&lang=zh_CN#rd

微服务/分布式框架

讲下使用的微服务框架是什么(spring-cloud 或者 dubbo),讲下微服务框架常见的组件和作用

微服务是一种架构风格,一个大型复杂软件应用由多个微服务组成.系统中的微服务可被独立部署,各微服务直接是松耦合的.每个微服务区仅关注于完成一件任务.

调用方式方面:
Dubbo采用单一长连接和NIO异步通信(保持连接/轮询处理),并且序列化使用定制的Hessian2框架,适合于小数据量并发的服务调用.
SpringCloud采用HTTP协议,效率没有这么高

Dubbo
是分布式服务架构,用于远程服务调用

服务容器负责启动,加载,运行服务提供者
服务提供者在启动的时候,向注册中心注册自己提高的服务
服务消费者在启动时,向注册中心订阅自己所需的服务
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心(根据数据可以动态调整权重)

特点
远程通信:提供多种基于长连接的NIO框架抽象封装(非阻塞I/O的通信方式,Netty),多线程模型,序列化(Hessian2/ProtoBuf),以及 请求-响应 模式的信息交换
集群容错:提供基于接口方法的透明远程过程调用(RPC),包括多协议支持,软负载均衡,失败容错,地址路由,动态配置等集群支持
自动发现:基于注册中心,使服务消费者能动态的查找服务器提供方,使地址透明,使服务提供方可以平滑扩缩容。

Spring Cloud
基于Springboot,为微服务体系开发中的架构问题,提供一整套的解决方案–服务注册与发现,服务消费,服务保护与熔断,网关,分布式链路追踪,分布式配置管理等

核心技术
分布式/版本化配置
服务注册与发现
路由
服务之间调用
负载均衡
熔断器
分布式消息传递

流程
通过gateway网关来访问内部服务
网关收到请求后,从注册中心(nacous)获取可用服务
由Ribbon(网关集成了)进行负载均衡,分发到服务器
微服务之间通过Feign进行通信
Sentinel负责处理服务超时熔断
Seata进行分布式事务的处理
Skywalking进行链路调用的查看
apolle来实现动态配置

特点
有强大的Spring社区,Netflix等公司支持,而且开源社区活跃
标准化的将微服务的成熟产品和框架整合在一起,提供整套的微服务解决方案,开发成本较低,且风险小
基于SpringBoot,简单配置,快速开发,轻松部署和测试
支持REST服务调用,相比于RPC,更加轻量和灵活,有领域跨语言服务的实现,结合Wsagger使得服务文档一体化
提供docker及Kubernetes微服务编排支持

Dubbo 专注 RPC 和服务治理,Spring Cloud 则是一个微服务架构生态
ZK是CP

Dubbo构建就像是组装电脑,各环节自由度很高,但是最终结果可能因为一个环境不行就整个系统不可用了.总让人不放心.
SpringCloud就是品牌机,拥有足够高的稳定性

https://www.jianshu.com/p/41294802dbf8

分布式锁和分布式事务如何处理

在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下,没有任何问题。

•但当我们的应用是分布式集群工作的情况下,属于多JVM下的工作环境,跨JVM之间已经无法通过多线程的锁解决同步问题。

分布式锁需要满足几个特点:
1.互斥性
2.锁超时,支持自动释放,防止死锁
3.正确,高效,高可用:解铃还须系铃人(加锁和解锁必须是用一个线程)。加锁和解锁必须要高效,具备一定的容错性
4.可重入,如果一个线程拿到锁后继续去获取锁还是能够拿到(方法的递归调用)
5.阻塞/非阻塞:如果获取不到直接返回为非阻塞,如果获取不到等待锁的释放叫做阻塞
6.公平/非公平

实现方式有三种
1.数据库(唯一索引)
2.redis
3.zk

数据库:唯一索引具有互斥性,同一时刻只能允许一个竞争者获取锁.加锁时我们在数据库中插入一条锁记录,利用业务id防止重复.当一个对象获取到锁后,第二个竞争者再来加锁会抛出唯一索引冲突,然后抛出异常,就判定当前竞争者加锁失败.防重业务id需要自己来定义,如果对象是一个方法,则防重的id是这个方法的名字.如果是对象,则是类名

redis:基于缓存来实现效率最高,加锁速度最快
使用Redisson.getLock(lockey). lock.lock(). lock.unlock()实现
Redission
1.使用lua脚本,实现了setnx和expire的原子性操作
2.使用hash类型 key存储锁key field 存储请求id value存储重入次数 也是用lua脚本保证了原子性
3.加入了守护线程(看门狗)定时/延时判断拿到锁的现场是否还继续持有锁,如果有则帮忙续期
4.客户端会有发布/订阅方案,一旦锁被释放,就会得到信号通知,继续尝试获取锁
5.解锁的话会先把重入的次数解锁完,如果field归0或者不存在会向redisson_lock_channel屏到发布释放消息

Zookeeper:当客户要获取锁,则创建节点,使用完锁,则删除该节点
1.客户端获取锁时,在lock节点创建临时顺序节点
2.然后获取lock下面所有的子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么任务该客户获取到了锁.使用完锁后,删除锁
3.如果发现自己创建的节点非lock所有子节点中最小的,说明还没获取到锁,此时客户端找到比自己小的那个节点,对其注册事件监听,监听删除事件
4.如果发现比自己小的节点被删除了,则客户端的watcher会收到相应通知,同时再判断自己创建的节点是否是lock子节点中序号最小的,如果是则获取到了,如果不是重复上面的步骤

分布式事务
原子性:一个事物内的操作要么都执行,要么都不执行
一致性:数据满足完整性约束的,不会存在中间状态的数据。比如你账上有400,我账上有100,你给我打200块,此时你账上的钱应该是200,我账上的钱应该是300,不会存在我账上钱加了,你账上钱没扣的中间状态。
隔离性:多个事物并发执行互不干扰
持久性:事务完成后数据被永久保存。

2PC
二阶段提交,引入一个事物协调者来协调管理各参与者的提交和回滚。准备(投票)和提交两个阶段
保证强一执行的分布式事务,是同步阻塞协议,效率低,存在单点故障,极端情况下会存在数据不一致

3PC
相比于上面引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一个字的状态
准备阶段,预提交阶段和提交阶段
准备阶段协调者会询问参与者的自身壮阔,如果现在还好吗?负载重不重这种
准备阶段的变更不回直接执行事务,而是先去询问此时的参与者是否有条件接这个事务,不会一上来就干活锁资源,使得某些资源不可用的情况下所有参与者都阻塞者。
预提交的引入起到了一个统一状态的作用,多了一次询问,性能会差些。
引入超时机制后,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段大概率是提交的,如果是等待预提交超时,那么该干啥就干啥,本来就啥也没干
超时机制会带来数据不一致,比如等待提交命令时超时了,参与者默认执行提交事务操作,但有可能执行的是回滚操作,这样一来数据就不一致了

相比于2PC,通过预提交阶段可以减少故障恢复时候的复杂性,但不能保证数据一致。
综上所述,两个PC都不能保证数据100%一致性,因此一般需要有定时扫描补偿机制

TCC
TCC是业务层面的分布式事务.Try-Confirm-Cancel
Try指的是预留,即资源的预定和锁定
Confirm指的是确认操作,这一步是真正的执行
Cancel指的是撤销操作,把预留阶段的动作撤销了

思想上来说和2PC差不多,都是先试探性地执行,如果都可以那就真正的执行,不行就回滚。
TCC对业务的侵入性较大和业务紧耦合,这个适用范围更大,但是开发量也更大,毕竟都是在业务上实现的。

本地消息表
利用各系统本地的事务来实现分布式事务
本地消息顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。
然后再去调用下一个操作,如果下一个操作调用成功了,消息表的消息状态可以直接改成已成功
如果调用失败也没事,会有后台定时任务去读区本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。
有时候会调用不成功,会有重试,重试需要保证幂等性,并且一般有最大重试次数,超过最大次数了记录下报警人工处理
最终一致性,容忍数据暂时不一致的情况

消息事务
基于mq来实现
第一步先给给服务器发送事务(异步,消费者不可见),然后发送成功后,发送方再执行本地事务
第二步根据本地事务的结果向服务器发送Commit或者RollBack命令
并且mq发送方提供一个反查事务状态借口,如果一段时间内消息没有收到任务操作请求,那么服务器会通过得知发送方事务是否执行成功,然后再执行Commit或者RollBack操作
最终一致性

最大努力通知
调用失败的多次重试,表面一种柔性事务的思想:我已经尽我最大的努力想达成事务的最终一致了

解决方案:
使用Seata
在启动类上加入@EnableAutoDataSourceProxy
然后需要控制的方法上加上@GlobalTransactional

https://zhuanlan.zhihu.com/p/183753774

Redis 是否可以持久化,Redis 集群扩容怎么做

RDB和AOF
RDB
优点
1.RDB是一个紧凑压缩的二进制文件,存储效率高
2.RDB内部存储的是某个时间节点redis的数据快照,非常适合用于数据备份和全量复制
3.恢复速度比AOF快
缺点
1.无法做到实时持久化,
2.bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
3.众多版本RDB文件格式不统一

AOF
优点
1.记录每次写指令,重启时再重新执行AOF文件中命令达到恢复数据的目的
2.可配置每秒,数据准确性较高,性能高
3.会有重写机制,会压缩数据,存储速度快

选择:
对数据非常敏感建议使用AOF,默认策略时everysecond,每秒一次,最多丢失0-1秒内的数据
但是AOF文件存储体积较大,且恢复速度较慢

集群扩容:
redis集群采用的是集中式,而不是分布式的。redis实例都是全量存储

分布式存储:数据分区存储,按照一定的规则,将全量的数据划分到不同的节点上,每个节点上存储全量数据的一个子集

redis cluster(查询路由方案)

客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
redis定义了16384个槽(slot),编号范围0-16383;数据key来了之后首先经过一个哈希函数算出一个值,这个值会映射到一个slot里。

虚拟槽分区 巧妙地使用了 哈希空间,使用 分散度良好 的 哈希函数 把所有数据 映射 到一个 固定范围 的 整数集合 中,整数定义为 槽(slot)。这个范围一般 远远大于 节点数,比如 Redis Cluster 槽范围是 0 ~ 16383。槽 是集群内 数据管理 和 迁移 的 基本单位。采用 大范围槽 的主要目的是为了方便 数据拆分 和 集群扩展。每个节点会负责 一定数量的槽,如图所示:

这种结构很容易 添加 或者 删除 节点。如果 增加 一个节点 6,就需要从节点 1 ~ 5 获得部分 槽 分配到节点 6 上(1-5的分一部分给6)。如果想 移除 节点 1,需要将节点 1 中的 槽 移到节点 2 ~ 5 上(1给到2-5),然后将 没有任何槽 的节点 1 从集群中 移除 即可。

集群后会有些限制
key批量操作 支持有限。
类似 mset、mget 操作,目前只支持对具有相同 slot 值的 key 执行 批量操作。对于 映射为不同 slot 值的 key 由于执行 mget、mget 等操作可能存在于多个节点上,因此不被支持。

key事务操作 支持有限。
只支持 多 key 在 同一节点上 的 事务操作,当多个 key 分布在 不同 的节点上时 无法 使用事务功能。

key 作为 数据分区 的最小粒度
不能将一个 大的键值 对象如 hash、list 等映射到 不同的节点。

不支持 多数据库空间
单机 下的 Redis 可以支持 16 个数据库(db0 ~ db15),集群模式 下只能使用 一个 数据库空间,即 db0。

复制结构 只支持一层
从节点 只能复制 主节点,不支持 嵌套树状复制 结构。

http://redis.cn/topics/partitioning.html
https://zhuanlan.zhihu.com/p/44537690

消息队列常用的是那些(MQ),消息补偿的机制是怎么做的

就是一旦某个操作发生了异常,如何通过内部机制将这个异常产生的「不一致」状态消除掉。

基于本地消息的最终一致性方案的最核心做法就是在执行业务操作的时候,记录一条消息数据到DB,并且消息数据的记录与业务数据的记录必须在同一个事务内完成,这是该方案的前提核心保障

独立消息服务最终一致性:将消息的存储单独做成一个RPC服务,模拟了事务消息的消息预发送过程,如果预发送消息失败,那么生产者业务就不会去执行。
消息服务器中,还有个定时任务,会定期轮训长时间处于待发送状态的消息,通过一个check补偿机制来确认该消息对应的业务是否成功,如果成功消息修改为可发送,然后将其投递给MQ;如果业务处理失败,则将对应的消息更新或者删除即可
通过MQ自身重发机制来保证消息被消费
https://www.jianshu.com/p/eb571e4065ec

redis为什么快

1.基于内存访问(高效的数据结构 简单动态字符串 双向链表 压缩列表 哈希表 跳跃表 整数数组)
2.使用单线程进行操作,避免了多线程上下文切换和竞争
3.IO多路复用(又称事件驱动)
select/epoll
操作系统为你提供了一个功能,当你的某个socket可读或者可写的时候,它可以给你一个通知。这样当配合非阻塞的socket使用时,只有当系统通知我哪个描述符可读了,我才去执行read操作,可以保证每次read都能读到有效数据而不做纯返回-1和EAGAIN的无用功。

redis集群和原理,主从同步原理,rehash

集群和原理

1.客户端通过hash算法,将redis数据的key进行散列

2.通过代理,客户端请求代理,代理解析客户端的数据后将请求转发到正确的节点

3.查询路由Redis Cluster方式,客户端随机请求一个redis实例,由redis将请求转发给正确的节点

优点:

无中心节点,按槽来存储存储数据,可以平滑的扩缩容,自动故障转移。

哈希,一个值计算出来后,对节点数取模,然后映射到对应节点。节点数变化,会导致数据重新迁移

一致哈希,所有存储节点首尾相接,每个key在计算后顺时针放到邻接的存储节点上,有节点新增或者删除,只会影响顺时针相邻的节点

虚拟槽,总共0-16383.每个结点分配部分槽数,key经过计算放到对应槽中。

优点:数据与节点之间解耦,节点自身维护槽的映射关系。

主从同步原理

3个阶段:建立链接阶段,主库同步数据到从库阶段,发送同步期间新写命令到从库阶段

全量同步

1.建立链接

从库会和主库建立链接,从库执行replicaof并发送psync命令并告诉主库即将进行同步,主库确认恢复后,主从库间开始同步

主库接收到psync(同步)命令后,用FULLRESYNC(全量同步)包含主库的runID和复制进度offset,返回给从库

2.主库同步数据给从库

主库执行bgsave命令生成RDB文件,并将文件发送给从库,同时主库为每个slave开辟一块replication buffer缓冲区,记录从生成RDB文件开始到接受到的所有写命令

从库拿到RDB文件后,保存到磁盘并且清空当前数据库的数据,再加载RDB文件到内存中

3.发送新写命令到从库

从节点加载RDB完成后,主节点将replication buffer缓冲区的数据发送到从节点,从节点接受并执行,从节点同步到和主节点相同的状态

master先把数据写到buffer中,然后通过网络发出去,这样完成了交互

buffer专门用来传播写命令,保证主从一致 叫replication buffer

RDB是二进制文件,会压缩,网络传输和写入磁盘,恢复效率更好

增量同步:用于网络终端等情况后的复制,只将终端期间主节点执行的写命令发送到从节点,比全量更高效

repl_backlog_buffer缓冲区,,master会将写指令操作记录在repl_backlog_buffer中,因为内存有限,是个定长的环形数组,如果满了,从头开始覆盖前面的内容

master使用master_repl_offset记录自己写到的位置偏移量,slave使用slave_repl_offset记录已经读取到的偏移量

网络断开链接后,slave会先发送psync命令给master,同时将自己的runID和slave_repl_offset发送给master,master只需要把master_repl_offset和slave_repl_offset之间的命令同步给主从库即可

后续基于长连接的方式保持同步,同时主从维持着心跳机制:PING和REPLCONF ACK

总的来说,replication buffer是主从进行全量复制时,主库用于和从库链接的客户端的buffer,repl_backlog_buffer是为了支持从库增量复制,主库上用于持续保存写操作的一块专用buffer

哨兵工作原理

1.每隔10s,每个sentinel节点向master和slave发送info命令来获取最新的拓扑结构

感知从节点,维护正确拓扑结构,更新主从的完整信息

2.每隔2s,每个sentinel节点都会向redis数据节点的_sentinel_:hello频道去发布消息,其他sentinel节点也都会订阅该频道

发布订阅,相互订阅,发送对节点的看法和自身的信息

3.每隔1s,每隔sentinel节点要向其他节点(主节点,从节点,其他sentinel),发送ping命令做心跳检测

判断服务是否可达

rehash

扩展或者收缩哈希表时需要将ht[0]里面的所有的键值对rehash到ht[1]里面,分多次,渐进式地完成。因为键值对数量很大一次性rehash到ht[1],可能会导致服务器一段时间内停止服务区(单线程)

(1)为ht[1]分配空间,为dict字典同时持有ht[0]和ht[1]两个哈希表

(2)在字典值中维持一个索引计数器遍历rehashidx,将值设置为0,表示rehash工作正式开始

(3)在rehash期间,每次对字典执行增删改查时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1]上,当rehash工作完成后,将rehashidex++,表示下一次要迁移链表所在桶的位置

(4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有桶对应的键值对都会被rehash到ht[1],此时程序会把rehashidx属性的值设为-1,表示rehash操作已完成

(5)完成后释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备

添加操作,会把数据插入ht[1]

查询操作,会现在ht[0]上进行查找,如果没有找到在ht[1]进行查找

删除,ht[0],ht[1]都要删除

网络io,epoll为什么快

io

阻塞io:下达指令后,一直等待指令的回应结果,期间无法做其他事情

注册socket->绑定端口和socket->监听->阻塞等待->连接->接受->返回数据->while(true)

非阻塞io:下达指令后,可以做其他工作,期间轮询,如果收到指令完成的通知,再去处理指令结果

注册socket->绑定端口和socket->监听->设备非阻塞(nonblock)可以先左其他事情,过会自己主动去问(轮询)->连接->接受->返回数据

IO多路复用:找内核帮忙监视,期间可以做其他事情

创建epoll对象->创建监听树(红黑树),创建就绪队列(双向链表)->创建socket->绑定端口和socket->监听->设置非阻塞->添加描述符到监听树->阻塞等待注册的时间发送(轮询)->连接->处理分发->增删改查连接

select 轮询,遍历所有fd 采用标注为存储

poll 轮询遍历,查询每个fd的状态 采用链表存储文件描述符

epoll epoll_wait观察就绪链表中有无数据即可,最后将链表的数据返回给数组并返回给就绪的数量

https://blog.csdn.net/m0_46690280/article/details/114668799?spm=1001.2014.3001.5501

RPC调用流程

客户端(Client): 服务的调用方。

服务端(Server):真正的服务提供者。

客户端存根:存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。

服务端存根:接收客户端发送过来的消息,将消息解包,并调用本地的方法。

具体调用过程:

服务消费者(client客户端)通过本地调用的方式调用服务。
客户端存根(client stub)接收到请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
客户端存根(client stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
服务端存根(server stub)收到消息后进行解码(反序列化操作)。
服务端存根(server stub)根据解码结果调用本地的服务进行相关处理。
本地服务执行具体业务逻辑并将处理结果返回给服务端存根(server stub)。
服务端存根(server stub)将返回结果重新打包成消息(序列化)并通过网络发送至消费方。
客户端存根(client stub)接收到消息,并进行解码(反序列化)。
服务消费方得到最终结果。

动态代理
生成Client Stub(客户端存根)和Server Stub(服务端存根)的时候需要用到java动态代理技术。
序列化 在网络中,所有的数据都将会被转化为字节进行传送,需要对这些参数进行序列化和反序列化操作。
目前主流高效的开源序列化框架有Kryo、fastjson、Hessian、Protobuf等。
NIO通信
Java 提供了 NIO 的解决方案,Java 7 也提供了更优秀的 NIO.2 支持。可以采用Netty或者mina框架来解决NIO数据传输的问题。开源的RPC框架Dubbo就是采用NIO通信,集成支持netty、mina、grizzly。
服务注册中心
通过注册中心,让客户端连接调用服务端所发布的服务。主流的注册中心组件:Redis、
Zookeeper、Consul 、Etcd。Dubbo采用的是ZooKeeper提供服务注册与发现功能。
负载均衡
在高并发的场景下,需要多个节点或集群来提升整体吞吐能力。
健康检查
健康检查包括,客户端心跳和服务端主动探测两种方式。

cpu飙升排查

1.使用top找到当前消耗cpu最多的程序的pid,top默认是按照cpu利用率desc排序
2.使用top找到当前消耗cpu最多的线程(top -Hp 1139) ,查看java进程下所有线程的CPU占用情况
3.执行 printf “%x\n” pid 把线程号pid转换成16进制
4.执行 jstack 进程号|grep 线程id 可以查看具体cpu消耗在哪
5.执行 jstat -gcutil 进程号 统计间隔时间 统计次数 ,查看某个线程GC持续变化情况,如果发现返回FGC很大,而且一直增大确认Full GC。 也可以使用jmap -heap 进程ID,查看进程的堆内信息
6.jmap -dump:format=b,file=filename 进程ID,导出某进程下内存heap输出到文件

分析
1.看具体内存对象占用情况,看是生成了大量的对象导致内存溢出,还是手动调用了System,gc()
2.jstack可以直接定位到代码行,看是否有bug,或者无限循环,递归等等
3.死锁的情况,会出现关键字deadlock
4.访问接口有阻塞性的操作,导致调用整体比较耗时。先找到该接口,压测访问

状态码区别

状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。注意,状态码是服务端向客户端发送的反映响应结果的标识码

100-101 Informational(信息提示性) 接受的请求正在处理 100-199
200-206 Success(请求成功) 请求正常处理完毕 200-299
300-305 Redirection(重定向) 需要进行附加操作以完成请求 300-399
400-415 Client Error(客户端错误) 服务器无法处理请求 400-499
500-505 Server Error(服务器错误) 服务器处理请求出错 500-599

https://zhuanlan.zhihu.com/p/68184454

设计一个简易秒杀系统思路

什么是秒杀?
短时间(瞬时),大量请求,买一个(数量少)商品
解决:高并发(读,写)

目标:
稳:高可用,系统文档的提供服务
准:超卖。数据一致性问题
快:高性能(优化点)

架构原则
数据要少:请求参数和相应参数要少,降低对网络带宽的占用,降低对cpu的消耗,对IO数据库(非必要的信息不要来回传)
请求数要少:合并请求
路径要短:每个节点可靠性99%,5个节点95%
依赖要少:花里胡哨(页面渲染等)。优先等级高的展示,低的去掉
不要单点:保证高可用:负载均衡,水平扩展(reolicates:3)

动静分离
数据分区:url,用户,浏览的时间,地域,cookie(缓存信息)->静态数据,cdn,负载均衡
商品详情的缓存 url1(商品详情),url1:详情(json)
放到离用户最近的地区。cdn(浏览器缓存,map,redis,文件缓存等)
根据不同的用户类型做缓存(vip,普通用户)

热点数据
缓存动态数据
识别热点
监控,追踪。根据用户访问,添加购物车,下单,统计分析(经验)。
发现热点数据:分析日志,添加队列,系统订阅。

处理热点数据
lru,业务,系统,数据隔离(互不影响),加分布式锁

1.服务单一职责,哪怕秒杀挂了,不影响其他的服务

2.Redis集群主从同步读写分离,我们还搞点哨兵,开启持久化

3.Nginx转发

4.恶意请求拦截,ip或者账号限流

5.资源静态化

6.按钮控制,还没开始先置灰

7.限流:

限流这里我觉得应该分为前端限流后端限流

前端限流:这个很简单,一般秒杀不会让你一直点的,一般都是点击一下或者两下然后几秒之后才可以继续点击,这也是保护服务器的一种手段。

后端限流:秒杀的时候肯定是涉及到后续的订单生成和支付等操作,但是都只是成功的幸运儿才会走到那一步,那一旦100个产品卖光了,return了一个false,前端直接秒杀结束,然后你后端也关闭后续无效请求的介入了。

8.提前把商品的库存加载到Redis中,lua脚本

9.限流&降级&熔断&隔离

10.mq削峰填谷

https://www.zhihu.com/question/54895548

数据库

1、mysql 查询去重如何做

SELECT DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>

1.distinct
2.group by

2、mysql select * from table for update 是什么意思(行锁)

for update 是在数据库中上锁用,在数据库行上加一个排它锁。当一个事务未完成的时候,其他事务只能读取不能写入或者更新

当选中的某一行是通过主键id选中的,那么是行级锁
如果通过其他的方式选中行,或者选中的条件不明确包含主键,这个时候会锁表。

3、mysql union 和 union all 这两个关键字的区别是什么

是进行两表结果集的合并操作

union是去重且排序,union all是不去重也不排序
1.对重复结果的处理:union在进行表连接后筛选掉重复的记录,union all不会
2.对排序的处理:union会按照字段的顺序进行排序,union all只是将两个结果合并
从效率来说,union all要比union快很多。

4、SQL优化的一般步骤是什么,怎么看执行计划(explain关键字),如何理解其中各个字段的含义。

–1.通过show status命令了解各种SQL的执行频率
比如InnoDB_rows_read:select查询返回行数,InnoDB_rows_inserted:insert查询返回行数
大概看下目前数据库主要以什么操作为主
通过查看Com_commit和Com_rollback可以了解事务提交回滚情况

–2.定位执行效率较低的SQL语句
show processlist命令查看当前MySQL在进行的线程 可以查看线程状态 是否锁表等 可以实时查看SQL的执行情况

–3.通过EXPLAIN分析较低SQL的执行计划

–4.通过show profile分析SQL
可以看到sql的执行过程中每一步的耗时多少

–5.通过trace分析优化器如何选择执行计划
–6.确定问题并采取相应的优化措施

explain

  • id:SELECT查询的标识符,每个SELECT都会自动分配一个唯一标识符
    id越大越先执行,如果一样从上往下执行

  • select_type:查询的类型
    区分普通查询、子查询、联合查询等复杂查询
    (1)SIMPLE
    简单的select查询,查询中不包含子查询或者UNION。
    (2)PRIMARY
    查询中若包含任何复杂的子查询,那么最外层的查询被标记为PRIMARY。
    (3)DERIVED
    在from子句中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中。
    (4)SUBQUERY
    在select或where子句中包含了子查询,该子查询被标记为SUBQUERY。
    (5)UNION
    若第二个select查询语句出现在UNION之后,则被标记为UNION。若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    (6)UNION RESULT
    从UNION表获取结果的SELECT。

  • table:哪一张表
    (1)<union a,b>:输出结果中编号为 a 的行与编号为 b 的行的结果集的并集。
    (2):输出结果中编号为 a 的行的结果集,derived 表示这是一个派生结果集,如 FROM 子句中的查询。
    (3):输出结果中编号为 a 的行的结果集,subquery 表示这是一个物化子查询。

  • partitions分配的分区

  • type:join类型
    system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。
    一般来说,得保证查询至少到达range级别,最好能达到ref。

(1)system
当表仅存在一行记录时(系统表),数据量很少,速度很快,这是一种很特殊的情况,不常见。
(2)const
当你的查询条件是一个主键或者唯一索引(UNION INDEX)并且值是常量的时候,查询速度非常快,因为只需要读一次表。
(3)eq_ref
除了system和const,性能最好的就是eq_ref了。唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
(4)ref
非唯一性索引扫描,返回匹配某个单独值的所有行。区别于eq_ref,ref表示使用除PRIMARY KEY 和UNIQUE index 之外的索引,即非唯一索引,查询的结果可能有多个。可以使用 = 运算符或者<=> 运算符
除了system和const,性能最好的就是eq_ref了。唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。

(5)fulltext

查询时使用 fulltext 索引。

(6)ref_or_null

对于某个字段既需要关联条件,也需要null 值的情况下。查询优化器会选择用ref_or_null 连接查询。
(7)index_merge

在查询过程中需要多个索引组合使用,通常出现在有or 关键字的sql 中。

(8)unique_subquery

该联接类型类似于index_subquery。子查询中的唯一索引。在某些in子查询里,用于替换eq_ref
(9)index_subquery

利用索引来关联子查询,不再全表扫描。用于非唯一索引,子查询可以返回重复值。类似于unique_subquery,但用于非唯一索引

(10)range

只检索给定范围的行,使用一个索引来选择行。key 列显示使用了哪个索引,一般就是在你的where 语句中出现
了between、<、>、in 等的查询,这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。

(11)index

sql语句使用了索引,但没有通过索引进行过滤,一般是使用了覆盖索引或者利用索引进行了排序分组。

index和ALL都是读全表,区别在于index是遍历索引树读取,ALL是从硬盘读取。index通常比ALL更快,因为索引文件通常比数据文件小。

(12)ALL

全表扫描,性能最差。

  • possible_type:可能选用的索引
  • key:确切使用的索引
    查询时若使用了覆盖索引,则该索引只出现在key字段中。
  • key_len:索引长度
    在不损失精确性的情况下,长度越短越好。
  • ref:哪个字段或常数与key一起被使用
    显示索引的哪一列被使用了,常见的取值有:const, func,null,字段名。

当使用常量等值查询,显示const,
当关联查询时,会显示相应关联表的关联字段
如果查询条件使用了表达式、函数,或者条件列发生内部隐式转换,可能显示为func
其他情况null

  • rows:一共扫描了多少行,估值
  • filtered:查询条件所过滤的数据的百分比
  • extra:额外的信息
    (1)Using filesort

说明mysql 会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL 中无法利用索引完成的排序操作称为“文件排序”。 通常这种情况是需要进行优化的。

(2)Using temporary

使了用临时表保存中间结果,MySQL 在对查询结果排序时使用临时表。常见于排序order by 和分组查询group by。
在使用group by 和 order by的时候,列的数量和顺序尽量和索引的一样。

(3)Using index
Using index 表示相应的select 操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,可以提高效率。

如果同时出现using where,表明索引被用来执行索引键值的查找。如果没有同时出现using where,表明索引只是用来读取数据而非利用索引执行查找。
(4)Using where

表示查询时有索引被用来进行where过滤。

(5)Using join buffer

查询时使用了连接缓存。

https://www.jianshu.com/p/9af06c15c20b

https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

5、MYsql的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。

索引原理:底层使用的是B+树
所有节点不存放数据,数据都保存在叶子节点
叶子节点有顺序访问指针
树不高,一般3层左右,磁盘的读写代价更低,页可以存放更多的数据
查询更稳定
有序,查询效率高

索引的类型有哪些

普通索引:对关键字没有要求。 name一个字段建索引 独立索引 name age 组合索引

唯一索引:要求关键字值不能重复,同时增加唯一约束。

主键索引:要求关键字不能重复,也不能为NULL。同时增加主键约束。

全文索引:关键字的来源不是所有字段的数据,而是从字段中提取的特别关键词。mysql支持不是太好,一般用第3方的搜索引擎来解决 elasticsearch工具完成搜索

组合索引(最左前缀):在联合索引的情况下,不需要索引的全部定义,只要满足最左前缀,就可以利用索引来加快查询速度。

如何创建合理的索引
1.选择唯一性索引

唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
2.为经常需要排序、分组和联合操作的字段建立索引

经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。

3.为常作为查询条件的字段建立索引

如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。

4.限制索引的数目

索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。
5.尽量使用数据量少的索引

如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。

6.尽量使用前缀来索引

如果索引字段的值很长,最好使用值的前缀来索引。
7.删除不再使用或者很少使用的索引

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。

https://zhuanlan.zhihu.com/p/88963084

索引如何优化

索引优化:
1.基于索引
2.最左前缀匹配原则 如果是多个值需要去覆盖,建联合索引然后查询的时候按顺序匹配
3.sql查询进行不做运算,一个效率不高,第二个如果在索引上做运算会失效
4.避免使用select *
5.返回的值最好在索引上,避免回表查询
6.少于null,is not null ,or这种会导致索引失效
7.千万别用IN,使用JOIN或者EXISTS代替它,因为in会外面查一次,里面查一次,变成平方了

事务和实现机制,binlog,redo undolog

mysql有redo log(重做日志)和undo log(回滚日志),redo log保证事务持久性,undo log是事务原子性和隔离性实现的基础

undo log属于逻辑日志,记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之相反的工作。以update为例,当事务执行updated时,生成的undo log中会包含被修改行的主键,修改了哪些列和修改前后的值等信息。

每条数据变更操作都伴随着一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上。

mysql为了提升性能不会把每次的修改都实时同步到磁盘,而是先存到buffer pool(缓冲池)里头。然后使用后台线程去做缓冲池和磁盘之间的同步

持久化redo log,两部分组成:重做日志缓冲(redo log buffer)和重做日志文件(redo log),前者在内存,后着在磁盘中

redo log会记录已成功提交事务的修改,持久化到磁盘,系统重启之后会读取redo log恢复数据

mvcc(多版本并发控制)

在每行记录的后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存了行的过期时间(时间是系统版本号)

实现依赖于undo log和read view

Undo_log:记录某行数据的多个版本的数据

read view:判断当前版本数据的可见性

binlog是Mysql sever层维护的一种二进制日志,记录mysql索引的DML(增删改查)操作,通过biglog日志可以做数据恢复,增量备份,主主复制和主从复制

磁盘顺序写和非顺序写,随机读原理

磁盘读写时耗 = 寻道时间 + 旋转延迟时间 + 操作时耗

**寻道时间:**读写数据时磁头首先要移到到指定磁道(柱面),这段时间称为寻道时间

**旋转延迟时间:**当磁头移动到指定磁道后,需要等待要操作的扇区旋转到磁头的下方,这段时间称为转延迟时间

**操作时耗:**磁头进行读写操作花费的时间

顺序读写=读取一个大文件

主要花费在传输时间,会有磁盘预读(读取的起始地址连续读取多个页面)

随机读写=读取多个小文件

需要多次寻道和旋转延迟,因为数据没有在一起,将预读浪费掉了

还有一个是文件系统的overhead,读写文件之前,需要一层层目录找到这个文件,以及做一堆属性,权限之类的检验。

算法

1.使用递归及非递归两种方式实现快速排序

  public int[] sortArray(int[] nums) {if(nums.length<2){return nums;}quickSort(nums, 0, nums.length-1);return nums;}public void quickSort(int[]nums,int left,int right){if(left>=right){return;}int pivot=position(nums,left,right);quickSort(nums, left, pivot-1);quickSort(nums,pivot+1 , right);}public int position(int[] nums,int left,int right){int pivotIndex=new Random().nextInt(right-left+1)+left;//这一步后,基准点放到了最前面swap(nums, left, pivotIndex);int pivot=nums[left];int i=left+1;int j=right;while(true){while(i<=right&&nums[i]<pivot){i++;}while(j>left&&nums[j]>pivot){j--;}if(i>=j){break;}swap(nums, i, j);i++;j--;}//基准点用完后回到原位swap(nums, left, j);return j;}public void swap(int[] nums,int left,int right){if(left==right){return;}nums[left]^=nums[right];nums[right]^=nums[left];nums[left]^=nums[right];}

递归

  public int[] sortArray(int[] nums) {Stack<Integer>stack=new Stack<>();stack.push(nums.length-1);stack.push(0);while(!stack.isEmpty()){//先进后出int low=stack.pop();int high=stack.pop();if(low<high){int index=partition(nums,low,high);stack.push(index-1);stack.push(low);stack.push(high);stack.push(index+1);}}return nums;}public int partition(int[]nums,int left,int right){//把最左边当成基准点int pivot=nums[left];int start=left;while(left<right){while(left<right&&nums[right]>=pivot){right--;}while(left<right&&nums[left]<=pivot){left++;}swap(nums, left, right);}//用完后交换一下swap(nums, left, start);return left;}public void swap(int[]nums,int left,int right){int temp=nums[left];nums[left]=nums[right];nums[right]=temp;}

2.二叉树的层序遍历

https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

public List<List<Integer>> levelOrder(TreeNode root) {if(root==null){return new ArrayList();}List<List<Integer>>list=new ArrayList();dfs(list,0,root);return list;}public void dfs(List<List<Integer>>list,int level,TreeNode root){if(root==null){return;}if(list.size()-1<level){//说明还没有到过这个层次list.add(new ArrayList());}//接下来是有的逻辑List<Integer> res=  list.get(level);res.add(root.val);//继续递归dfs(list, level+1, root.left);dfs(list, level+1, root.right);}

3.常用的排序方式有哪些,时间复杂度是多少?

https://blog.csdn.net/m0_46690280/article/details/112761515?spm=1001.2014.3001.5501

冒泡排序
通过依次比较两个相邻的的元素,看两个元素是否满足大小关系要求,如果不满足则交换两个元素

插入排序
将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序

选择排序
择排序每次会从排序区间中找到最小的元素,将其放到已排序区间的末尾。

归并排序
把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。

快速排序
如果要对m->n之间的数列进行排序,我们选择m->n之间的任意一个元素数据作为分区点(Pivot),然后我们遍历m->n之间的所有元素,将小于pivot的元素放到左边,大于pivot的元素放到右边,pivot放到中间,这样整个数列就被分成三部分了,m->k-1 之间的元素是小于pivot的,中间是pivot,k+1->n之间的元素是大于pivot的。然后再根据分治递归的思想处理两边区间的的元素数列,直到区间缩小为1

桶排序
将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序

4.如何随机生成不重复的 10 个 100 以内的数字?

1.Math:(数据类型)Math.random()*(最大数-最小数+1)+最小值
2.放入set或者list中,生成比较,如果有就重新生成

5. 470. 用 Rand7() 实现 Rand10()

https://leetcode-cn.com/problems/implement-rand10-using-rand7/submissions/

  public int rand10() {while (true){int num = (rand7() - 1) * 7 + rand7();// 如果在40以内,那就直接返回if(num <= 40) return 1 + num % 10;// 说明刚才生成的在41-49之间,利用随机数再操作一遍num = (num - 40 - 1) * 7 + rand7();if(num <= 60) return 1 + num % 10;// 说明刚才生成的在61-63之间,利用随机数再操作一遍num = (num - 60 - 1) * 7 + rand7();if(num <= 20) return 1 + num % 10;}}

6.LRU实现

https://leetcode-cn.com/problems/lru-cache/

用继承LinkedHashMap来实现

class LRUCache extends LinkedHashMap<Integer,Integer>{private int capacity;public LRUCache(int capacity) {//初始化super(capacity,0.75F,true);this.capacity=capacity;}public int get(int key) {//如果有返回当前值,没有返回-1return super.getOrDefault(key, -1);}public void put(int key, int value) {super.put(key, value);}@Overrideprotected boolean removeEldestEntry(Map.Entry<Integer,Integer>eldest){//删除判断条件return size()>capacity;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

使用map和node相互配合,快速检索最不常使用的节点

class LRUCache {Map<Integer,Node>hashtable;Node head;Node tail;int capacity;int size;public LRUCache(int capacity) {//初始化this.capacity=capacity;this.size=0;hashtable=new HashMap();head=new Node();tail=new Node();//头指针和尾指针相互连接head.next=tail;tail.prev=head;}public int get(int key) {//根据key获取nodeNode node=hashtable.get(key);if(node==null){return  -1;}//如果存在的话,需要删除当前节点,然后放到最前面去moveToHead(node);return node.value;}public void moveToHead(Node node){removeNode(node);addToHead(node);}public void removeNode(Node node){//删除节点的逻辑Node p=node.prev;Node t=node.next;p.next=t;t.prev=p;node.prev=null;node.next=null;}//添加到头节点的逻辑public void addToHead(Node node){//拿到第一个节点Node first=head.next;head.next=node;node.prev=head;first.prev=node;node.next=first;}//删除最后一个的逻辑public Node removeTail(){//删除最后一个节点Node last=tail.prev;Node cur=last.prev;cur.next=tail;tail.prev=cur;last.prev=null;last.next=null;return last;}public void put(int key, int value) {//如果存在的话,把值更新一下,然后删除节点放到最前Node node=hashtable.get(key);if(node!=null){node.value=value;moveToHead(node);return;}else{//如果不存在的话,创建节点,放到map中,加到头结点node=new Node(key,value);hashtable.put(key,node);addToHead(node);this.size++;if(this.size>this.capacity){//如果当前size大于容量了,说明需要扩容,删除尾结点Node n=removeTail();hashtable.remove(n.key);this.size--;}}}
}//定义node 前后指针,key和value
class Node{int key;int value;Node prev;Node next;public Node(){};public Node(int key,int value){this.key=key;this.value=value;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

236. 二叉树的最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {//如果有一个等于root,说明就是root了if(root==null||root==q||root==p){return root;} //往左查,往后查TreeNode leftTree= lowestCommonAncestor(root.left, p, q);TreeNode rightTree= lowestCommonAncestor(root.right, p, q);//如果左树为空,往右查if(leftTree==null){return rightTree;}//如果右树为空,往右查if(rightTree==null){return leftTree;}//如果两个树都为空,然后根节点return root;}
}

网络协议

http 协议和 RPC 协议的区别

http协议:(超文本传输协议),是一个应用层协议,由请求和响应构成
是一种无状态协议(服务器不保留与客户交易时的任何状态。也就是说上一次的请求对这一次的请求没有任何影响,服务端也不会对客户端上次的请求进行任何记录处理)
通过客户端和服务器建立一个TCP套接字连接来进行报文的发送和接受
套接字(Socket)(就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口

优点
1.简单,灵活,易扩展
2.应用广泛,环境成熟
3.无状态(没有任何记录,减轻服务器负担。对服务器无要求,可以集群)
缺点
1.明文不安全
2.无状态(无法进行连续步骤的操作)
3.基于“请求-应答”模式,加剧了HTTP的性能问题,著名问题“队头阻塞”(当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据)

rpc协议:是进程直接通信方式,可以理解成远程程序调用,允许程序调用另一个地址空间的过程或者函数
优点
1.分布式应用中多个节点的协调工作
2.部署灵活,实现服务器之间的远程通信
3.解耦
4.拓展性强
缺点
1.RPC框架开发难度大
2.RPC框架调用成功率首先与网络情况

HTTP和RPC对比
(TCP是链接方式,HTTP是信息载体)
传输协议
RPC 可以基于TCP或者HTTP
HTTP 基于HTTP
传输效率
RPC 自定义的TCP协议,可以让请求报文更小。
HTTP如果是HTTP1.1协议,请求中有很多无用的内容,HTTP2.0,经过简单封装差不多
性能消耗
RPC 基于thrift实现二进制传输
HTTP 大部分JSON,字节大小和序列化更耗费时间
负载均衡
RPC 自带
HTTP 自己用实现
服务治理
RPC 自动通知
HTTP 自己实现

http 协议 方法 POST,GET,PUT DELETE 4中方法的区别

GET请求会向数据库发送索要数据的请求来获取数据,类似于select,只查询不新增修改
PUT请求时向服务器端发送数据,改变内容,类似于update,用于修改数据,不会增加数据的条数
POST请求向服务器端发送数据,会改变数据的条数,类似于insert,会创建新内容。 几乎提交操作都是POST
DELET删除某一个资源,类似于delete

简述 TCP 三次握手以及四次挥手的流程。为什么需要三次握手以及四次挥手?

TCP三次握手:
1.发送端发送SYN和随机生成的序列化seq给接收端,发送端进入SYN_SENT状态,等待接收方确认
2.接收方收到数据后,看到SYN为1知道客户端请求建立链接,服务器端将ACK,SYN都置位1,ack=j+1,并随机产生序列化seq=K,并将数据包发送给发送端以确认连接请求,接受方进入SYN_RCVD状态
3.客户端确认后,检查ack是否为j+1,ACK是否为1,如果正确建立成功,接收方和发送方进入ESTABISHED,完成三次握手,开始传输数据

为什么要三次握手
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常

第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常

第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常(server确认了自己发送也正常)

所以三次握手就能确认双发收发功能都正常,缺一不可。

四次挥手
1.客户端发送一个FIN=M,表示关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态,意思是说“客户端我这没有数据给你了”,但是如果你服务器端还有数据没有发送完,先不急着关闭连接,可以继续发送。
2.服务器端接收到FIN后,先发送ack=M+1,告诉服务器端,你的请求我收到了,但是我还没准备好,请继续等我的消息,这时候客户端进入FIN_WAIT_2状态,继续等待服务器端的FIN报文
3.服务器端确定数据发送完后,则会向客户端发送FIN=N报文,告诉客户端,我这数据发完了,准备关闭连接了。服务器端进去LAST_ACK状态
4.客户端收到FIN=N,报文后,知道可以关闭连接了,但它还是不相信网络,怕服务器端不知道要关闭,所有发送ack=N+1后进入TIME_WAIT状态。服务器端收到ACK后,知道可以断开链接了。客户端等待2秒后依然没有收到回复,说明服务器端已经正常关闭了,客户端也关闭连接。

HTTP 与 HTTPS 有哪些区别?

HTTP协议是超文本传输协议的缩写,英文是Hyper Text Transfer Protocol。它是从WEB服务器传输超文本标记语言(HTML)到本地浏览器的传送协议。

HTTP是一个基于TCP/IP通信协议来传递数据的协议,传输的数据类型为HTML 文件,图片文件, 查询结果等。

HTTP协议一般用于B/S架构()。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。

HTTP特点
http协议支持客户端/服务端模式,也是一种请求/响应模式的协议。
简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
灵活:HTTP允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。
无连接:限制每次连接只处理一个请求。服务器处理完请求,并收到客户的应答后,即断开连接,但是却不利于客户端与服务器保持会话连接,为了弥补这种不足,产生了两项记录http状态的技术,一个叫做Cookie,一个叫做Session。
无状态:无状态是指协议对于事务处理没有记忆,后续处理需要前面的信息,则必须重传。

HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):一般理解为HTTP+SSL/TLS,通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。


1.首先客户端通过URL访问服务器建立SSL连接。
2.服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
3.客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
4.客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
5.服务器利用自己的私钥解密出会话密钥。
6.服务器利用会话密钥加密与客户端之间的通信。

HTTPS的缺点
HTTPS协议多次握手,导致页面的加载时间延长近50%;
HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗;
申请SSL证书需要钱,功能越强大的证书费用越高。
SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大。

总结HTTPS和HTTP的区别
HTTPS是HTTP协议的安全版本,HTTP协议的数据传输是明文的,是不安全的,HTTPS使用了SSL/TLS协议进行了加密处理。
http和https使用连接方式不同,默认端口也不一样,http是80,https是443。

TCP 与 UDP 在网络协议中的哪一层,他们之间有什么区别?

传输层协议

**TCP(Transmission Control Protocol,传输控制协议)**是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。 一个TCP连接必须要经过三次“对话”才能建立起来

UDP(User Data Protocol,用户数据报协议)
1、UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

2、 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等, 因此一台服务机可同时向多个客户机传输相同的消息。

3、UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。

4、吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、 源端和终端主机性能的限制。

5、UDP使用尽最大努力交付,即不保证可靠交付, 因此主机不需要维持复杂的链接状态表(这里面有许多参数)。

6、UDP是面向报文的。发送方的UDP对应用程序交下来的报文, 在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界, 因此,应用程序需要选择合适的报文大小。

对比

1、基于连接与无连接;

2、对系统资源的要求(TCP较多,UDP少);

3、UDP程序结构较简单;

4、流模式与数据报模式 ;

5、TCP保证数据正确性,UDP可能丢包;

6、TCP保证数据顺序,UDP不保证。

从URL输入到页面展现的过程

  1. 在浏览器中输入url
  2. DNS域名解析
  3. 服务器处理请求
  4. 网站处理流程
  5. 浏览器显示页面信息

一、在浏览器输入URL
URL(Uniform Resource Locator,统一资源定位符)

由一串简单的文本字符组成,他的作用是用于定位互联网上资源的地址。URL通常由以下几部分组成:传输协议、域名、端口、文件路径。

https://zhuanlan.zhihu.com/p/33149162

"这一串字符即为URL。

协议部分
通过特定的传输协议,获得指定文档的内容。

该URL的协议部分为“ https: ”,这代表网页使用的是https协议。在Internet中可以使用多种协议。常见的协议有:” http:// ” (超文本传输协议,信息是明文传输)、“ https:// ”(进行加密的网络传输协议)、" file:// “(本地文件传输协议,file协议主要用于访问本地计算机中的文件)、” ftp:// “(文件传输协议),” mailto: "(资源为电子邮件地址,通过SMTP访问)。还有一种URL前面没有加指定协议,它是指获取该资源需要使用的协议与当前的URL是保持一致的。

二、DNS域名解析
当你在浏览器中输入URL后,你使用的电脑会发出一个DNS请求(将域名转化为IP地址的请求)。对于浏览器,实际上并不知道"https://zhuanlan.zhihu.com/p/33149162"到底是什么东西,需要确认其中域名所在服务器的IP地址才能找到目标网站(浏览器需要对IP地址进行识别,才能够进一步传递网址和信息内容)。那么,将域名解析成对应的服务器IP地址这项工作,就是由DNS服务器来完成。

域名解析

  1. 查找浏览器缓存
  2. 查找系统缓存 hosts
  3. 查找路由器缓存
  4. 查找ISP 网络服务商(电信)
  5. 根域名服务器查询

三、服务器处理请求
浏览器通过IP地址找到对应的服务器,要求建立TCP链接,此时服务器开始处理用户请求。

四、网站处理流程
用户输入网址后向服务器发送内容请求,服务器接收到请求后触发Controller(控制器),控制器从Model(模型)和视图(View)中获取各种数据信息进行处理,最后视图(View)将数据渲染为HTML使得页面完整的呈献给用户。

五、浏览器显示页面信息
HTML字符串被浏览器接收后被一句句读取解析。
解析到link标签后重新发送请求获取CSS。
解析到script标签后发送请求获取JS,并执行代码。
解析到img标签后发送请求获取图片资源。
浏览器根据HTML和CSS计算得到渲染,绘制到屏幕上。
JS会被执行,页面展现。

https过程

Https采用 对称加密,非对称加密,hash算法和CA认证

1.客户端发起请求给服务器,告诉客户端支持哪个版本ssl,算法,并生成随机数1
2.服务器接收到后,确定使用的ssl版本,算法,并生成随机数2,通过把证书发给客户端
3.客户端经过证书认证后,生成随机数3,并把(随机数1,3进行hash生成xx)xx发送给服务器
4.服务器接收到xx后,也进行一样的操作进行验证,并在本地将随机数1,2,3进行算法加密生成k。
然后对随机数1,2和xx进行hash生成zz发送给客户端
5.客户端收到zz后,进行验证,通过在本地将随机数1,2,3进行算法加密生成k
此时k就是相互传递的秘钥了。

线程和进程

进程:执行中的一段程序,即一旦程序被加载到内存中并准备执行,它就是一个进程。进程表示资源分配的基本概念,又是调度运行的基本单元,是系统中的并发执行的单元
线程:单个进程中执行中每个任务就是一个线程。线程就是进程中执行运算的最小单元

对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元。
做个简单的比喻:进程=火车,线程=车厢线程在进程下行进(单纯的车厢无法运行)一个进程可以包含多个线程(一辆火车可以有多个车厢)不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
https://www.zhihu.com/question/25532384

为进阿里刷面试题-日更相关推荐

  1. Mybatis面试题-日更

    什么是MyBatis的接口绑定?有哪些实现方式? 接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供 ...

  2. 渗透测试面试题--日更(1-9day)

    day-one 1.拿到一个待检测的web站,渗透测试思路? 答: (1)信息收集 获取域名的whois信息,获取注册者的邮箱姓名电话等 查服务器的旁站以及子域名站点,因为主站一般比较难,所以可以先看 ...

  3. 刷爆了!国企33岁程序员年入40w,直言:想降薪进阿里钻研技术

    最近在脉脉上看到一条消息,网友们炸开了锅: 大家根本不看好他的决定. 怕他玩命:楼主33岁去阿里才p6,就不要作死了,30岁以后去阿里玩命不值得. 更怕他受委屈:P6也就是互联网刚毕业三四年的人的级别 ...

  4. 大三时候看了这份路线,进阿里了,进大厂保姆教程,收藏起来

    hello,大家好,我是小孟.一个老程序员猿. 自己在职场厮杀多年,做过码农.项目主管.产品经理.希望通过自己的分享,让你少走弯路.关注我,必须不迷路,还能弯道超车. 小孟老婆CS(计算机科学)博士. ...

  5. 2021年1月8号,提桶进阿里做开发,把我的面试经历都告诉你

    瞎扯两句 我想,很多人和我一样在煎熬中度过着2020年,也经历了不少困难,随着国家对疫情的控制,互联网行业又重新迎来了生机. 就像标题说的,我在2021年1月18号拿到了阿里Java研发岗的offer ...

  6. 最新阿里Java技术面试题,看这一文就够了!

    金三银四跳槽季即将到来,作为 Java 开发者你开始刷面试题了吗?别急,小编整理了阿里技术面试题,看这一文就够了! 阿里面试题目目录 1:技术一面(基础面试题目) 2:技术二面(技术深度.技术原理) ...

  7. 渣科如何逆袭进阿里 No.137

    本文转载于公众号:一名叫大蕉的程序员,非本人经历. 大蕉,公众号:一名叫大蕉的程序员渣科如何逆袭进阿里 No.137 非985211本科.非纯技术专业.无ACM得奖记录.无大公司实习记录.大学前无技术 ...

  8. 25岁社招进阿里,从电商到有赞新零售,他仅1年就打开了马云一直想做的新领域!

    最近关于「新零售」的声音此起彼伏:阿里巨资收购高鑫零售,腾讯确认入股永辉超市-- 自2016年10月马云第一次提出了「新零售」概念之后,各巨头跑马圈地,线下成为了必争之地,新零售的蓝海才刚刚打开. 而 ...

  9. 数据库周刊33丨5大国产数据库中标中国移动;Oracle7月CPU安全预警;腾讯Tbase新版本发布;“2020数据技术嘉年华”有奖话题遴选;阿里云技术面试题;APEX 实现数据库自动巡检;MYSQ

    热门资讯 1.中国移动国产OLTP数据库中标公告:南大金仓阿里,万里开源中兴 分获大单 [摘要]近日,中国移动公布了 OLTP 自主可控数据库联合创新项目中标公告.公告显示:国产数据库中,南大通用.阿 ...

最新文章

  1. V8 Promise源码全面解读
  2. 《分布式系统:概念与设计》一1.3 分布式系统的趋势
  3. Scala与Java差异(三)之函数
  4. windows下nc(netcat)的安装及使用
  5. android开发 停止运行程序,开发的时候老是报错 XXXXX程序已停止运行。
  6. 超实用的JavaScript技巧及最佳实践
  7. 打开远程桌面时总提示无法打开连接文件default.rdp
  8. mysql的事务隔离级别
  9. echarts的legend显示不全_Echarts【1、数据过多导致显示不全分页,2、数据展示探讨分析】...
  10. 自定义view跟手移动android,Android自定义view圆并随手指移动
  11. android PreferenceScreen使用笔记
  12. 为什么很多人愿意去下载社交APP?
  13. java中protected的作用域_java作用域public ,private ,protected
  14. element 问号提示_点击HTML页面问号出现提示框(附源码)
  15. ios html5不显示图片,为什么iOS上的Safari不显示我的HTML5视频海报?
  16. 转转二手交易平台建设高效率循环经济链
  17. 最近贵金属白银走势:适时反戈
  18. JavaScript面试知识点
  19. 嵌入式系统开发笔记102:DEV C++的使用
  20. List、Set、Map、Queue、Deque、Stack的遍历方式总结

热门文章

  1. 创建sharepoint 2013 Content search web part(CSWP)(1)
  2. 2012-06-25创建数据库函数的基本语法
  3. 人才消费价值回归 学历虚高将会远离职场
  4. java与数据库之间关于时间类型的转换
  5. 1-1 结构化数据建模流程范例
  6. 虚拟光驱导致无法安装光驱驱动的解决方法
  7. 2020-07-03:简单的图书管理系统(Python、SQL Server)
  8. 诸暨市第七届“身边的道德模范”中 爱心助残协会何君平会长风采
  9. ppt模板网站,上面的大多数不能用了,后面的免费
  10. 响应式设计-Bootstrap栅格布局