热门推荐
2021大厂Java面试真题(四)
2024-11-17 04:15

1、Kafka 是什么?主要应用场景有哪些

2021大厂Java面试真题(四)

Kafka 是一个分布式流式处理平台。这到底是什么意思呢

流平台具有三个关键功能

  • 消息队列:发布和订阅消息流,这个功能类似于消息队列,这也是 Kafka 也被归类为消

息队列的原因。

  • 容错的持久方式存储记录消息流: Kafka 会把消息持久化到磁盘,有效避免了消息丢失

的风险。

  • 流式处理平台 在消息发布的时候进行处理,Kafka 提供了一个完整的流式处理类库。

Kafka 主要有两大应用场景

  • 消息队列 :建立实时流数据管道,以可靠地在系统或应用程序之间获取数据。

  • 数据处理 构建实时的流数据处理程序来转换或处理数据流

  • Kafka 可以将主题划分为多个分区(Partition,会根据分区规则选择把消息存储到哪个

分区中,只要分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中

这样就实现了负载均衡和水平扩展。另外,多个订阅者可以从一个或者多个分区中同时消

费数据,以支撑海量数据处理能力。

  • producer 只需要关心消息发往哪个 topic,而 consumer 只关心自己订阅哪个 topic

并不关心每条消息存于整个集群的哪个 broker。 为了性能考虑,如果 topic 内的消息

只存于一个 broker,那这个 broker 会成为瓶颈,无法做到水平扩展。所以把 topic 内

的数据分布到整个集群就是一个自然而然的设计方式。

65535 。

服务器的 ip ,端口号 ,客户端的 ip 都是确定的。 能变的只有客户端的端口号。

加网卡 ,保证四元组唯一,理论上能是客户端和服务器之间建立 10 万以上的连接 。

  • 多个线程同时操作一个 hashmap 就可能出现不安全的情况。

  • 如果两个线程同时遇到 HashMap 的大小达到 12 的倍数时,就很有可能会出现在将

oldTable 转移到 newTable 的过程中遇到问题,从而导致最终的 HashMap 的值存储

异常。

  • 构造 entry<K,V>单链表时,也会出现不安全的情况。

单列索引

  • 普通索引:MySQL 中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值

和空值,纯粹为了查询数据更快一点。

  • 唯一索引:索引列中的值必须是唯一的,但是允许为空值

  • 主键索引:是一种特殊的唯一索引,不允许有空值。

组合索引

多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使

,使用组合索引时遵循最左前缀集合。

全文索引

只有在 MyISAM 引擎上才能使用,只能在 CHAR,VARCHAR,TEXT 类型字段上使用全文

索引,介绍了要求,说说什么是全文索引,就是在一堆文字中,通过其中的某个关键字等,就

能找到该字段所属的记录行,比如有"你是个靓仔,靓女 …" 通过靓仔,可能就可以找到该条

记录

空间索引

空间索引是对空间数据类型的字段建立的索引,MySQL 中的空间数据类型有四种

GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用 SPATIAL 关

键字。要求,引擎为 MyISAM,创建空间索引的列,必须将其声明为 NOT NULL。

操作系统中可以拥有多个进程,一个进程里可以拥有多个线程,线程在进程内执行

进程和线程的区别

  • 容易创建新线程。创建新进程需要重复父进程

  • 线程可以控制同一进程的其他线程。进程无法控制兄弟进程,只能控制其子进程

  • 进程拥有自己的内存空间。线程使用进程的内存空间,且要和该进程的其他线程共享这个

空间;而不是在进程中给每个线程单独划分一点空间。

  • (同一进程中的)线程在共享内存空间中运行,而进程在不同的内存空间中运行

  • 线程可以使用 wait,notify,notifyAll)等方法直接与其他线程(同一进程

通信;而,进程需要使用“进程间通信”(IPC)来与操作系统中的其他进程通信。

  • 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘

关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

  • 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进

程间的通信。

  • 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访

问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因

,主要作为进程间以及同一进程内不同线程之间的同步手段。

  • 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列

标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大

小受限等缺点。

  • 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

  • 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段

共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针

对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配

合使用,来实现进程间的同步和通信。

  • 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用

于不同及其间的进程通信。

对于一个定时任务,如果当前任务已经被某一个服务器处理后,另外一个服务器就不需要执行 这个任务了
  • 在定时任务里加锁机制,等某台服务器获取权限,其他服务器将不再执行此次定时任务。

  • 在数据库的创建定时任务控制表 job_controller,创建 updated_by 字段,用来存放执

行代码的服务器生成的序列号。创建 updateTime 字段,用于记录标记更新 update_by

的时间戳,也可以理解为上一次任务执行的时间戳。

  • 在代码层面,在执行任务的时候,首先生成一个序列号,然后将序列号存储在当前任务的

记录上。然后再从数据库里查询当前记录的序列号,在做标记前,首先检查当前任务的上

一次执行时间离当前时间超过阈值(自己定义,如果超过则表明还没有其他节点执行该

任务,然后为 task 保存标签和当前运行时间。当然如果上一次运行时间为空的情况下

也是允许标记的,如果数据库中的序列号与当前节点生成序列号相匹配,则执行任务的具

体逻辑,反之,则什么都不做处理。

  • 基于数据库实现分布式锁

  • 基于缓存实现分布式锁

  • 基于 Zookeeper 实现分布式锁

  • set px nx

  • 守护线程,进行 renew

  • Redis 分布式锁实现: 先拿 setnx 来争抢锁,抢到之后,再用 expire(过期)给锁加一个

过期时间防止锁忘记了释放。

  • 如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了,那会怎么样

set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和 expire 合成一条指令来用

的。

11、Redis 的数据类型及它们的使用场景

string

  • key/value; 二进制安全的。意思是 redis 的 string 可以包含任何数据。比如 jpg 图片

或者序列化的对象 。一个键最大能存储 512MB。

hash

  • 存储对象数据

list : 简单的字符串列表

关注列表

  • 队列

set: string 类型的无序集合

共同关注列表

  • 统计独立 IP

zset : (sorted set:有序集合),每个元素都会关联一个 double 类型的分数。redis 正是通

过分数来为集合中的成员进行从小到大的排序。

排行

  • 带权重的消息队列
  • 信号(signal)是一种处理异步事件的方式。信号是比较复杂的通信方式,用于通知接

受进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。

  • 信号量(Semaphore)进程间通信处理同步互斥的机制。是在多线程环境下使用的一

种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

简单地说,信号就是一种异步通信,通知进程某种事件的发生;信号量是进程/线程同步与互

斥的一种机制,保证进程/线程间之间的有序执行或对公共资源的有序访问。

select:支持阻塞操作的设备驱动通常会实现一组自身的等待队列如读/写等待队列用于支持

上层(用户层)所需的 BLOCK 或 NonBLOCK 操作。当应用程序通过设备驱动访问该设备时

(默认为 BLOCK 操作),若该设备当前没有数据可读或写,则将该用户进程插入到该设备驱

动对应的读/写等待队列让其睡眠一段时间,等到有数据可读/写时再将该进程唤醒。

select 就是巧妙的利用等待队列机制让用户进程适当在没有资源可读/写时睡眠,有资源可读/

写时唤醒。

epoll:epoll 由三个系统调用组成,分别是 epoll_create,epoll_ctl 和 epoll_wait。

epoll_create 用于创建和初始化一些内部使用的数据结构;epoll_ctl 用于添加,删除或者修

改指定的 fd 及其期待的事件,epoll_wait 就是用于等待任何先前指定的 fd 事件。

  • 最容易想到的方法是将数据全部排序,然后在排序后的集合中进行查找,最快的排序算法

的时间复杂度一般为 O(nlogn,如快速排序。

  • 局部淘汰法,该方法与排序方法类似,用一个容器保存前 10000 个数,然后将剩余的所

有数字——与容器内的最小数字相比,如果所有后续的元素都比容器内的 10000 个数还

,那么容器内这个 10000 个数就是最大 10000 个数。如果某一后续元素比容器内最

小数字大,则删掉容器内最小元素,并将该元素插入容器,最后遍历完这 1 亿个数,得

到的结果容器中保存的数即为最终结果了。此时的时间复杂度为 O(n+m^2,其中 m

为容器的大小,即 10000。

  • 分治法,将 1 亿个数据分成 100 份,每份 100 万个数据,找到每份数据中最大的

10000 个,最后在剩下的 10010000 个数据里面找出最大的 10000 个。如果 100

万数据选择足够理想,那么可以过滤掉 1 亿数据里面 99%的数据。100 万个数据里面

查找最大的 10000 个数据的方法如下:用快速排序的方法,将数据分为 2 堆,如果大

的那堆个数 N 大于 10000 个,继续对大堆快速排序一次分成 2 堆,如果大的那堆个

数 N 大于 10000 个,继续对大堆快速排序一次分成 2 堆,如果大堆个数 N 小于

10000 个,就在小的那堆里面快速排序一次,找第 10000-n 大的数字;递归以上过程

就可以找到第 1w 大的数。参考上面的找出第 1w 大数字,就可以类似的方法找到前

10000 大数字了。此种方法需要每次的内存空间为 10^64=4MB,一共需要 101 次这

样的比较。

  • Hash 法,如果这 1 亿个书里面有很多重复的数,先通过 Hash 法,把这 1 亿个数字

去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间,然后通

过分治法或最小堆法查找最大的 10000 个数。

  • 采用最小堆法,首先读入前 10000 个数来创建大小为 10000 的最小堆,建堆的时间复

杂度为 O(mlogm(m 为数组的大小即为 10000,然后遍历后续的数字,并于堆

(最小)数字进行比较。如果比最小的数小,则继续读取后续数字;如果比堆顶数字大

则替换堆顶元素并重新调整堆为最小堆。整个过程直至 1 亿个数全部遍历完为止。然后

按照中序遍历的方式输出当前堆中的所有 10000 个数字。该算法的时间复杂度为 O

(nmlogm,空间复杂度是 10000(常数)。

生产者丢失消息的情况

生产者(Producer) 调用 send 方法发送消息之后,消息可能因为网络问题并没有发送过去。

为了确定消息是发送成功,我们要判断消息发送的结果,Kafka 生产者(Producer) 使用

send 方法发送消息实际上是异步的操作,我们可以通过 get()方法获取调用结果,但是这样

也让它变为了同步操作,可以采用为其添加回调函数的形式,示例代码如下

ListenableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, o);

future.addCallback(result -> logger.info(“生产者成功发送消息到 topic:{} partition:{}的消息”,

result.getRecordmetadata().topic(), result.getRecordmetadata().partition()),

ex -> logger.error(“生产者发送消失败,原因:{}”, ex.getMessage()));

Producer 的 retries**(重试次数)设置一个比较合理的值,一般是 3 ,但是为了保证消息不**

丢失的话一般会设置比较大一点。设置完成之后,当出现网络问题之后能够自动重试消息发送

避免消息丢失。另外,建议还要设置重试间隔,因为间隔太小的话重试的效果就不明显了,网

络波动一次你 3 次一下子就重试完了

消费者丢失消息的情况

当消费者拉取到了分区的某个消息之后,消费者会自动提交了 offset。自动提交的话会有一个

问题,试想一下,当消费者刚拿到这个消息准备进行真正消费的时候,突然挂掉了,消息实际

上并没有被消费,但是 offset 却被自动提交了。

解决办法也比较粗暴,我们手动关闭自动提交 offset,每次在真正消费完消息之后再自己手

动提交 offset 。 但是,细心的朋友一定会发现,这样会带来消息被重新消费的问题。比如你

刚刚消费完消息之后,还没提交 offset,结果自己挂掉了,那么这个消息理论上就会被消费两

次。

Kafka 弄丢了消息

试想一种情况:假如 leader 副本所在的 broker 突然挂掉,那么就要从 follower 副本重新选

出一个 leader ,但是 leader 的数据还有一些没有被 follower 副本的同步的话,就会造成消

息丢失。

当我们配置了 unclean.leader.election.enable = false 的话,当 leader 副本发生故障时就

不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了

消息丢失的可能性。

消息队列在实际应用中包括如下四个场景

  • 应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程

失败

  • 异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理

减少处理时间

  • 限流削峰:广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况

  • 消息驱动的系统:系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息

消费者(可能有多个)负责对消息进行处理

悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会

上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多

这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如 Java 里

面的同步原语 synchronized 关键字的实现也是悲观锁。

乐观锁

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更

新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适

用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制

其实都是提供的乐观锁。在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是

使用了乐观锁的一种实现方式 CAS 实现的。

  • 数据结构实现:ArrayList :基于数组,便于按 index 访问,超过数组需要扩容,扩容成

本较高。linkedList:使用链表实现,无需扩容。

  • 随机访问效率:ArrayList 比 linkedList 在随机访问的时候效率要高,因为 linkedList

是线性的数据存储方式,所以需要移动指针从前往后依次查找。

  • 增加和删除效率:在非首尾的增删操作,linkedList 要比 ArrayList 效率要高,因为

ArrayList 增删操作要影响数组内的其他数据的下标。

  • 内存空间占用:linkedList 比 ArrayList 更占内存,因为 linkedList 的节点除了存储数

,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

  • 线程安全:ArrayList 和 linkList 都是不同步的,不保证线程安全。

  • 综合来说,需要频繁读取集合中的元素时,更推荐使用 Arrayist,而在增删操作较多时

更推荐使用 linkedList。

  • linkedList 的双向链表是链表的一种,它的每个数据结点中都有 2 个指针,分别指向直

接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便的访问它的

前驱结点和后继结点。

所谓 SQL 注入式攻击,就是攻击者把 SQL 命令插入到 Web 表单的输入域或页面请求的

查询字符串欺骗服务器执行恶意的 SQL 命令

如何防范 SQL 注入式攻击

在利用表单输入的内容构造 SQL 命令之前,把所有输入内容过滤一番就可以了。过滤输入内

容可以按多种方式进行。

  • 对于动态构造 SQL 查询的场合

a. 替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改 SQL 命令的含

义。

b. 删除用户输入内容中的所有连字符

c. 对于用来执行查询的数据库帐户,限制其权限**。**用不同的用户帐户执行查询、插入、更新、

删除操作。

  • 用存储过程来执行所有的查询。

  • 限制表单或查询字符串输入的长度。

  • 检查用户输入的合法性。

  • 将用户登录名称、密码等数据加密保存。

  • 检查提取数据的查询所返回的记录数量。

  • 原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。

  • 一致性或可串性。事务的执行使得数据库从一种正确状态转换成另一种正确状态

  • 隔离性。在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务

  • 持久性。事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故

,事务的处理结果也会得到保存

尽可能使用散列表(hashes,散列表(是说散列表里面存储的数少)使用的内存非常小

所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用

户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的

所有信息存储到一张散列表里面

缓存穿透

  • 问题:大量并发查询不存在的 KEY,在缓存和数据库中都不存在,同时给缓存和数据库

带来压力。

  • 原因:一般而言,缓存穿透有 2 种可能性:业务数据被误删,导致缓存和数据库中都没

有数据。恶意进行 ddos 攻击。

  • 分析:为什么会多次透传呢?不存在 一直为空,需要注意让缓存能够区分 KEY 不存在和

查询到一个空值。

  • 解决办法:缓存空值的 KEY,这样第一次不存在也会被加载会记录,下次拿到有这个

KEY。Bloom 过滤或 RoaingBitmap 判断 KEY 是否存在,如果布隆过滤器中没有查到

这个数据,就不去数据库中查。在处理请求前增加恶意请求检查,如果检测到是恶意攻击

则拒绝进行服务。完全以缓存为准,使用延迟异步加载的策略(异步线程负责维护缓存的

数据,定期或根据条件触发更新,这样就不会触发更新。

缓存击穿

  • 问题:某个 KEY 失效的时候,正好有大量并发请求访问这个 KEY。

  • 分析:跟穿透其实很像,属于比较偶然的。

  • 解决办法:KEY 的更新操作添加全局互斥锁。完全以缓存为准,使用延迟异步加载的策

(异步线程负责维护缓存的数据,定期或根据条件触发更新,这样就不会触发更新。

缓存雪崩

  • 问题:当某一时刻发生大规模的缓存失效的情况,导致大量的请求无法获取数据,从而将

流量压力传导到数据库上,导致数据库压力过大甚至宕机。

  • 原因:一般而言,缓存雪崩有 2 种可能性:大量的数据同一个时间失效:比如业务关系

强相关的数据要求同时失效 Redis 宕机

  • 分析:一般来说,由于更新策略、或者数据热点、缓存服务宕机等原因,可能会导致缓存

数据同一个时间点大规模不可用,或者都更新。所以,需要我们的更新策略要在时间上合

,数据要均匀分享,缓存服务器要多台高可用。

  • 解决办法:更新策略在时间上做到比较平均。如果数据需要同一时间失效,可以给这批数

据加上一些随机值,使得这批数据不要在同一个时间过期,降低数据库的压力。使用的热

数据尽量分散到不同的机器上。多台机器做主从复制或者多副本,实现高可用。做好主从

的部署,当主节点挂掉后,能快速的使用从结点顶上。实现熔断限流机制,对系统进行负

载能力控制。对于非核心功能的业务,拒绝其请求,只允许核心功能业务访问数据库获取

数据。服务降价:提供默认返回值,或简单的提示信息

23、数组和链表的区别?当数组内存过大时会出现什么问题?链表增删过多会 出现的什么问题

  • 数组静态分配内存,链表动态分配内存

  • 数组事先定义固定的长度,不能适应数据动态的增减的情况。当数据增加时,可能超出原

先定义的元素个数;当数据减少时,造成内存浪费

  • 链表动态地进行存储分配,可以适应数据动态地增减的情况

  • 数组在内存中连续,链表不连续

  • 数组元素在栈区,链表元素在堆区

  • (静态)数组从栈中分配空间,对于程序员方便快速,但是自由度小

  • 链表从堆中分配空间,自由度大但是申请管理比较麻烦。

  • 数组利用下标定位,时间复杂度为 O(1),链表定位元素时间复杂度 O(n)

  • 数组插入或删除元素的时间复杂度 O(n),链表的时间复杂度 O(1)。

  • 当数组内存过大时会出现什么问题(堆内存溢出,链表增删过多会出现的什么问题(大

量内存碎片

23、数组和链表的区别?当数组内存过大时会出现什么问题?链表增删过多会

出现的什么问题

  • 数组静态分配内存,链表动态分配内存

  • 数组事先定义固定的长度,不能适应数据动态的增减的情况。当数据增加时,可能超出原

先定义的元素个数;当数据减少时,造成内存浪费

  • 链表动态地进行存储分配,可以适应数据动态地增减的情况

  • 数组在内存中连续,链表不连续

  • 数组元素在栈区,链表元素在堆区

  • (静态)数组从栈中分配空间,对于程序员方便快速,但是自由度小

  • 链表从堆中分配空间,自由度大但是申请管理比较麻烦。

  • 数组利用下标定位,时间复杂度为 O(1),链表定位元素时间复杂度 O(n)

  • 数组插入或删除元素的时间复杂度 O(n),链表的时间复杂度 O(1)。

  • 当数组内存过大时会出现什么问题(堆内存溢出,链表增删过多会出现的什么问题(大

量内存碎片

  • 冒泡排序,O(n2),通过重复走完数组的所有元素,通过两两比较,直到没有数可以交换

的时候结束这个数,再到下个数,直到整个数组排好顺序。

  • 插入排序,O(n2),每次从未排好序的数据堆中拿出一个数,插入到已排好序的数据队列

的正确位置。

  • 选择排序,O(n2),每次从未排好序的数据堆中找到最小的数,插入到已排好序的数据队

列的头部。

  • 快速排序,O(N*logN),以数据堆中的一个数为标准,将数据堆分为小于等于和大于该数

的两堆,对于分割后的两堆数再分别利用上述方法进行分割,以此类推,直到堆中只有一

个数为止。

  • 堆排序,O(N*logN),将数据堆中的数两两组队排序,对于排序好的这些子堆再两两组队

排序,以此类推,直到只剩下一个堆。

  • 归并排序,O(N*logN),基于堆的排序算法,分为最大堆和最小堆。排序分为两个过程堆

的构造和堆的排序。

  • 方法区(method):被所有的线程共享。方法区包含所有的类信息和静态变量。(运行时

常量池

  • 堆(heap):被所有的线程共享,存放对象实例以及数组,Java 堆是 GC 的主要区域。

  • 栈(stack):每个线程包含一个栈区,栈中保存一些局部变量等。(本地局部变量、操作数

栈、动态链接、返回地址

  • 程序计数器:是当前线程执行的字节码的行指示器。

  • 本地方法栈

  • 索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),它们包含着

对数据表里所有记录的引用指针。

  • 普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。

  • 普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相

同的值,在为这个数据列创建索引的时候就应该用关键字 UNIQUE 把它定义为一个唯一

索引。也就是说,唯一索引可以保证数据记录的唯一性。

  • 主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一

条记录,使用关键字 PRIMARY KEY 来创建。

  • 索引可以覆盖多个数据列,如像 INDEX(columnA, columnB)索引,这就是联合索引。

  • 索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,因为在执

行这些写操作时,还要操作索引文件。

阻塞 I/O, 非阻塞 I/O 模型,I/O 复用模型,信号驱动 I/O 模型 ,异步 I/O 模型。

域名解析–> 发起 TCP 的 3 次握手 –> 建立 TCP 连接后发起 http 请求 –> 服务器响应

http 请求–>浏览器得到 html 代码 –> 浏览器解析 html 代码,并请求 html 代码中的资

(如 js、css、图片等) –> 浏览器对页面进行渲染呈现给用户 。

优点:

  • 保证性能下限: 虚拟 DOM 可以经过 diff 找出最小差异,然后批量进行 patch,这种操作虽

然比不上手动优化,但是比起粗暴的 DOM 操作性能要好很多,因此虚拟 DOM 可以保证

性能下限

  • 无需手动操作 DOM: 虚拟 DOM 的 diff 和 patch 都是在一次更新中自动进行的,我们

无需手动操作 DOM,极大提高开发效率

  • 跨平台: 虚拟 DOM 本质上是 Javascript 对象,而 DOM 与平台强相关,相比之下虚拟

DOM 可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等

缺点:

无法进行极致优化: 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化,比

如 VScode 采用直接手动操作 DOM 的方式进行极端的性能优化。

幻读是一个事务在前后两次查询同一个范围的时候、后一次查询看到了前一次查询未看到的行。

在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻

读在“当前读”下才会出现。

SERIALIZABLE(可串行化)可以防止幻读:最高的隔离级别,完全服从 ACID 的隔离级别。

    以上就是本篇文章【2021大厂Java面试真题(四)】的全部内容了,欢迎阅览 ! 文章地址:http://keair.bhha.com.cn/quote/1231.html 
     动态      相关文章      文章      同类文章      热门文章      栏目首页      网站地图      返回首页 康宝晨移动站 http://keair.bhha.com.cn/mobile/ , 查看更多