字节跳动一面,汗流浃背。

大家好,我是二哥呀。

应该是上周,有球友在 VIP 群里报喜,说字节已经 OC 了。字节有很多优点,比如说 TikTok 真正做到了全球化,抖音更是成为微信后又一个国民级的应用。

但字节同时也存在一些问题,比如说加班严重,字节一年人间三年并非空穴来风;还有就是抖音的短视频几乎成为当下最没有养分的“精神安眠药”。

但对于绝大多数的小伙伴来说,能去字节这个互联网大厂,已经是职业生涯中最优的选择之一。

那接下来我们就以《Java 面试指南》中收录的字节跳动面经同学 21 抖音商城一面为例,来看看宇宙厂的面试官都喜欢问哪些问题,好做到心中有数。

能看得出来,基本上就是围绕着二哥一直强调的 Java 后端四大件展开,希望大家在准备八股的时候要有针对性,这样效率会高很多。

字节跳动抖音商城一面面经

redis为什么能处理高并发

Redis 的速度⾮常快,单机的 Redis 就可以⽀撑每秒十几万的并发,性能是 MySQL 的⼏⼗倍。原因主要有⼏点:

①、基于内存的数据存储,Redis 将数据存储在内存当中,使得数据的读写操作避开了磁盘 I/O。而内存的访问速度远超硬盘,这是 Redis 读写速度快的根本原因。

②、单线程模型,Redis 使用单线程模型来处理客户端的请求,这意味着在任何时刻只有一个命令在执行。这样就避免了线程切换和锁竞争带来的消耗。

③、IO 多路复⽤,基于 Linux 的 select/epoll 机制。该机制允许内核中同时存在多个监听套接字和已连接套接字,内核会一直监听这些套接字上的连接请求或者数据请求,一旦有请求到达,就会交给 Redis 处理,就实现了所谓的 Redis 单个线程处理多个 IO 读写的请求。

三分恶面渣逆袭:Redis使用IO多路复用和自身事件模型

④、高效的数据结构,Redis 提供了多种高效的数据结构,如字符串(String)、列表(List)、集合(Set)、有序集合(Sorted Set)等,这些数据结构经过了高度优化,能够支持快速的数据操作。

redis如何保证扩容过程中数据正常访问插入

Redis 集群使用数据分片和哈希槽的机制将数据分布到不同的节点上。集群扩容和缩容的关键,在于槽和节点之间的对应关系。

三分恶面渣逆袭:集群的伸缩

当需要扩容时,新的节点被添加到集群中,集群会自动执行数据迁移,以重新分布哈希槽到新的节点。数据迁移的过程可以确保在扩容期间数据的正常访问和插入。

三分恶面渣逆袭:扩容实例

当数据正在迁移时,客户端请求可能被路由到原有节点或新节点。Redis Cluster 会根据哈希槽的映射关系判断请求应该被路由到哪个节点,并在必要时进行重定向。

如果请求被路由到正在迁移数据的哈希槽,Redis Cluster 会返回一个 MOVED 响应,指示客户端重新路由请求到正确的目标节点。这种机制也就保证了数据迁移过程中的最终一致性。

当需要缩容时,Redis 集群会将槽从要缩容的节点上迁移到其他节点上,然后将要缩容的节点从集群中移除。

加分布式锁时redis如何保证不会发生冲突

①、使用 SET NX PX 或 SETNX 命令确保锁的获取是一个原子操作,同时设置锁的过期时间防止死锁。

比如说 SET lock_key unique_value NX PX 5000 命令,其中 NX 确保了原子操作,,如果 lock_key 已存在,SET 操作会返回 nil;PX 5000 设置过期时间为 5000 毫秒,避免死锁。

②、使用 Lua 脚本将锁的检查和释放操作封装为一个原子操作,确保安全地释放锁。

EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock_key unique_value

③、使用 Redlock 算法确保锁的正确获取和释放。

RLock lock = redisson.getLock("lock_key");
try {
    // 500ms 等待时间,10000ms 锁过期时间
    boolean isLocked = lock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
    if (isLocked) {
        // 执行需要同步的操作
    }
} finally {
    lock.unlock();
}

分布式锁过期怎么办?

在分布式系统中,使用 Redis 分布式锁时,锁通常会设置一个过期时间以防止死锁。然而,如果客户端在执行任务的过程中超过了锁的过期时间,那么锁将被 Redis 自动释放,可能会导致其他客户端获取到了同一个锁,引发严重的并发问题。

郭慕荣博客园:看门狗

Redisson 提供的分布式锁是支持锁自动续期的,也就是说,如果线程在锁到期之前还没有执行完,那么 Redisson 会自动给锁续期,避免锁的过期。

这被称为“看门狗”机制。

class RedissonWatchdogExample {
    public static void main(String[] args) {
        // 配置 Redisson 客户端
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);

        // 获取锁对象
        RLock lock = redisson.getLock("myLock");

        try {
            // 获取锁,默认看门狗机制会启动
            lock.lock();

            // 模拟任务执行
            System.out.println("Task is running...");
            Thread.sleep(40000); // 模拟长时间任务(40秒)

            System.out.println("Task completed.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }

        // 关闭 Redisson 客户端
        redisson.shutdown();
    }
}

看门狗启动后,每隔 10 秒会刷新锁的过期时间,将其延长到 30 秒,确保在锁持有期间不会因为过期而释放。

当任务执行完成时,客户端调用 unlock() 方法释放锁,看门狗也随之停止。

如果客户端宕机服务器如何感知?

每个客户端在 Redis 中维护一个特定的键(称为心跳键),用于表示客户端的健康状态。该键具有一个设置的超时时间,例如 10 秒。

客户端定期(如每 5 秒)更新这个心跳键的超时时间,保持它的存活状态,通常通过 SET 命令重设键的过期时间。

import redis.clients.jedis.Jedis;

public class ClientHeartbeat {
    private static final String HEARTBEAT_KEY = "client:heartbeat";
    private static final int EXPIRE_TIME = 10; // 10秒

    public static void main(String[] args) {
        // 创建 Redis 连接
        Jedis jedis = new Jedis("localhost");

        // 定时更新心跳键
        while (true) {
            try {
                // 设置心跳键并设置过期时间
                jedis.setex(HEARTBEAT_KEY, EXPIRE_TIME, "alive");

                // 打印心跳日志
                System.out.println("Heartbeat sent.");

                // 等待一段时间后再次发送心跳
                Thread.sleep(5000); // 每5秒发送一次心跳
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

Redis 服务端定期检查这个心跳键。如果发现该键已超时并被 Redis 自动删除,说明客户端可能已宕机。

import redis.clients.jedis.Jedis;

public class ServerMonitor {
    private static final String HEARTBEAT_KEY = "client:heartbeat";

    public static void main(String[] args) {
        // 创建 Redis 连接
        Jedis jedis = new Jedis("localhost");

        // 定期检查心跳键
        while (true) {
            try {
                // 检查心跳键是否存在
                if (jedis.exists(HEARTBEAT_KEY)) {
                    System.out.println("Client is alive.");
                } else {
                    System.out.println("Client is down or disconnected.");
                }

                // 每隔一段时间检查一次
                Thread.sleep(10000); // 每10秒检查一次
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

io多路复用

I/O 多路复用是一种同时监视多个文件描述符(或套接字)是否可读、可写的机制,当其中的某个或多个文件描述符准备好时,通知应用程序执行相应的 I/O 操作。常见的 I/O 多路复用机制包括 select、poll 和 epoll(在 Linux 中)等。

特性selectpollepoll
文件描述符限制FD_SETSIZE 限制无限制无限制
时间复杂度O(n)O(n)O(1)
数据复制需要需要不需要
工作方式线性扫描线性扫描事件通知
内核支持所有 UNIX 系统所有 UNIX 系统Linux 2.6 及以上版本
适用场景少量连接中等连接大量并发连接

比如说你是一名数学老师,上课时提出了一个问题:“今天谁来证明一下勾股定律?”

同学小王举手,你就让小王回答;小李举手,你就让小李回答;小张举手,你就让小张回答。

这种模式就是 IO 多路复用,你只需要在讲台上等,谁举手谁回答,不需要一个一个去问。

有盐先生:IO 多路复用

Redis 就是使用 epoll 这样的 I/O 多路复用机制,在单线程模型下实现高效的网络 I/O,从而支持高并发的请求处理。

mysql分为几层?binlog写入在哪一层

MySQL 的架构大致可以分为三层,从上到下依次是:连接层、服务层、和存储引擎层。

三分恶面渣逆袭:Redis 的基础架构

①、连接层主要负责客户端连接的管理,包括验证用户身份、权限校验、连接管理等。可以通过数据库连接池来提升连接的处理效率。

②、服务层是 MySQL 的核心,主要负责查询解析、优化、执行等操作。在这一层,SQL 语句会经过解析、优化器优化,然后转发到存储引擎执行,并返回结果。这一层包含查询解析器、优化器、执行计划生成器、缓存(如查询缓存)、日志模块等。

③、存储引擎层负责数据的实际存储和提取,是 MySQL 架构中与数据交互最直接的层。MySQL 支持多种存储引擎,如 InnoDB、MyISAM、Memory 等。

binlog写入在哪一层?

binlog 在服务层,负责记录 SQL 语句的变化。它记录了所有对数据库进行更改的操作,用于数据恢复、主从复制等。

sql的语法树解析

语法树(或抽象语法树,AST)是 SQL 解析过程中的中间表示,它使用树形结构表示 SQL 语句的层次和逻辑。语法树由节点(Node)组成,每个节点表示 SQL 语句中的一个语法元素。

  • 根节点:通常是 SQL 语句的主要操作,例如 SELECT、INSERT、UPDATE、DELETE 等。
  • 内部节点:表示语句中的操作符、子查询、连接操作等。例如,WHERE 子句、JOIN 操作等。
  • 叶子节点:表示具体的标识符、常量、列名、表名等。例如,users 表、id 列、常量 1 等。

以一个简单的 SQL 查询语句为例:

SELECT name, age FROM users WHERE age > 18;

这个查询语句的语法树可以表示为:

          SELECT
         /      \
     Columns     FROM
    /      \      |
  name      age  users
               |
             WHERE
               |
            age > 18

内容来源

  • 星球嘉宾三分恶的面渣逆袭:https://javabetter.cn/sidebar/sanfene/nixi.html
  • 二哥的 Java 进阶之路(GitHub 已有 12000+star):https://javabetter.cn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默王二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值