ITPub博客

首页 > 数据库 > 数据库开发技术 > 阿里技术面完后,一点也不慌

阿里技术面完后,一点也不慌

原创 数据库开发技术 作者:皇子谈技术 时间:2024-09-22 16:07:09 0 删除 编辑

面试完不慌的底气一般来自哪里?手里有更牛批的Offer?稳了?

大家好,我是皇子。

之前说过不管是BOSS直聘,还是拉勾等,很多挂着招聘需求的企业,但并不是真的还在招人了。

应该有至少三种情况:

A、已经招到了,HR忘记关闭。这种情况,看HR的活跃度,可以不投。

B、公司买了账号,纯粹是为了打个广告或者养号。这种即使有活跃度,也不会向你发送面试邀请,这种比较难辨别。

C、还有一种,可能就是HR背了KPI,要找点事做,囤人才简历。真的会筛简历,安排面试,不过后面就是没有HC了。


补充找工作相关常用术语

HR (Human Resources):人力资源部,负责招聘、培训、绩效评估等工作的部门。

HC (Headcount):职位编制或招聘名额,指公司计划招聘的职位数量。

JD (Job Description):职位描述,详细说明了职位的职责、要求和资格条件。

CV (Curriculum Vitae):简历,详细记录了个人教育背景、工作经验、技能等。

KPI (Key Performance Indicators):关键绩效指标,用于衡量员工或团队的工作表现。

AK (All Kill):指在面试中表现非常出色,全部答对或全部通过。

PA (Pass All):全部通过,指在所有环节(如笔试、面试、背景调查等)都通过。

AC (Accepted):接受,通常指申请或面试被接受。

回到这位校招的同学身上。

虽然这位同学一点也不慌,心态很好(找工作,就需要如此内心强大的)。

客观上就算还有HC,但是回答只有50%,阿里继续走流程的难度应该还是挺大的。


好在年轻,还有很多机会的,而且现在很多企业都很看好新一代的年轻人(别说只是因为工薪资低,因为大厂真的给得起),思维活跃、肯干、潜力无限。


那接下来我也整理该同学遇到的一些八股文,其他基础的就不列举了,无手撕,供各位校招朋友参考。

Java

面向对象的三要素?

想必滚过烂熟了,三个核心要素是封装、继承和多态。不过这么回答肯定还不够。

还需要展开讲讲他们的概念和特性,才能让面试官认为你的基础很过关了,起码要介绍这三者是共同构成了面向对象编程的基本原则,使得代码更加模块化、可重用和易于维护吧。


1、封装(Encapsulation)

封装是指将数据(属性)和操作数据的方法(行为)绑定在一起,作为一个整体进行考虑,并尽可能隐藏对象的内部细节。

封装的主要目的是保护对象的内部状态,防止外部直接访问和修改,从而增加代码的安全性和稳定性。

特点:

  • 数据隐藏:通过访问修饰符(如 private、protected、public)来控制类的成员变量和方法的可见性。
  • 访问控制:提供公共方法(如 getter 和 setter 方法)来访问和修改私有成员变量,从而在外部访问时进行必要的检查和验证。

2、继承(Inheritance)

继承是指一个类(子类)可以继承另一个类(父类)的属性和方法。

通过继承,子类可以复用父类的代码,同时还可以添加新的属性和方法,或者重写父类的方法以实现不同的行为。

特点:

  • 代码复用:子类可以直接使用父类的属性和方法,避免重复编写相同的代码。
  • 层次结构:通过继承可以建立类的层次结构,更好地组织和管理代码。

3、多态

多态是指同一个接口可以被不同的对象以不同的方式实现。多态包括方法重载(Overloading)和方法重写(Overriding)。

多态使得程序具有更高的灵活性和可扩展性。

特点:

  • 方法重载:在一个类中,允许存在多个同名但参数列表不同的方法。
  • 方法重写:子类可以重写父类的方法,以提供不同的实现。

代码发送到Maven仓库的命令是?

先确保 pom.xml 文件中包含必要的配置信息,包括仓库的 URL、用户名和密码等。通常这些信息会放在 <distributionManagement> 部分。

然后在 Maven settings.xml 文件中(通常位于 ~/.m2/settings.xml),添加仓库的认证信息。

接下来就可以将项目代码发送到Maven仓库了,在项目根目录下运行 mvn deploy 命令,Maven 会根据 pom.xml 中的配置将构建产物上传到指定的仓库。

MySQL





















如何分库分表?

高分回答,可以先介绍下为什么需要分库分表:

  • 性能下降:当表中的数据量过大时,查询速度会显著变慢,因为需要扫描更多的数据行来完成查询操作。
  • 锁定竞争:大量并发写入操作会导致锁竞争加剧,影响整体吞吐量。
  • 维护困难:大表的备份、恢复、优化等操作变得更加复杂和耗时。
  • 资源限制:单一数据库服务器的硬件资源(如CPU、内存、I/O能力)有限,难以支撑超大规模的数据存储需求。

1、分库

  • 垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。

  • 水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中的数据拆分到多个库中。


2、分表

  • 垂直分表:将表中的列拆分到不同的表中,每个表只包含原表的一部分列。适用于某些列访问频率很高而其他列很少被访问(主表、扩展表)的情况。

  • 水平分表:根据某些字段的值将数据分布到不同的表中,这些表具有相同的结构。例如,可以根据用户ID的按一定策略(hash哈希值、range区间值、List列表值)对用户表进行分表。

分库分表的如何实现有了解吗?

  1. 使用中间件:有许多成熟的中间件可以帮助实现数据库的分片,比如ShardingSphere、MyCat等。它们通常提供透明的分片功能,应用程序不需要感知底层的分片逻辑。
  2. 自定义逻辑:在应用层实现分片逻辑,通过编程方式决定数据应该存储在哪个分片中。这种方式更加灵活,但增加了开发和维护的成本。
  3. 数据库内置支持:某些版本的MySQL提供了表分区的功能,可以在创建表时指定分区策略,如范围分区、列表分区、哈希分区等。

解释ACID?

原子性(Atomicity)

事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。

回滚可以用回滚日志(Undo Log)来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。


一致性(Consistency)

数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。假如 A 账户给 B 账户转 10 块钱,不管成功与否,A 和 B 的总金额是不变的。


隔离性(Isolation)

一个事务所做的修改在最终提交以前,对其它事务是不可见的。


持久性(Durability)

一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。

系统发生崩溃可以用重做日志(Redo Log)进行恢复,从而实现持久性。

与回滚日志记录数据的逻辑修改不同,重做日志记录的是数据页的物理修改。

MVCC了解吗?介绍下底层实现原理

MVCC多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。


底层实现原理:

MVCC 的实现原理主要依赖于:记录中的三个隐藏字段、Undo Log、ReadView、快照读&当前读。


隐藏字段:每行记录处理我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID 等字段

参考官网:

https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html

https://www.bilibili.com/video/BV1864y1976i?spm_id_from=333.999.0.0&vd_source=4512bed9ed20ef639e616ff34000e244

Redis

Redis 如何实现延迟队列?

延迟队列主要用于处理需要在将来某个时间点执行的任务。所以我们可以

  • 使用 ZSET(有序集合),ZSET 是 Redis 中的一个数据结构,它允许我们存储带有分数(score)的成员(member)。分数可以用来表示时间戳,从而实现任务的延迟执行。
  • 另一种方法是使用 LIST 存储任务,并为每个任务设置一个过期时间。当任务过期时,可以通过一个定时任务来检查并处理这些任务。
  • 还可以通过 KEYSPACE EVENTS 监听键的过期事件。当一个键过期时,Redis 会触发一个事件,消费者可以监听这个事件并处理相应的任务。

ZSET 方法最为常见且易于实现,适合大多数延迟队列的场景。LIST 和 EXPIRE 方法在某些特定情况下也有其优势,特别是当任务数量较少且需要精确控制过期时间时。KEYSPACE EVENTS 方法则适用于需要实时处理过期任务的场景。


ZSET的实现步骤:

  1. 添加任务:将任务及其到期时间(时间戳)作为成员和分数存入
ZADD delay_queue <timestamp> <task_id>
  1. 消费任务:定期从 ZSET 中取出所有已到期的任务(即分数小于等于当前时间戳的任务)。
ZRANGEBYSCORE delay_queue -inf now
  1. 删除已处理的任务:处理完任务后,从 ZSET 中删除该任务。
ZREM delay_queue <task_id>

zset 底层数据结构是什么?

ZSET 是 Redis 中的一个数据结构,它允许我们存储带有分数(score)的成员(member),其中分数是用来排序的。底层数据结构由两个部分组成:

  1. 跳表(Skip List):这是一个多层链表结构,每一层都有一个指向下一个节点的指针。每一层的节点数量逐渐减少,顶层的节点数量最少。这样可以在 O(log N) 时间内完成插入、删除和查找操作。
  2. 哈希表(Hash Table):用于存储成员和分数的映射关系,确保成员的唯一性,并支持快速查找。

跳表的特点

  • 多层结构:每一层都是一个链表,高层的链表节点稀疏,底层的链表节点密集。
  • 快速查找:通过在高层快速跳过大量节点,然后逐层向下查找,最终在最底层找到目标节点。
  • 插入和删除:插入和删除操作也需要在多层链表中进行,但平均时间复杂度仍然是 O(log N)。

哈希表的特点

  • 键值对存储:每个成员作为一个键,对应的分数作为值。
  • 快速查找:哈希表支持 O(1) 时间复杂度的查找操作。

为什么使用跳跃表(跳表Skip List)?

1、插入和删除操作简单:跳表的插入和删除操作相对简单,不需要像平衡二叉树那样进行复杂的旋转操作来维持平衡。这使得跳表在实现和维护上更加容易,同时也减少了代码的复杂性和潜在的错误。

2、并发性能好:跳表在并发环境下的性能较好。由于跳表的多层结构,可以在不同层上并行进行插入和删除操作,而不会像平衡二叉树那样需要频繁地进行全局性的调整。这使得跳表在高并发场景下具有更好的性能。

3、实现成本低:跳表的实现相对简单,代码量较少,容易理解和调试;衡二叉树的实现较为复杂,需要处理各种旋转操作,代码量较多,调试难度较大。

注意:

  • 跳表在平均情况下的时间复杂度与平衡二叉树相当,均为 O(log N),但在最坏情况下,跳表的时间复杂度为  O(N),而平衡二叉树的时间复杂度仍然保持在 O(log N)。然而,在实际应用中,跳表的性能通常非常接近 O(log  N),并且在大多数情况下表现良好。

  • 虽然跳表在最坏情况下可能会占用更多的内存(因为每个节点可能有多个指针),但在实际应用中,跳表的内存开销通常是可以接受的。相比之下,平衡二叉树的每个节点只需要两个指针(左子节点和右子节点),但在插入和删除时需要进行额外的旋转操作,这些操作可能会导致更多的内存分配和释放。

Elasticesearch

ES为什么那么快?

  • 倒排索引:倒排索引将文档中的每个词项映射到包含该词项的文档列表。这种结构使得全文搜索非常高效,因为只需查找词项即可快速定位相关文档。
  • 分布式架构:Elasticsearch 是一个分布式系统,可以水平扩展以处理大量数据和高并发请求。数据被分成多个分片(shards),每个分片可以独立处理搜索和索引请求。通过将数据分布在多个节点上,并通过自动负载均衡机制,将请求分发到不同的节点,避免单点过载,这样也可以并行处理请求,显著提高性能。
  • 内存缓存:Elasticsearch 使用内存缓存来加速查询。常见的缓存机制包括:

(1)查询缓存:缓存最近的查询结果,以减少重复查询的开销。

(2)过滤缓存:缓存过滤条件的结果,以加快过滤操作。

(3)字段数据缓存:缓存字段数据,以加快聚合和排序操作。

  • 使用 Apache Lucene 作为其底层搜索引擎库:Lucene 是一个高性能的全文检索库,提供了高效的索引和搜索算法。Lucene 的优化技术包括:

(1)分段索引:将索引分成多个段,每个段独立管理,便于并行处理和优化。

(2)合并策略:定期合并小段索引,减少索引文件的数量,提高搜索性能。

(3)压缩:对索引数据进行压缩,减少磁盘 I/O 开销。


倒排索引如何实现的?

倒排索引将文档中的每个词项映射到包含该词项的文档列表。

在了解倒排索引的实现原理之前,我们有先看下倒排索引的构建过程是什么?

  1. 分词:将文档内容切分成词项(Term,我们常说的关键词)。
  2. 词项规范化:对词项进行标准化处理,如小写转换、去除停用词等。
  3. 构建词项字典:将每个词项插入词项字典,并记录其元数据。
  4. 构建倒排列表:将每个词项及其对应的文档ID和位置信息插入倒排列表。

倒排索引的实现原理由三部分组成:词项索引(Term Index)、词项字典(Term Dictionary)和倒排列表(Posting List)。

词项字典(Term Dictionary)

在实际生产场景中,比如 ES 最常用的日志分析,日志内容进行分词之后,可以得到多少的 term?---海量的Term,那么如何快速的在海量 term 中查询到对应的 term 呢?遍历一遍显然是不现实的。于是乎就有了词项字典(Term Dictionary)。

词项字典存储所有唯一的词项及其元数据。每个词项通常包含以下信息:

  • 词项本身(Term)
  • 倒排列表的指针(Pointer to Posting List)

词项字典通常使用一种高效的数据结构来实现,如 B+树 或 Trie(前缀树),以支持快速的查找和插入操作。


词项索引(Term Index)

现在词项字典(Term Dictionary)应该放在哪里?有人肯定想说放在内存里面吧?磁盘 io 那么慢。

但是如果把整个 term dictionary 放在内存里面会有什么后果呢?

内存很容易撑爆...ES 默认可是会对全部 text 字段进行索引,必然会消耗巨大的内存,为此 ES 针对索引进行了深度的优化。在保证执行效率的同时,尽量缩减内存空间的占用。于是乎就有了词项索引(Term Index)。


词项索引从数据结构上分类算是一个“Trie(前缀树)”,也就是我们常说的字典树,专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。

这棵树不会包含所有的 term,它包含的是 Term 的一些前缀(这也是字典树的使用场景,公共前缀)。通过 term index 可以快速地定位到 term dictionary 的某个 offset,然后从这个位置再往后顺序查找。

Lucene 在这里还做了两点优化,一是 term dictionary 在磁盘上面是分 block 保存的,一个 block 内部利用公共前缀压缩,比如都是 Ab 开头的单词就可以把 Ab 省去。二是 term index 在内存中是以 FST(finite state transducers)的数据结构保存的。


倒排列表(Posting List)

倒排列表存储包含特定词项的文档列表及其相关信息。每个倒排列表条目通常包含以下信息:

  • 文档ID(Document ID)
  • 词项在文档中的频率(Term Frequency in Document)
  • 词项在文档中的位置(Positions in Document)

倒排列表通常使用 数组 来实现,以支持高效的遍历和访问。

实际上倒排列表中还用到了一些优化技术,以减少存储空间和磁盘IO。如:对倒排列表进行增量编码方式(FOR)进行压缩,减少存储空间和 I/O 开销;跳表(Skip List):在倒排列表中添加跳表,加快范围查询的速度;缓存:缓存常用的词项和倒排列表,减少磁盘 I/O 次数。


以上,点亮【赞与在看】让我们心中充满力量、披荆斩棘、不惧未来!

【推荐阅读】

如何下保证MySQL数据库与Redis缓存数据一致性?

电商的核心数据库设计

虾皮面完,想开始摆烂

关于作者:一位热爱技术,并在职场与自媒体间探索的实践者,希望通过分享个人经验和见解,帮助更多人实现自我成长和价值。

原文作者:皇子谈技术;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2023-12-21

  • 博文量
    113
  • 访问量
    56510