高级java开发必问面试题

面向对象

什么是面向对象?

对比面向过程,是两种不同的处理问题角度
面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),及各自需要做什么。

比如:实现一个模块功能

篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记的【点击此处即可】即可免费获取
 

面向过程会将任务拆解成一系列的步骤(函数),1,书写技术文档 2,排期 3,代码编写 4,测试 5,上线

面向对象会拆除技术架构师 java开发工程师 测试工程师 运维工程师
架构师:调研 技术文档编写
开发工程师:代码编写
测试工程师:测试
运维工程师:上线

面向过程比较直接高效,而面向对象更容易复用、扩展和维护

面向对象

封装

封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项
内部细节对外部调用透明,外部调用无需修改或者关心内部实现
eg: javabean的getset方法对外访问,因为属性的赋值智能由javabean本身决定。而不能由外部胡乱修改。

private String name;
public void setName(String name){
this.name = "tuling_"+name;
}
public String getName(){
retrun this.name;
}
该name有自己的命名规则,明显不能由外部直接赋值

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

继承

继承基类的方法,并做出自己的改变或扩展
子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性化的

多态

基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同

== 和equals比较

==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象地址。

equeals: object中默认也是采用==比较,通常会重写。

  • Object:
public boolean equals(Object obj) {
return (this == obj);
}
  • 1
  • 2
  • 3
  • String
    String类中被重写的equals()方法其实是比较两个字符串的内容。
 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
public static void main(String[] args)  {
        String aa="aa";
        String a2=new String("aa");
        String a3=a2;
        System.out.println(aa==a2); //false
        System.out.println(aa==a3); //false
        System.out.println(a2==a3); //true

        System.out.println(aa.equals(a2));//true
        System.out.println(aa.equals(a3));//true
        System.out.println(a2.equals(a3)); //true
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

hashCode与equals

hashCode()的作用是获取哈希码(散列码),实际返回一个int整数。
作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,java中的任何类都包含有hashCode()函数。
散列表存储的键值对(key-value),它的特点是:能根据键快速检索出对应的value,这其中就用到了散列码。

为什么要有hashCode:
以“HashSet如何检查重复”为例子来说明为什么要有hashCode。
对象加入HashSet时,HashSet会先计算出对象的HashCode值来判断对象加入的位置,看该位置时候有值,如果没有,HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法来检查两个对象是否真的相同,HashSet就不会让其加入操作成功。
如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

  • 如果两个对象相等,则hashcode一定也是相等
  • 两个对象相等,对两个对象分别调用equals方法都返回true。
  • 两个对象有相同的hashcode的值,他们不一定是相等的
  • 因此equals方法被覆盖过,则hashCode方法也必须被覆盖。
  • hashCode()的默认行为是堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等。

重载和重写的区别

重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法修饰符为private则子类就不能重写该方法。

Final

最终的

  • 修饰类:表示类不可被继承
  • 修饰方法:表示方法不可被子类覆盖,但是可以重载。
  • 修饰变量:表示变量一旦被赋值就不可以更改它的值。

HashMap怎么解决hash冲突的

  1. 什么是hash算法:
    就是把任意长度的输入,通过散列算法,变成固定长度的输出。这个输出结果是散列值。
    Hash表又叫做“散列表”,它是通过key直接访在内存位置的数据结构,在具体实现上,我们通过hash函数把key映射到表中的某个位置,来获取这个位置的数据,从而加快查找速度。
    在这里插入图片描述
  2. hash冲突
    由于哈希算法被计算的数据是无限的,而计算的结果范围有限,所以总会存在不同的数据经过计算后得到相同的值,这就是hash冲突。
  3. 解决hash冲突的方法
    开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的次序从hash表中找到一个空闲的位置,然后把冲突的元素放到这个空闲位置中。ThreadLocal就用到了线性探测法来解决hash冲突

链式寻址法:
这是一种常见的方法,简单理解就是把存在hash冲突的key,以单项链表的方式存储,比如hashMap就是采用链式寻址法来实现的。

再hash法:
就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响比较大。

建立公共溢出区。
就是把hash表分为基本表和溢出表两个部分,凡事存在冲突的元素,一律放到溢出表。

类加载器

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器
1)启动类加载器(Bootstarp ClassLoader)用来加载java核心类库,无法被java程序直接引用。
2)扩展类加载器(ExtensionsClassLoader)用来加载java的扩展库。Java虚拟机的实现会提供一个扩展库目录。
该类加载器在此目录里面查找并加载类。
3)系统类加载器(System class loader)也叫应用类加载器:它根据Java应用的类路径(ClassPath)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClasssLoader()来获取它。
4)用户自定义类加载器,通过继承java.lang.ClassLoader类的方式实现。

spring是什么

轻量级的开源J2EE框架。它是一个容器框架,用来装javabean(java对象)。
Spring是一个轻量级的控制翻转IOC和面向切面AOP的容器架构

  • 从大小与开销两方面而言Spring都是轻量级的。
  • 通过控制翻转(IOC)的技术达到松耦合的目的。
  • 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统服务进行内聚性的开发
  • 包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个框架。
  • 将简单的组件配置、组合成为复杂的应用、这个意义上是一个框架。

AOP的理解

系统是由许多不同的组件组成的,每一个组件各负责一块特定的功能。除了实现自身的核心功能外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常称为横切关注点,因为他们会跨越系统的多个组件。

当我们需要分散的对象引入公共行为的时候,OOP则显的无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。

日志代码往往水平的散步在所有的对象层次中,而它所散列到的对象的核心功能毫无关系。
在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP:将程序中的交叉业务逻辑(比如安全、日志、事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以执行某个方法之前额外的做一些事情,在某个方法执行之后额外做的一些事情。

谈谈你对IOC的理解

容器概念、控制翻转、依赖注入
IOC容器:实际上就是个map(key,value),里面存的各种对象(在xml里配置的bean节点、@repository@service@controller@compoment),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类目使用反射创建对象放到map里、扫描到有上述注解的类还是通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired,resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入,id就是对象名)

  • 控制反转
    没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变成了被动行为,控制权颠倒过来了,这就是“控制反转”这个名词的由来。

全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个ioc,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

  • 依赖注入
    获得依赖对象的过程被反转了。控制被反转之后,获得依赖对象的过程由自身管理变成为了由IOC容器主动注入。依赖注入实现了IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

零拷贝

零拷贝指的是,应用程序在需要把内核中的一块区域数据转移到另外一块区域去时,不需要经过先复制到用户空间,再转移到目标内核区域去了,而直接实现转移。

在这里插入图片描述
在这里插入图片描述

RocketMQ 架构设计

在这里插入图片描述

RocketMq 事务消息原理

依赖于TransationListener接口

  • executeLocalTransaction方法会在发送消息后调用,用于执行本地事务,如果本地事务执行成功,rocket在提交信息
  •  篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

    需要全套面试笔记的【点击此处即可】即可免费获取

  • checkLocalTransation用于对本地事务检查,rocket依赖此方法做补偿

prepare:将消息(消息上带有事务标识)投递到一个名为RMS_SYS_TRANS_HALC_TOPIC的topic中,而不是投递到真正的topic中。

commit/rollback:producer在通过TransactionListeer的executorLocalTransaction方法执行本地事务,当producer的localTranstion处理成功或失败后,producer会向broker发送commit或rollback命令,如果是commit,则broker会将投递到RMQ_SYS_TRANS_HALF_TOPIC中的消息投递到真实的topic中,然后再投递一个标识删除的消息RMQ_SYS_TRANS_HALF_TOPIC中,表示当前事务已经完成。
如果是rollback,则没有投递到真实的topic的过程,只需要投递表示删除的消息到RMQ_SYS_TRANS_OP_HALF_TOPIC。最后,消费者和普通消息一样消费事务消息。

  • 第一阶段(prepare)失败:给应用返回消息发送失败
  • 事务失败:发送回滚命令给broker,由broker执行消息的回滚
  • commit或rollback失败,由broker定时向producer发起事务检查,如果本地事务成功,则提交消息,否则回滚消息式服务。

事务状态的检查有两种情况:

  • commit/rollback:broker会执行相应的commit/rollback操作
  • 如果是transaction_not_type,则一段事件后会再次检查,当检查的次数超过上限(默认15次)则丢弃消息。
@RocketMQTransactionListener
class TransactionListenerImpl implements RocketMQLocalTransactionListener {
	@Override
	public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
		// ... local transaction process, return bollback, commit or unknown
		System.out.println("executeLocalTransaction");
		return RocketMQLocalTransactionState.UNKNOWN;
	}

	@Override
	public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
		// ... check transaction status and return bollback, commit or unknown
		System.out.println("checkLocalTransaction");
		return RocketMQLocalTransactionState.COMMIT;
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

RockeMq顺序消息消费原理

默认是不能保证的,需要程序保证发送和消费的是同一个queue,多线程消费也无法保证
发送顺序:发送端自己业务逻辑保证先后,发往一个固定的queue,生产者可以在消费体上设置消息的顺序
发送者实现MessageQueueSeletor接口,选择一个queue进行发送,也可使用rocketmq提供的默认实现。
SelectMessageQueueByHash:按参数的hashcode与可选队列进行求余选择
SelectMessageQueueByRandom:随机选择

mq:queue本身就是顺序追加写,只需保证一个队列同一时间只有一个consumer消费,通过加锁实现,consumer上的顺序消费有一个定时任务,每隔一定时间向broker发送请求延长锁定。

消费端:
pull模式:消费者需要自己维护需要拉取queue,一次拉取的消息都是顺序的,需要消费端自己保证顺序消费。
push模式:消费实例实现自MQPushConsumer接口,提供注册监听的方法消费消息,registerMessageListener、重载方法。
MessageListenerConcurrently:并行消费
MessageListenerOrderly:串行消费,consumer会把消息放入本地队列并加锁,定时任务保证锁的同步。

简述RockerMQ持久化机制

commitLog:日志数据文件,被所有的queue共享,大小为1G,写满之后重新生成,顺序写
consumeQueue:逻辑queue,消息先到达commitLog、然后异步转发到consumeQueue,包含queue在commitLog中的物理位置偏移量Offset,消息实体内容的大小和MessageTag的hash值。大小约为600w个字节,写满之后重新生成,顺序写。
indexFile:通过key或者时间区间来查找CommitLog中的消息,文件名以创建的时间戳命名,固定的单个IndexFile大小为400M,可以保存2000W个索引。

所有队列共用一个日志数据文件,避免了kafka的分区数过多、日志文件过多导致磁盘IO读写压力较大造成的性能瓶颈,rocketmq的queue只存储少量的数据、更加轻量化,对于磁盘的访问是串行化避免磁盘竞争,缺点在于:写入是顺序写,但读是随机的,先读ConsumeQueue,再读CommitLog,会降低消费读的效率。

消息发送到broker后,会被写入commitLog,写之前加锁,保证顺序写入。然后转发到consumeQueue。

消息消费时先从consumeQueue读取消息在CommitLog中的起始物理偏移量Offset,消息大小和消息的tag的HashCode值。在重commitLog读取消息内容。

  • 同步刷盘:
    消息持久化到磁盘才会给生产者返回ack,可以保证消息可靠,但是会影响性能
  • 异步刷盘
    消息写入pageCache就返回ack给生产者,刷盘采用异步线程,降低读写延迟提高性能和吞吐。

RocketMQ如何保证不丢消息

生产者:

  • 同步阻塞的方式发送消息,加上失败重试机制,可能broker存储失败,可以通过查询确认
  • 异步发送需要重写回调方法,检查发送结果
  • ack机制,可能存储CommitLog,存储ConsumerQueue失败,此时对消费者不可见

broker:
同步刷盘、集群模式下采用同步复制,会等待slave复制完成才会返回确认。

消费者:
offset手动提交,消息消费保证幂等。

消息发送

生产者向消息队列里写入消息,不同的业务场景需要生产者采用不同的写入策略。比如同步发送,异步发送,Oneway发送,延迟发送,发送事务消息等。默认使用的是DefaultMQProducer类,发送消息要经过五个步骤。
1)设置Producer的GroupName.
2)设置InstanceName,当一个jvm需要启动多个Producer的时候,通过设置不同的InstanceName来区分,不设置的话系统使用默认名称“Default”。
3)设置发送失败重试次数,当网络出现异常的时候,这个次数影响消息的重复投递次数。想保证不丢消息,可以设置重试几次。
4)设置NameServer地址。
5)组装消息并发送。

消息发生返回状态(SendResule#SendStatus)有如下四种
Flush_Dish_timeout
Flush_slave_timeout
Slave_not_available
send_ok

不同状态在不同的刷盘策略和同步策略的配置下含义是不同的。
1,Flush_Dish_Timeout:表示没有在规定时间内完成刷盘(需要Broker的刷盘策略被设置成Sync_flush才会报这个错)。
2,Flush_slave_Timeout:表示在主备方式下,并且Broker被设置成Sync_Master方式,没有在设定时间内完成主从同步。
3,Slave_not_available:这个状态产生的场景和Flush_slave_timeout类似,表示在主备方式下,并且Broker被设置成Sync_master,但是没有找到被设置成slave的broker。
4,Send_ok:表示发送成功,发送成功的具体含义,比如消息是否已经被存储到磁盘?消息是否被同步到Slave上?消息在Slave上是否被写入磁盘?需要结合所配置刷盘策略,主从策略来定。这个状态可以理解为,没有发生上面列出的三个问题状态就是Send_Ok.
写一个高质量的生产者程序,重点在于对发送结果的处理,要充分考虑各种异常,写清对应的处理逻辑。

提升写入性能
发送一条消息处刑曲需要经过三步。
1,客户端发送请求到服务器
2,服务器处理该请求。
3,服务器向客户端返回应答。
一次消息的发送耗时是上述三个步骤的总和。
在一些对速度要求高,但是可靠性要求不高的场景下,比如日志收集类应用,可以采用Oneway方式发送。

Oneway方式发送请求不等待应答,即将数据写入客户端的Socket缓冲区就返回,不等待对方返回结果。

用这种方式发送消息的耗时可以缩短到毫秒级。

另一种提高发送速度的方法是增加Producer的并发量,使用多个Producer同时发送,我们不担心多Producer同时写会降低消息写磁盘的效率,RocketMq引入了一个并发窗口,在窗口内消息可以并发地写入DirectMem中,然后异步地将连续一段无空洞的数据刷入文件系统中。

顺序写CommitLog可以让rocketMq无论在HDD还是SSD磁盘情况下都能保持较高的写入性能。

目前在阿里内部经过调优的服务器上,写入性能达到90w+的tps,我们可以参考这个数据进行系统优化。

消息消费

简单总结消费的几个要点:
1,消息消费方式(pull和push)
2,消息消费的模式(广播模式和集群模式)
3,流量控制(可以结合sentinel来实现,后面单独讲)
4,并发线程数设置
5,消息的过滤(tag,key)taga||tagb||tagc*null

当consumer的处理速度跟不上消息的产生速度,会造成越来越多的消息积压,这个时候可以查看消费逻辑本身有没有优化的空间,除此之外还有三种方法可以提高Consumer的处理能力。
1,提高消费并行度
在同一个ConsumerGroup下(Clustering方式),可以通过增加Consumer实例的数量来提高并行度。
通过加机器,或者在已有的机器启动多个Consumer进程都可以增加Consumer是实例数。
注意:总的consumer数量不要超过topic的read queue数量,超过的consumer实例接收不到消息。

此外,通过提高单个Consumer实例中的并行处理的线程数,可以在同一个Consumer内增加并行度来提高吞吐量(设置方法是修改consumerThreadMin和consumeThreadMax)

2,以批量方式进行消费
某些业务场景下,多条消息同时处理的时间会大大小于逐个处理的时间总和,比如消费消息中涉及update某个数据库,一次update10条的时间会大大小于10次update1条数据的时间。

可以通过批量方式消费来提高消费的吞吐量。实现方法是设置Consumer的consumerMessageBatchMaxSize这个参数,默认是1,如果设置N,在消息多的时候每次收到长度为N的消息链表。

3,检测延时情况,跳过非重要消息
consumer在消费过程中,如果发现由于某种原因发生严重的消息堆积,短时间无法消除堆积,这个时候可以选择丢弃不重要的消息,使Consumer尽快追上Producer的进度。

Mysql

索引的基本原理

索引用来快速地寻找那些具有特定值得记录。如果没有索引,一般来说执行查询时遍历整张表。
索引得原理:就是把无序得数据变成有序得查询。
1,把创建了索引得列得内容进行排序
2,对排序结果生成倒排表
3,在查询得时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体得数据

mysql聚族和非聚族索引的区别

业务系统里面的sql耗时,慢查询

在业务系统中,除了使用主键进行的查询,其他的都会在测试库上测试其耗时,慢查询的统计主要是运维在做,会定期将业务中的慢查询反馈给我们。

慢查询的优化首先要搞明白慢的原因是什么?是查询没有命中索引?是load了不需要的数据列?还是数据量太大?
所以优化是针对这三个方向来的。
首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析和重写。
分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

最左前缀原则是什么

当一个SQL想利用索引是,就一定要提供索引所对应的字段中最左边的字段,也就是排在前面的字段,比如针对a,b,c三个字段建立了一个联合索引,那么在写一个sql时就一定要提供a字段的条件,这样才能用到联合索引,这是由于在建立a,b,c三个字段的联合索引时,底层的B+树是按照a,b,c三个字段从左往右去比较大小进行排序,所以如果想利用B+树进行快速查找也得符合这个规则。

explain的type类型

  • all 全表扫描
  • index 索引全扫描
  • range 索引范围扫描,常用语<,<=,>=,between等操作。
  • ref 使用非唯一索引或唯一索引前缀扫描,返回单条记录,常出现在关键查询中。
  • eq-ref 类似ref,区别在于使用的是唯一索引前缀扫描,返回单条记录,常出现在关联查询中。
  • const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询。
  • null mysql不访问任何表或索引,直接返回结果。
explain 的key和key_len,extra

Innodb是如何实现事务的

innodb通过Buffer Pool,LogBuffer,Redo Log,UndoLog来实现事务
1,Innodb在收到一个update语句后,会先根据条件找到数据所在的页,并将该页缓存在Buffer Pool中
2,执行update语句,修改buffer pool中的数据,也就是内存中的数据
3,针对update语句生成一个redolog对象,并存入LogBuffer中
4,针对update语句生成undolog日志,用于事务回滚
5,如果事务提交,那么则把redolog对象进行持久化,后续还有其他机制将buffer pool中所修改的数据页持久化到磁盘中。
6,如果事务回滚,则利用undolog日志进行回滚。

redolog 和 undolog的区别

redolog 重做日志:
确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入到磁盘,在重写mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性

  • 为什么需要redolog?

我们都知道,事务的四大特性里面有一个持久性,具体来说就是只要事务提交成功,那么对数据库做的修改就被永久保存下来了,不可能因为任何原因再回到原来状态。
那么mysql是如何保证一致性呢?最简单的做法是在每次事务提交的时候,将该事务涉及修改的数据页全部刷新到磁盘中。但是这么做会有严重的性能问题,主要体现在两个方面。

1,因为innodb是以 页为单位进行磁盘交互的,而一个事务很可能只修改一个数据页里面的几个字节,这个时候将完整的数据页刷到磁盘的话,太浪费资源了
2,一个事务可能涉及修改多个数据页,并且这些数据在物理上并不连续,使用随机IO写入性能太差。

因此mysql设计了redolog ,具体来说就是只记录事务对数据页做了哪些修改,这样就能完美地解决性能问题了。

undolog 回滚日志
保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。
在这里插入图片描述

zk的初始化选举和崩溃选举过程

zxid:事务id sid:节点id
先对比zxid,在对比节点id,先投自己,选票内容(zxid,sid),遇强改投。

投票箱:每个节点在本地维护自己和其他节点的投票信息,改投时需要更新信息,并广播节点状态。

  • Looking:竞选状态
  • Following:随从状态
  • Observing:观察状态,同步leader状态,不参与投票
  • Leading:领导者状态

初始化选举

  • 节点1启动,此时只有一台服务器,它发出去的请求没有任何响应,所以它的选举状态是Looking竞选状态。
  • 节点2启动,它与节点1进行通信,互相交换自己的选举结果,由于两者没有历史数据,所以serviceId值较大的节点2胜出,但是由于没有达到半数以上,所以服务器1,2还是继续保持Looking状态。
  • 节点3启动,与1,2节点通信交互数据,服务器3成为1,2,3中的leader,此时三台服务器选举了服务器3,所以3成为leader。
  • 节点4启动,理论上服务器4应该是1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能切换为follower
  • 节点5启动,同4一样

崩溃选举

  • 变更状态,leader故障后,follower进入looking状态
  • 各节点投票,先投自己(zxid,sid),在广播投票
  • 接收到投票,对比zxid和sid,如果本节点小,则将票改为接收到的投票信息,并记录投票信息,重新广播。否则本节点大,不做任何处理
  • 统计本地投票信息,超过半数,则切换为leading状态,并广播
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值