【快手】日常实习 Java一面面经|讲解|0312
今天挑选一篇快手日常实习一面,给大家做讲解分析,参考回答和学习资料指引,期望对大家有所帮助~
感谢这位同学的分享,预祝Offer多多~~~
1.为什么模糊查询用ES而不用Mysql呢?
- 为什么ES查询比Mysql快
- 说一下倒排索引?
解析:
考察对ES的理解,ES属于常用组件,但是校招来说,如果没用用到过,简历上没有写过的话,一般不会延伸过去。但是如果会ES当然是加分的
参考回答:
为什么模糊查询用ES而不用Mysql呢?
假设我们有一个在线零售商店,销售数十万种不同的商品,包括服装、电子产品、家居用品等。用户通常通过关键词搜索来查找他们想要的商品。在这种情况下,实现一个高效且准确的搜索功能至关重要。
使用MySQL的劣势:
- 性能问题:如果尝试使用MySQL的LIKE语句进行模糊查询,当商品数量达到数十万或更多时,查询性能可能会变得非常差。这是因为LIKE语句通常不会使用索引,而是会扫描整个表来查找匹配的行。
- 功能限制:MySQL的全文搜索功能在某些情况下可能不够用,特别是当需要处理多种语言、同义词、拼写错误或复杂查询时。
- 索引结构劣势:MySQL使用B树等数据结构来存储索引,这在精确查找时非常高效,但不太适合全文搜索和模糊查询。
使用Elasticsearch的优势:
- 高性能全文搜索:Elasticsearch专为全文搜索而设计,使用倒排索引和分词器等高级技术来快速定位包含查询关键词的文档。这使得它能够在毫秒级内返回结果,即使面对数十亿级别的文档集合。
- 丰富的查询功能:Elasticsearch支持多种类型的查询,包括模糊查询、通配符查询、正则表达式查询等。它还提供了高亮显示、分页、排序和聚合等高级功能,以改善用户体验并满足复杂的业务需求。
- 多语言支持:Elasticsearch内置了对多种语言的支持,包括中文、英文等。它可以处理不同语言的文本数据,并提供相应的搜索和分析功能。
- 索引结构优势:Elasticsearch使用倒排索引,这使得它非常适合执行全文搜索和模糊查询。倒排索引可以快速定位到包含特定词汇的文档。
为什么ES查询比Mysql快?
- 索引结构差异:Elasticsearch使用倒排索引,而MySQL通常使用B+树等结构。倒排索引根据关键词快速定位到相关的文档,非常适合全文搜索和模糊查询。相比之下,MySQL的索引结构在处理这类查询时可能效率较低。
- 数据存储与检索方式:Elasticsearch将数据存储在内存中,以提高检索速度。而MySQL则需要先从磁盘读取索引,然后查找对应的数据节点。这种差异使得Elasticsearch在查询速度上具有优势。
- 分布式架构:Elasticsearch是一个分布式搜索和分析引擎,可以轻松扩展到数百台服务器以处理大规模数据。这种分布式架构使得Elasticsearch能够并行处理查询请求,进一步提高查询速度。而MySQL的扩展性相对有限,可能需要通过分库分表等方式来提高性能,但这会增加系统的复杂性和维护成本。
说一下倒排索引?
倒排索引(Inverted Index)是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构,也是Elasticsearch等搜索引擎能够快速定位到相关文档的关键技术之一。
举例
假设有三篇文档:
- 文档1:我喜欢吃苹果。
- 文档2:他喜欢打篮球。
- 文档3:苹果很甜,但我不喜欢运动。
倒排索引为:
- 我:文档1, 文档3
- 喜欢:文档1, 文档2
- 吃:文档1
- 苹果:文档1, 文档3
- 他:文档2
- 打:文档2
- 篮球:文档2
- 很甜:文档3
- 但:文档3
- 不:文档3
- 运动:文档3
当你想搜索“苹果”时,倒排索引会快速告诉你它在文档1和文档3中。这就是倒排索引的核心原理:以单词为关键字,列出含有这个单词的文档编号,实现快速查找。
学习指引: 面试学习:《掘金》|浅谈ElasticSearch原理 面试学习:《简书》|理解ElasticSearch工作原理
2.什么场景下我们使用Mysql,什么情况下我们用Redis?
- Redis快,那Mysql不是也有索引吗,为什么要用redis
解析:
Mysql,redis的特点,需要掌握。
参考回答:
使用MySQL的场景:
- 关系型数据存储:MySQL是一个关系型数据库管理系统(RDBMS),非常适合存储和查询结构化数据,即数据之间存在明确关系的数据。例如,电商网站中的用户信息、订单信息、商品信息等。
- 事务处理:MySQL支持ACID事务特性,这意味着它可以保证一系列操作的原子性、一致性、隔离性和持久性。对于需要确保数据完整性和一致性的应用,如银行转账、订单处理等,MySQL是理想的选择。
- 复杂查询:MySQL支持SQL(结构化查询语言),可以进行复杂的查询操作,如连接、聚合、排序等。对于需要进行大量数据处理和分析的应用,MySQL非常适用。
使用Redis的场景:
- 缓存:Redis是一个高性能的内存数据库,通常用作缓存层,以减少对后端数据库的访问。对于读多写少、数据实时性要求较高的应用,如新闻网站、社交媒体等,使用Redis作为缓存可以显著提高性能和用户体验。
- 实时数据处理:Redis支持多种数据结构(如字符串、哈希、列表、集合和有序集合等),并且提供了丰富的操作命令。这使得Redis非常适合处理实时数据流,如实时排行榜、实时统计等。
- 消息队列:Redis提供了发布/订阅功能和阻塞队列等特性,可以用作消息中间件来实现异步通信和分布式系统之间的解耦。
- 高并发场景:由于Redis基于内存存储且采用单线程模型(实际上是通过IO多路复用来处理并发请求),它在高并发场景下表现优异。对于需要快速响应大量并发请求的应用,如秒杀系统、实时推荐系统等,Redis是一个很好的选择。
Redis快,那Mysql不是也有索引吗,为什么要用redis?
确实,MySQL也有索引,它可以提高查询速度。但是,Redis和MySQL在数据存储和查询机制上有一些根本的区别,使得在某些场景下Redis表现得更快。
内存/磁盘
:Redis是基于内存存储的,而MySQL主要是基于磁盘存储。内存访问速度比磁盘访问速度要快得多,因此Redis在读取数据时通常会比MySQL更快。数据结构
:Redis存储的是k-v格式的数据,部分数据结构时间复杂度是O(1),即常数阶,而MySQL引擎的底层实现通常是B+Tree,时间复杂度是O(logn),即对数阶。这意味着在大多数情况下,Redis查找数据的速度会比MySQL更快。
学习指引: 《知乎》|MySQL与Redis的区别与联系详解
3.Synchronized加锁和ReentrantLock加锁有什么区别?
- 为什么我们有了synchronized还要用redis分布锁呢
解析: Synchronized系列常考面试题。
参考回答:
- 底层实现不同:synchronized 是依赖于 JVM 实现的,具体实现并没有直接暴露给我们;ReentrantLock 是 JDK 层面实现的,所以我们可以通过查看它的源代码,来看它是如何实现的。
- ReentrantLock 比 synchronized 增加了一些高级功能: 等待可中断;可实现公平锁;可实现选择性通知(锁可以绑定多个条件Condition)
为什么我们有了synchronized还要用redis分布锁呢?
因为synchronized只能保证同一JVM进程内的线程安全,而无法控制不同JVM进程或不同服务器之间的并发安全
学习指引:
面试学习:***********************************************
理解学习:《网课:Java并发编程78讲》|synchronized 和 Lock 孰优孰劣,如何选择?
面试学习:《小林coding》| 如何用 Redis 实现分布式锁的?
4.说一下HashMap添加元素的一个底层原理?
解析:
HashMap是Java集合中最常考的结构,并且pu t流程也是最高频面试题之一。
大致流程即可,其中肯定很多分支细节没办法描述。
参考回答:
- ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;
- ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;
- ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;
- ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;
- ⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; ⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。
学习指引:
面试学习:《美团技术团队》博客|Java 8系列之重新认识HashMap
5.那你说一下,扩容的时候这个数据是怎么迁移的呢
解析:
我们都已JDK8为例去讲。如果面试官问了,再去讲JDK7 。 所以都需要掌握
参考回答:
JDK7的元素迁移:
JDK7中,HashMap的内部数据保存的都是链表。因此逻辑相对简单:在准备好新的数组后,map会遍历数组的每个“桶”,然后遍历桶中的每个Entity,重新计算其hash值(也有可能不计算),找到新数组中的对应位置,以头插法插入新的链表。 这里有几个注意点:
- 因为是头插法,因此新旧链表的元素位置会发生转置现象。
- 元素迁移的过程中在多线程情境下有可能会触发死循环(无限进行链表反转)
JDK8的元素迁移:
java1.8+在扩容时,不需要重新计算元素的hash进行元素迁移。而是用原先位置key的hash值与旧数组的长度(oldCap)进行"与"操作。
- 如果结果是0,那么当前元素的桶位置不变。
- 如果结果为1,那么桶的位置就是原位置+原数组长度 值得注意的是:为了防止java1.7之前元素迁移头插法在多线程是会造成死循环,java1.8+后使用尾插法
学习指引:
面试学习:《知乎》|HashMap的扩容机制
6.Mysql的受隔离级别,可以说一下嘛,分别解决了什么问题呢?
解析:
参考回答:
四种隔离级别
- 读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;
- 读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;
- 可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;
- 串行化(serializable );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;
分别解决了什么问题呢?
根据分析图:要解决脏读现象,就要升级到「读提交」以上的隔离级别;要解决不可重复读现象,就要升级到「可重复读」的隔离级别,要解决幻读现象不建议将隔离级别升级到「串行化」。
学习指引:
面试学习:《小林coding》|事务的隔离级别有哪些?文章包括隔离级别介绍和解决的问题。
7.如果Mysql遇到慢sql我们怎么排查啊?
解析:
排查慢SQL的原因和思考SQL优化点事类似的过程。
参考回答:
当MySQL遇到慢查询(慢SQL)时,我们可以通过以下步骤进行排查和优化:
- 开启慢查询日志:
确保MySQL的慢查询日志已经开启。通过查看
slow_query_log
和slow_query_log_file
变量来确认。如果没有开启,可以在MySQL配置文件(如
my.cnf
或my.ini
)中设置这些变量,然后重启MySQL服务。
- 分析慢查询日志:
使用
mysqldumpslow
或其他慢查询日志分析工具来查看和分析慢查询日志中的条目。关注查询的执行时间、锁等待时间以及被调用的次数等信息。
- 使用
EXPLAIN
命令:
对于日志中记录的慢查询,使用
EXPLAIN
命令来查看查询的执行计划。分析查询是否使用了合适的索引,以及是否存在全表扫描等低效操作。
- 优化查询:
- 根据
EXPLAIN
的输出结果,优化查询语句,比如添加或修改索引。- 避免在查询中使用
*
,而是指定需要的列。- 减少JOIN操作的数量或复杂性,特别是在大数据集上。
- 考虑将计算密集型的操作移到应用层进行。
学习指引: 一篇就够了。 《掘金:全解Mysql》| SQL优化篇:如何成为一位写优质SQL语句的绝顶高手!
8.Kafka如何保证消息的不丢失呢?
解析:
kafka如何保证消息不丢失,或者至少一次消费,高频考题,需要从producer,broker,consumer 三端进行处理。
参考回答:
Producer端
- 不要使用 producer.send(msg),而要使用 producer.send(msg, callback)。记住,一定要使用带有回调通知的 send 方法。
- 设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
- 设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
Broker端
- 设置 unclean.leader.election.enable = false。这是 Broker 端的参数,它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。
- 设置 replication.factor >= 3。这也是 Broker 端的参数。其实这里想表述的是,最好将消息多保存几份,毕竟目前防止消息丢失的主要机制就是冗余。
- 设置 min.insync.replicas > 1。这依然是 Broker 端参数,控制的是消息至少要被写入到多少个副本才算是“已提交”。设置成大于 1 可以提升消息持久性。在实际环境中千万不要使用默认值 1。
- 确保 replication.factor > min.insync.replicas。如果两者相等,那么只要有一个副本挂机,整个分区就无法正常工作了。我们不仅要改善消息的持久性,防止数据丢失,还要在不降低可用性的基础上完成。推荐设置成 replication.factor = min.insync.replicas + 1。
Consumer端
- 确保消息消费完成再提交。Consumer 端有个参数 enable.auto.commit,最好把它设置成 false,并采用手动提交位移的方式。
学习指引: 《掘金》|kafka如何保证消息不丢失
9.那可以说一下JVM的那几块内存结构嘛?
- 哪些是线程独享的?
- 方法区里面存的都是什么东西呢 解析:
参考回答:
堆
:这是Java内存管理中最大的一块区域,被所有线程共享。它主要用于存放对象实例,几乎所有的对象实例都会在这里分配内存。堆区是垃圾收集器管理的主要区域,因此也被称为“GC堆”。方法区
:这也是被所有线程共享的内存区域。它主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虚拟机栈
:这是线程独享的内存区域。每个线程在创建时都会创建一个虚拟机栈,每一个方法执行的时候都会创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。本地方法栈
:与虚拟机栈类似,本地方法栈也是线程独享的,但它主要用于执行Native方法。程序计数器
:这是一块较小的内存空间,也是线程独享的。它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
线程私有的:
- 程序计数器
- 虚拟机栈
- 本地方法栈
学习指引:
10.说一下Threadlocal的适用场景和原理
解析:
Java并发中的高频题之一Threadlocal,重点掌握。
参考回答:
适用场景:
- 每个线程需要有自己单独的实例。
- 实例需要在多个方法中共享,但不希望被多线程共享。对于Java Web应用而言,Session保存了很多信息,且很多地方都需要操作Session。存在多方法共享Session的需求,如果不使用ThreadLocal,可以在每个线程内构建一个Session实例,并将该实例在多个方法间传递。但使用ThreadLocal可以使代码耦合度更低,且实现更优雅。
原理:
- ThreadLocal是线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有指定线程可以获得存储数据,其他线程则无法获取到。
- ThreadLocal中有一个静态内部类ThreadLocalMap(一个简化版的HashMap),用来存储每个线程独有的ThreadLocal变量,以ThreadLocal对象的弱引用为key,ThreadLocal值为value。ThreadLocal的实例通常是与线程一一对应的,每个线程都维护自己的ThreadLocal实例。
- ThreadLocal的get()和set()方法操作的变量是每个线程自己独有的threadLocals,并非ThreadLocal类的静态变量。每个线程的threadLocals变量都有自己的独立实例。
- 当调用ThreadLocal的set()方法时,它的实现首先获取了当前线程,并在当前线程的ThreadLocalMap中设置了一个值,当前线程不共享它的ThreadLocalMap实例,因此其他线程无法访问到这个值。
- 当调用ThreadLocal的get()方法时,它同样首先获取当前线程,然后获取当前线程的ThreadLocalMap实例,并返回ThreadLocalMap中当前线程存储的值。
学习指引: 《Java 全栈知识体系》|Java 并发 - ThreadLocal详解
11.redis常用的数据结构有哪些?
解析:
重点掌握5中常用结构
参考回答:
- String(字符串):这是Redis最基本的数据类型,一个key对应一个value。它是二进制安全的,可以包含任何数据,如jpg图片或序列化的对象。一个键最大能存储512MB的数据。
- Hash(哈希):Redis的哈希是一个键值对集合,是string类型的field和value的映射表。它特别适合用于存储对象,可以像数据库中更新一个属性一样只修改某一项属性值,而不需要取出整个字符串反序列化成对象修改再序列化存回去。
- List(列表):Redis的列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),它的增删操作非常快,且提供了操作某一段元素的API。
- Set(集合):Redis的Set是string类型的无序集合。它是通过哈希表实现的,添加、删除和查找的时间复杂度都是O(1)。
- Sorted Set(有序集合):与Set相似,但Sorted Set中的每个字符串元素都会关联一个double类型的分数。Redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)可以重复。
除了以上五种基本数据类型,Redis还支持一些其他的数据结构和功能,如Bitmaps(位图)、HyperLogLog(基数统计)、Geospatial(地理位置)和Streams(流)等。
推荐学习: 《小林 coding》Redis 常见数据类型和应用场景
本文也是 《热门面经讲解》专栏 系列文章之一,文末尾有专栏链接,大家可以点个关注,我会持续更新~
#面经##快手##实习##暑期实习##牛客在线求职答疑中心#挑选近期热门真实后端面经进行讲解分析,给出:个人分析+参考回答+学习资料指引。