近几年字节飞书测开部分面试题整理

文章目录

  • 一、面试问题
    • [1. 创建索引](#1. 创建索引)
    • [2. 拦截器(Interceptor)和过滤器(Filter)的区别](#2. 拦截器(Interceptor)和过滤器(Filter)的区别)
    • [3. 为什么jwt令牌代替session?](#3. 为什么jwt令牌代替session?)
    • [4. 有一个100行的数据,和一个1万行的数据,写sql 的时候要注意什么?](#4. 有一个100行的数据,和一个1万行的数据,写sql 的时候要注意什么?)
    • [5. redis和memcache的区别](#5. redis和memcache的区别)
    • [6. linux部分命令](#6. linux部分命令)
    • [7. 飞书视频测试功能点](#7. 飞书视频测试功能点)
    • [8. 飞书视频性能保障](#8. 飞书视频性能保障)
    • [9. 在线协同文档的测试用例](#9. 在线协同文档的测试用例)
    • [10. 飞书低代码平台页面的性能保障工作](#10. 飞书低代码平台页面的性能保障工作)
  • 二、SQL
    • [1. 查出表中学生成绩最好的学生信息](#1. 查出表中学生成绩最好的学生信息)
    • [2. 2020/12/1 10:28:31这种数据按照小时统计每个不同小时出现数据个数](#2. 2020/12/1 10:28:31这种数据按照小时统计每个不同小时出现数据个数)
    • [3. 查询姓王 的同学的个数、年龄第二大的同学](#3. 查询姓王 的同学的个数、年龄第二大的同学)
  • 三、手撕算法
    • [1. 二分搜索二维数组](#1. 二分搜索二维数组)
    • [2. K个一组翻转链表](#2. K个一组翻转链表)
    • [3. 全排列](#3. 全排列)
    • [4. 轮转数组](#4. 轮转数组)
    • [5. 合并有序链表](#5. 合并有序链表)
    • [6. 下一个排列](#6. 下一个排列)
    • [7. 最长公共前缀](#7. 最长公共前缀)
    • [8. 二叉树的非递归前序遍历](#8. 二叉树的非递归前序遍历)
  • 关于本博客内容的说明

一、面试问题

1. 创建索引

sql 复制代码
-- 主键索引
ALTER TABLE users ADD PRIMARY KEY (id);

-- 唯一索引
ALTER TABLE table_name ADD UNIQUE (column);
ALTER TABLE table_name ADD UNIQUE (column1,column2);

-- 普通索引
ALTER TABLE table_name ADD INDEX index_name (column);
ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);

-- 全文索引
ALTER TABLE table_name ADD FULLTEXT (column);

2. 拦截器(Interceptor)和过滤器(Filter)的区别

  1. 作用位置不同
    • 过滤器(Filter) :基于Servlet规范,工作在Web容器层(如Tomcat),对所有请求/响应进行预处理和后处理。
    • 拦截器(Interceptor) :基于框架(如Spring MVC),工作在应用层,在控制器(Controller)方法前后执行。
  2. 控制粒度不同
    • Filter:可拦截所有HTTP请求(静态资源、Servlet、JSP等),但无法获取具体的控制器和方法信息。
    • Interceptor :可精确控制到某个Controller或方法,并能获取参数、返回值等上下文信息。
  3. 依赖框架不同
    • Filter:是JavaEE标准,不依赖Spring等框架。
    • Interceptor :通常依赖框架(如Spring的HandlerInterceptor)。
  4. 执行顺序
    1. Filter → 2. Interceptor → 3. Controller → 4. Interceptor → 5. Filter
  5. 常见用途
    • Filter:全局编码设置、跨域处理(CORS)、日志记录、权限拦截(粗粒度)。
    • Interceptor:登录校验、参数校验、审计日志(细粒度)。

一句话总结

  • Filter 是Web容器的"门卫",Interceptor是Spring的"管家",前者更底层,后者更灵活。

3. 为什么jwt令牌代替session?

  1. 无状态 & 可扩展性

    • Session:依赖服务端存储(如Redis),集群环境下需共享Session,增加复杂度。
    • JWT:令牌自带用户信息(签名加密),服务端无需存储,天然支持分布式。
  2. 跨域/跨服务支持

    • Session:基于Cookie,受同源策略限制,跨域需额外配置(如CORS)。
    • JWT :可放在HTTP Header(如Authorization),轻松实现跨域、微服务间认证。
  3. 性能优势

    • Session:每次请求需查询服务端存储(如Redis),增加I/O开销。
    • JWT:只需本地验签(如HS256/RSA),减少服务端压力。
  4. 移动端/API友好

    • Session:移动端(APP)需额外适配Cookie机制。
    • JWT:直接通过Header传递,适配RESTful API、APP、第三方调用。
  5. 安全性权衡

    • Session:服务端可控(强制失效、续期)。
    • JWT:需自行处理令牌过期(如Refresh Token)、无法主动废止(需短有效期+黑名单)。

总结

JWT适合分布式、无状态、跨平台 场景,Session更适合强控制、短会话需求。实际可结合使用(如JWT存用户ID,关键操作二次验证)。

4. 有一个100行的数据,和一个1万行的数据,写sql 的时候要注意什么?

性能优化重点

  1. 索引策略

    • 对1万行表的关键查询字段建立索引
    • 小表(100行)通常不需要索引
  2. JOIN操作优化

    sql 复制代码
    -- 优先小表驱动大表
    SELECT * FROM small_table s JOIN large_table l ON s.id = l.sid
  3. 查询范围控制

    • 对大表使用WHERE条件尽早过滤数据
    • 避免SELECT *,只查询必要字段

具体注意事项

  1. 执行计划分析

    • 对1万行表查询使用EXPLAIN分析执行计划
    • 关注是否使用了合适的索引
  2. 分页处理

    sql 复制代码
    -- 大表必须分页查询
    SELECT * FROM large_table LIMIT 100 OFFSET 0
  3. 批量操作

    • 对大表避免循环单条操作,使用批量INSERT/UPDATE
  4. 事务管理

    • 大表操作使用合理的事务大小,避免长事务
  5. 内存考虑

    • 1万行数据可能超出内存时,考虑流式处理

5. redis和memcache的区别

对比维度 Redis Memcached
数据结构 支持多种数据结构(String、Hash、List、Set、Sorted Set等) 仅支持简单的 Key-Value 字符串
持久化 支持 RDB(快照)和 AOF(日志)持久化,数据可恢复 纯内存存储,重启后数据丢失
性能 单线程模型(避免锁竞争),适合复杂操作和高并发读 多线程模型,纯 KV 场景下吞吐量更高
集群支持 原生支持集群(Redis Cluster)、主从复制 无内置集群,依赖客户端分片(如一致性哈希)
内存管理 支持 LRU 淘汰策略,可配置最大内存限制 固定内存分配,超出时自动淘汰旧数据
适用场景 缓存、会话存储、消息队列、排行榜、实时分析等复杂场景 简单的键值缓存,如 HTML 片段、数据库查询结果缓存
事务支持 支持简单事务(MULTI/EXEC)和 Lua 脚本 无事务支持
网络模型 单线程 Reactor 模式(6.0 后支持多线程 I/O) 多线程处理请求
数据大小 适合存储中小型数据(Value 通常 ≤ 1MB) 适合存储较大的 Value(默认支持 1MB,可调整)

6. linux部分命令

  1. 统计一个文件中某个字符串出现次数

    shell 复制代码
    grep -o "要搜索的字符串" 文件名 | wc -l
  2. 某个端口号被哪个进程占用

    shell 复制代码
    netstat -tulnp | grep ":端口号"
    lsof -i :端口号
  3. 显示所有进程

    shell 复制代码
    ps aux
    top

7. 飞书视频测试功能点

  1. 功能测试
    • 基础功能:发起/加入视频会议、摄像头/麦克风开关、屏幕共享、会议录制。
    • 权限控制:主持人权限(禁言、踢人)、参会者权限(发言、聊天)。
    • UI交互:按钮状态、布局适配、横竖屏切换。
  2. 兼容性测试
    • 设备:iOS/Android/Windows/macOS、不同分辨率。
    • 浏览器:Chrome/Firefox/Safari/Edge(Web端)。
    • 版本兼容:新旧版本飞书客户端互通。
  3. 性能测试
    • 网络:弱网(3G/高延迟/丢包)下的视频流畅度。
    • 负载:多人会议(50+/100+)时的CPU、内存占用。
    • 稳定性:长时间会议(2h+)是否卡顿或崩溃。
  4. 安全测试
    • 加密:视频流是否端到端加密。
    • 防入侵:非法链接/密码爆破防护。
    • 隐私:会议链接泄露后能否二次加入。
  5. 异常测试
    • 断网恢复:网络中断后自动重连。
    • 设备异常:摄像头/麦克风被占用时的提示。
    • 冲突场景:来电/其他APP通知是否会中断会议。

8. 飞书视频性能保障

  1. 网络优化(最高优先级)
    • 弱网适配:动态码率调整(如WebRTC的拥塞控制),确保高延迟/丢包下仍流畅。
    • CDN加速:就近接入节点,降低跨国/跨运营商延迟。
    • 协议优化:采用UDP(如QUIC)减少重传,提升实时性。
  2. 端侧性能
    • 设备兼容:针对低端机型优化编解码(H.264/AV1)、降低CPU/内存占用。
    • 功耗控制:智能降帧率/分辨率,避免过热耗电。
  3. 服务端负载
    • 分布式架构:媒体服务器分区域部署,避免单点过载。
    • 弹性扩缩容:自动扩容应对突发流量(如大型直播)。
  4. 关键指标监控
    • QoS指标:帧率(≥15fps)、延迟(≤500ms)、卡顿率(<5%)。
    • 告警机制:实时监控异常(如服务器CPU>80%),自动降级策略。

9. 在线协同文档的测试用例

  1. 基础功能测试
    1. 文档编辑与保存
      • 新建文档:验证能否正确创建文本、表格、幻灯片等。
      • 编辑内容:输入文本、插入图片、调整格式(字体、颜色、段落)。
      • 自动保存:检查编辑后是否自动保存,断网恢复后数据是否同步。
    2. 多人实时协作
      • 多人同时编辑:多个用户同时修改同一段落,检查冲突处理(如OT算法)。
      • 光标位置显示:不同用户的编辑光标是否实时可见。
      • 操作同步延迟 :修改后,其他用户能否在 1s 内 看到更新。
    3. 评论与批注
      • 添加评论:@他人、回复评论、删除评论。
      • 批注模式:不同权限用户能否查看/修改批注。
  2. 权限与安全测试
    1. 访问权限
      • 只读/可编辑/管理员:不同角色能否正确操作(如仅查看、可修改、可分享)。
      • 链接分享:公开链接、指定人员访问、密码保护链接。
    2. 数据安全
      • 内容加密:传输是否使用 HTTPS,存储是否加密。
      • 防篡改:无权限用户尝试修改时是否被拒绝。
  3. 性能测试
    • 高并发编辑:100+ 用户同时编辑时,服务器是否稳定。
    • 大文档加载:10MB+ 文档的打开速度(应 ≤3s)。
    • 弱网环境:3G/高延迟下,操作是否正常同步。
  4. 兼容性测试
    • 浏览器:Chrome/Firefox/Safari/Edge。
    • 设备:PC/手机/平板,不同分辨率适配。
    • 操作系统:Windows/macOS/iOS/Android。
  5. 异常场景测试
    1. 网络异常
      • 断网恢复:编辑过程中断网,恢复后数据是否同步。
      • 冲突处理:两人同时修改同一行,是否提示冲突或自动合并。
    2. 数据恢复
      • 版本历史:能否回滚到任意历史版本。
      • 误删恢复:删除文档后,能否在回收站恢复。

10. 飞书低代码平台页面的性能保障工作

保障飞书低代码平台页面的性能需要从前端渲染后端服务数据层网络传输监控运维等多个角度协同优化。

  1. 前端性能优化
    1. 减少渲染负载
      • 组件懒加载 :按需加载非首屏组件(如动态import()或React.lazy)。
      • 虚拟列表(Virtualized List) :长列表渲染仅展示可视区域元素(如使用react-window)。
      • DOM复用:避免重复渲染,合理使用React.memo或Vue的v-once。
    2. 资源优化
      • 代码拆分(Code Splitting):基于路由或功能拆包,减少首屏资源体积。
      • 静态资源CDN加速:JS/CSS/图片等通过CDN分发,启用HTTP/2 + Brotli压缩。
      • 图标与图片优化:使用SVG代替位图,懒加载非关键图片,WebP格式适配。
    3. 缓存策略
      • 本地缓存:利用IndexedDB或LocalStorage缓存元数据(如表单配置)。
      • Service Worker:实现离线可用性,加速重复访问。
  2. 后端服务优化
    1. 接口性能
      • GraphQL替代REST:按需查询字段,减少数据传输量。
      • 批量请求合并:多个独立API合并为单个请求(如Facebook的Batch API)。
      • 接口缓存:高频读取数据(如组织架构)使用Redis缓存,设置合理TTL。
    2. 计算优化
      • 预计算与预聚合:复杂报表数据提前计算,避免实时处理。
      • 异步任务队列:耗时操作(如导出Excel)转为后台任务,通过WebSocket通知结果。
    3. 服务治理
      • 自动扩缩容:基于CPU/内存指标动态调整K8s Pod副本数。
      • 熔断与降级:依赖服务故障时降级返回缓存数据或简化逻辑(Hystrix/Sentinel)。
  3. 数据层优化
    1. 数据库查询
      • 索引优化:为低代码动态查询字段添加组合索引,避免全表扫描。
      • 读写分离:查询走从库,写入走主库(如MySQL Group Replication)。
      • 分库分表:大企业数据按租户ID或业务维度拆分。
    2. 实时同步
      • 增量更新:通过Binlog或CDC(如Debezium)同步数据变更,避免全量拉取。
      • OT/CRDT算法:文档协同场景使用操作转换或冲突-free 数据类型。
  4. 网络传输优化
    • 协议升级:HTTP/3(QUIC)减少连接延迟,尤其适合移动端。
    • 数据压缩:接口Payload使用Protocol Buffers或MessagePack。
    • 边缘计算:动态内容通过边缘节点(如Cloudflare Workers)就近处理。
  5. 监控与持续优化
    1. 性能指标监控
      • 前端埋点:采集FP/FCP/LCP等Web Vitals,监听长任务(Long Tasks)。
      • 全链路追踪:通过OpenTelemetry追踪API调用链,定位慢请求。
      • 日志分析:ELK或Prometheus + Grafana监控慢查询、异常错误。
    2. 性能测试
      • 压力测试:模拟多租户并发操作(如JMeter),验证服务极限。
      • A/B测试:对比不同优化策略(如缓存策略)的实际效果。
    3. 渐进式优化
      • 性能评分卡:定期审计关键路径(如表单加载→提交全流程)。
      • 代码分析工具:ESLint插件检测性能反模式(如未销毁的Event Listener)。
  6. 低代码特定场景优化
    • 动态表单渲染:预编译JSON Schema为高效渲染代码,避免运行时解析。
    • 逻辑引擎性能:将可视化流程转换为WASM模块执行,提升计算效率。
    • 沙箱隔离:用户自定义脚本运行在V8 Isolate或WebWorker中,避免阻塞主线程。

总结:性能保障体系

维度 关键措施 工具/技术示例
前端 懒加载、虚拟列表、资源压缩 React.lazy, Webpack, CDN
后端 接口缓存、异步任务、自动扩缩容 Redis, Kafka, Kubernetes HPA
数据 索引优化、分库分表、增量同步 MySQL索引, Debezium
网络 HTTP/3、边缘计算、数据压缩 QUIC, Cloudflare Workers
监控 全链路追踪、Web Vitals埋点 OpenTelemetry, Sentry

二、SQL

1. 查出表中学生成绩最好的学生信息

sql 复制代码
SELECT * FROM students ORDER BY score DESC LIMIT 1;
SELECT * FROM students WHERE score = (SELECT MAX(score) FROM students);

2. 2020/12/1 10:28:31这种数据按照小时统计每个不同小时出现数据个数

sql 复制代码
SELECT 
    DATE_FORMAT(time_column, '%Y-%m-%d %H:00:00') AS hour,
    COUNT(*) AS count
FROM 
    your_table
WHERE 
    time_column BETWEEN '2020-12-01' AND '2020-12-02'  -- 可选时间范围
GROUP BY 
    DATE_FORMAT(time_column, '%Y-%m-%d %H')
ORDER BY 
    hour;

3. 查询姓王 的同学的个数、年龄第二大的同学

sql 复制代码
SELECT COUNT(*) AS 王姓同学人数 FROM students WHERE name LIKE '王%';
SELECT * FROM students WHERE age < (SELECT MAX(age) FROM students) ORDER BY age DESC LIMIT 1;

三、手撕算法

1. 二分搜索二维数组

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int i = 0, j = matrix[0].length - 1;
        while (i <= matrix.length - 1 && j >= 0) {
            if (matrix[i][j] == target) {
                return true;
            }
            if (matrix[i][j] < target) {
                i++;
                continue;
            }
            if (matrix[i][j] > target) {
                j--;
                continue;
            }
        }
        return false;
    }
}

2. K个一组翻转链表

java 复制代码
// 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null) {
            return null;
        }
        ListNode pre = head, index = head.next; // 双指针操纵反转
        int m = 0;
        while (++m < k && index != null) {
            pre.next = index.next;
            index.next = head;
            head = index;
            index = pre.next;
        }
        if (m < k) {
            return reverseKGroup(head, m);
        }
        pre.next = reverseKGroup(index, k);
        return head;
    }
}

3. 全排列

java 复制代码
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }
        List<Integer> path = new ArrayList<>();
        boolean[] used = new boolean[len];
        dfs(nums, len, 0, res, path, used);
        return res;
    }

    private void dfs(int[] nums, int len, int depth, List<List<Integer>> res, List<Integer> path,
            boolean[] used) {
        if (depth == len) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < len; i++) {
            if (!used[i]) {
                path.add(nums[i]);
                used[i] = true;
                dfs(nums, len, depth + 1, res, path, used);
                used[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }
}

4. 轮转数组

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        k = k % n;
        // 0 - n-1 翻转
        reverse(nums, 0, n - 1);
        // 0 - k-1 翻转
        reverse(nums, 0, k - 1);
        // k - n-1 翻转
        reverse(nums, k, n - 1);
    }

    public void reverse(int[] nums, int start, int end) {
        int tmp;
        while (start < end) {
            tmp = nums[start];
            nums[start] = nums[end];
            nums[end] = tmp;
            start++;
            end--;
        }
    }
}

5. 合并有序链表

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if (list1 == null) {
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        if (list1.val < list2.val) {
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }
}

6. 下一个排列

java 复制代码
class Solution {
    public void nextPermutation(int[] nums) {
        // 1.从后向前查找第一个顺序对(i, i + 1),满足a[i] < a[i + 1],a[i]即为较小数,
        // 且[i + 1, n]为下降序列
        int i = nums.length - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            // 2. 在区间[i + 1, n]中从后向前查找第一个元素j,满足a[i] < a[j]
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            swap(nums, i, j);
        }
        // 如果在步骤 1 找不到顺序对,说明当前序列已经是一个降序序列,
        // 即最大的序列,我们直接跳过步骤 2 执行步骤 3,即可得到最小的升序序列。
        reverse(nums, i + 1);
    }

    public void swap(int[] nums, int start, int end) {
        int tmp = nums[start];
        nums[start] = nums[end];
        nums[end] = tmp;
    }

    public void reverse(int[] nums, int start) {
        int end = nums.length - 1;
        while (start < end) {
            swap(nums, start, end);
            start++;
            end--;
        }
    }
}

7. 最长公共前缀

java 复制代码
class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs.length == 0) {
            return "";
        }
        String res = strs[0];
        for (int i = 1; i < strs.length; i++) {
            int j = 0;
            for (; j < res.length() && j < strs[i].length(); j++) {
                if (res.charAt(j) != strs[i].charAt(j)) {
                    break;
                }
            }
            res = res.substring(0, j);
            if (res.equals("")) {
                return res;
            }
        }
        return res;
    }
}

8. 二叉树的非递归前序遍历

java 复制代码
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode node = root;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                res.add(node.val);
                stack.push(node);
                node = node.left;
            }
            node = stack.pop();
            node = node.right;
        }
        return res;
    }
}

关于本博客内容的说明

  1. 内容来源

    本文整理的时间有限,部分答案是结合DeepSeek的技术解读与个人理解综合而成。需要说明的是,这些问题在实际面试中(至少在我的经历里)大多并未被直接问到------毕竟面试内容往往取决于具体岗位需求和面试官风格。

  2. 算法题部分

    手撕代码环节中,我原以为面试官会考察第二道题目(LeetCode困难题),因为从该题的评论区来看,包括字节在内的多家大厂都曾将其作为考题。

    另外,我提供的示例代码时间复杂度可能并非最优解,这里主要是记录当前算法水平下我能快速实现的思路。毕竟面试中,清晰的逻辑表达和正确性通常比极致优化更重要(当然,如果能兼顾就更好了)。

相关推荐
Tom Boom5 天前
43. 远程分布式测试实现
分布式·测试开发·自动化·webdriver·自动化测试框架开发·分布式测试
Tom Boom7 天前
38. 自动化测试异步开发之编写客户端异步webdriver接口类
测试开发·webdriver·自动化测试框架开发·分布式测试
Tom Boom12 天前
23. 装饰器应用之测试用例的依赖实现
服务器·网络·测试开发·测试用例·自动化测试框架开发·wraps
Tom Boom13 天前
21. 自动化测试框架开发之Excel配置文件的测试用例改造
测试开发·selenium·测试工具·测试用例·excel·自动化测试框架开发·po改造
Tom Boom19 天前
19. 结合Selenium和YAML对页面实例化PO对象改造
python·测试开发·selenium·测试工具·自动化测试框架开发·po改造
Tom Boom1 个月前
14. 原生测试框架Unittest的skip、skipIf、skipUnless的使用
自动化测试·python·测试开发·unittest·自动化测试框架开发
阿华的代码王国3 个月前
【性能测试】Jmeter下载安装、环境配置-小白使用手册(1)
测试开发·jmeter·性能测试·测试·jmeter使用
LUCIAZZZ3 个月前
Java测试框架Mockito快速入门
java·测试开发·测试工具·spring·单元测试·log4j
霍格沃兹测试开发学社测试人社区3 个月前
性能测试丨JMeter 分布式加压机制
软件测试·分布式·测试开发·jmeter