11.4八股
1、限流怎么做?
限流是防止系统过载的关键手段,核心是控制单位时间内的请求量,常见实现方式:
1.固定窗口计数器
原理:将时间划分为固定窗口(如 1 秒),记录窗口内请求数,超过阈值则限流。
优点:简单易实现;缺点:窗口边界可能出现流量突增(如前窗口末尾和当前窗口开头各发阈值请求,瞬间超 2 倍阈值)。
2.滑动窗口计数器
原理:将固定窗口拆分为多个小格子(如 1 秒拆分为 10 个 100ms 格子),滑动统计最近 1 秒内的请求总和,超过阈值则限流。
优化:缓解固定窗口的边界问题,精度取决于格子数量。
3.漏桶算法
原理:请求先进入 “漏桶”,桶以固定速率(如 100QPS)处理请求,超出桶容量则丢弃。
适用:控制输出速率(如 API 网关限制下游服务流量),平滑突发流量。
4.令牌桶算法
原理:系统按固定速率(如 100 个 / 秒)往桶中放令牌,请求需获取令牌才能处理,桶满则令牌溢出。
优势:支持突发流量(桶中积累的令牌可应对短时间峰值),是最常用的限流算法(如 Guava 的 RateLimiter)。
5.分布式限流
场景:多实例部署时,需全局统一限流(如全链路 QPS 限制)。
实现:基于 Redis(如用 INCR+EXPIRE 统计窗口请求,超过阈值则拒绝)或分布式锁。
2、身份证110101200111038313,电话00012062508
3、第三方登录(OAuth2.0协议)

基于 QQ 第三方登录的实际场景示例
以 “某款手游(假设叫「星际冒险」)支持 QQ 登录” 为例,完整还原 OAuth2.0 协议的落地流程:
一、前置准备:游戏平台完成腾讯开放平台注册
-
「星际冒险」的开发团队在腾讯开放平台(https://open.tencent.com/)注册开发者账号,提交游戏资质审核。
-
审核通过后,获取平台分配的 Client ID=123456(游戏的唯一标识)和 Client Secret=abcdef123456xyz(接口调用密钥,需保密)。
-
配置授权回调地址为 https://game.example.com/qq/login/callback(用户授权后,腾讯会跳转至此地址传递授权码),并申请授权范围为 “获取用户昵称、头像、OpenID”。
二、用户实操流程:从点击登录到进入游戏
1. 发起登录请求
用户打开「星际冒险」手游,点击登录界面的 “QQ 登录” 按钮。此时手游客户端会自动跳转到腾讯的 OAuth2.0 授权页面,跳转链接携带关键参数:https://graph.qq.com/oauth2.0/authorize?client_id=123456&redirect_uri=https://game.example.com/qq/login/callback&response_type=code&scope=get_user_info
-
client_id=123456:标识请求来自「星际冒险」游戏;
-
redirect_uri:指定授权后的回调地址;
-
response_type=code:表示需要腾讯返回 Authorization Code;
-
scope=get_user_info:表示需要获取用户的基础信息。
2. 用户同意授权
腾讯授权页面加载后,用户输入自己的 QQ 账号密码登录,随后页面提示 “「星际冒险」请求获取你的昵称、头像”,用户点击 “同意授权”。此时腾讯服务器生成一次性的 Authorization Code=789xyz,并自动重定向到之前配置的回调地址,同时携带该 Code:https://game.example.com/qq/login/callback?code=789xyz
3. 游戏服务器获取 Access Token
「星际冒险」的后端服务器接收到回调请求,提取出 Code=789xyz 后,向腾讯服务器发送请求,兑换 Access Token:请求地址:https://graph.qq.com/oauth2.0/token请求参数:
-
client_id=123456(游戏标识);
-
client_secret=abcdef123456xyz(游戏密钥,验证身份);
-
code=789xyz(授权码);
-
grant_type=authorization_code(表示用授权码兑换令牌);
-
redirect_uri=https://game.example.com/qq/login/callback(需与之前配置一致)。
腾讯服务器验证参数无误后,返回 Access Token:access_token=abc123xyz&expires_in=7200&refresh_token=def456uvw
-
access_token=abc123xyz:访问令牌,后续调用 API 需携带;
-
expires_in=7200:令牌有效期 2 小时;
-
refresh_token:刷新令牌,令牌过期后可用于重新获取 Access Token。
4. 游戏服务器获取用户信息
「星际冒险」后端用 Access Token 调用腾讯开放平台的 “获取用户信息” API:请求地址:https://graph.qq.com/user/get_user_info请求参数:
-
access_token=abc123xyz(访问令牌);
-
oauth_consumer_key=123456(游戏 Client ID);
-
openid=xxx(用户的 QQ 唯一标识,需先通过 Access Token 获取)。
腾讯服务器返回用户信息:
json
{"openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", // 用户唯一标识(不同游戏中该值唯一)"nickname": "游戏小能手", // 用户昵称"figureurl_qq_2": "https://qzonestyle.gtimg.cn/...", // 用户头像"gender": "男"
}
5. 游戏完成登录流程
1.「星际冒险」后端获取到 OpenID 后,查询自家数据库:
-
若数据库中已存在该 OpenID 对应的游戏账号(用户之前用 QQ 登录过),则直接创建游戏会话,返回登录成功,用户进入游戏;
-
若不存在该 OpenID(新用户),则自动为用户创建一个游戏账号,绑定该 OpenID,并默认填充昵称、头像等信息,随后返回登录成功,用户直接进入游戏。
三、用户体验总结
用户全程无需注册「星际冒险」的专属账号,仅需通过 QQ 授权即可快速登录,且无需向游戏暴露 QQ 密码,既便捷又安全。这也是 OAuth2.0 协议在第三方登录场景中的核心价值。
4、给你一张贴吧表 post(id, uid, title, like_cnt,comment_cnt,create_time(unix时间戳形式)
4.1 SQL 查询:三十天内评论超 5000、点赞超 500 的前五十条帖子标题 按点赞数降序
SELECT title
FROM post
WHERE create_time >= UNIX_TIMESTAMP(NOW() - INTERVAL 30 DAY) -- 三十天内AND comment_cnt > 5000 AND like_cnt > 500
ORDER BY like_cnt DESC -- 按点赞数降序
LIMIT 0, 50; -- 前五十条
4.2 索引设计及原因
核心索引
- 联合索引:idx_create_comment_like (create_time, comment_cnt, like_cnt)
- 原因:查询条件包含
create_time范围筛选、comment_cnt和like_cnt等值判断,联合索引可覆盖查询条件,避免全表扫描。 - 顺序逻辑:
create_time作为范围查询字段放在最前,后续字段在范围筛选后可继续过滤数据。
- 原因:查询条件包含
- 覆盖索引:idx_cover_query (create_time, comment_cnt, like_cnt, title)
- 原因:查询结果仅需
title,覆盖索引可直接从索引中获取数据,无需回表查询,提升效率。
- 原因:查询结果仅需
辅助索引
- 单字段索引:idx_uid (uid):若存在按用户查询帖子的场景,加速
WHERE uid = xxx的查询。
4.3 表结构优化
- 分表策略:
- 按
create_time分表:如按月分表(post_202501、post_202502),减少单表数据量,提升查询速度。 - 按
uid哈希分表:适合高频按用户查询的场景,均衡各表数据分布。
- 按
- 字段优化:
like_cnt和comment_cnt采用无符号整数类型(INT UNSIGNED),节省存储空间。create_time可保留 Unix 时间戳(BIGINT),或转为DATETIME类型,便于日期函数直接操作。
- 冷热数据分离:将三十天内的热数据保留在主表,历史冷数据迁移至归档表,提升主表查询效率。
4.4 缓存设计:存储五十条目标帖子
缓存方案
- 缓存选型:使用 Redis 作为缓存,支持高并发读写和过期淘汰。
- 缓存 Key 设计:
post:top50:hot(标识 “三十天内热门帖子前五十”)。 - 缓存 Value 结构:采用 Hash 类型,field 为帖子
id,value 为帖子 JSON 字符串(包含title、like_cnt、comment_cnt等字段)。 - 缓存更新策略:
- 主动更新:帖子点赞 / 评论数变化时,同步更新缓存中的对应字段。
- 过期淘汰:设置缓存过期时间为 1 小时,到期后重新查询数据库并刷新缓存,保证数据时效性。
- 预热加载:系统启动时,自动执行查询 SQL 并将结果存入缓存,避免首次查询耗时过长。


5、枚举类的特征
以 “订单状态” 枚举类 OrderStatus 为例,直观展示枚举类的核心特征:
// 定义订单状态枚举类
public enum OrderStatus {// 枚举常量(实例),必须在类的第一行定义UNPAID("未支付", 1),PAID("已支付", 2),SHIPPED("已发货", 3),RECEIVED("已收货", 4),CANCELLED("已取消", 5);// 自定义属性private final String desc; // 状态描述private final int code; // 状态编码// 1. 构造方法默认私有(不可实例化)private OrderStatus(String desc, int code) {this.desc = desc;this.code = code;}// 自定义方法public String getDesc() {return desc;}public int getCode() {return code;}public static void main(String[] args) {// 2. 继承自 Enum 类(可调用 Enum 的方法)System.out.println(OrderStatus.PAID.getClass().getSuperclass()); // 输出:class java.lang.Enum(证明默认继承 Enum)// 3. 线程安全(枚举实例在类加载时初始化,唯一且不可变)OrderStatus s1 = OrderStatus.SHIPPED;OrderStatus s2 = OrderStatus.SHIPPED;System.out.println(s1 == s2); // 输出:true(单例,地址相同)// 4. 支持 switch 语句OrderStatus status = OrderStatus.RECEIVED;switch (status) {case UNPAID:System.out.println("订单未支付");break;case PAID:System.out.println("订单已支付");break;case RECEIVED:System.out.println("订单已收货"); // 输出此句break;// 其他状态...}// 5. 自带常用方法// values():返回所有枚举实例数组(按定义顺序)OrderStatus[] allStatus = OrderStatus.values();for (OrderStatus s : allStatus) {System.out.print(s + " "); // 输出:UNPAID PAID SHIPPED RECEIVED CANCELLED}// ordinal():返回实例的序号(从 0 开始)System.out.println("\nPAID 的序号:" + OrderStatus.PAID.ordinal()); // 输出:1// valueOf():根据名称获取枚举实例OrderStatus cancel = OrderStatus.valueOf("CANCELLED");System.out.println(cancel.getDesc()); // 输出:已取消}
}
特征对应说明
-
不可实例化
- 构造方法被
private修饰(即使不写,默认也是 private),无法通过new OrderStatus(...)创建实例,只能使用类中定义的UNPAID、PAID等常量。
- 构造方法被
-
继承自 Enum 类
- 无需显式写
extends Enum,但通过getSuperclass()可验证父类是java.lang.Enum。 - 由于 Java 单继承特性,枚举类不能再继承其他类(如
enum OrderStatus extends BaseClass会报错),但可实现接口(如enum OrderStatus implements Serializable)。
- 无需显式写
-
线程安全
- 枚举实例在类加载阶段(ClassLoader 的
loadClass方法)初始化,且仅初始化一次(单例),不存在多线程并发创建的问题,天然线程安全。
- 枚举实例在类加载阶段(ClassLoader 的
-
支持 switch 语句
- 可直接在
switch中使用枚举常量(如case PAID),比用字符串(case "PAID")更高效(底层通过序号判断,而非字符串比较)。
- 可直接在
-
自带常用方法
values():动态获取所有枚举实例(常用于遍历);ordinal():获取实例定义时的顺序(注意:若后续调整定义顺序,此值会变化,谨慎用于业务逻辑);valueOf(String name):将字符串转换为枚举实例(名称必须完全匹配,否则抛IllegalArgumentException)。
6、前端防刷能解决的问题与局限性
1. 能解决的问题
- 拦截普通用户的无效操作:防止用户因误操作、重复点击发起大量请求(如 1 秒内点击 10 次 “抢购” 按钮),减少前端到后端的无效流量。
- 限制未授权的提前请求:通过前端倒计时、按钮置灰,避免用户在活动开始前发起请求,减轻服务器预热阶段的压力。
- 过滤简单的前端篡改:通过前端校验(如表单参数格式、请求频率),拦截部分未按规则构造的请求(如手动修改请求参数中的 “商品 ID”)。
- 优化用户体验:通过加载动画、倒计时提示,引导用户规范操作,减少因操作混乱导致的额外请求。
2. 局限性
- 无法抵御技术型刷量:攻击者可绕过前端(如直接模拟 HTTP 请求、修改前端代码删除限流逻辑),直接向后端发起请求,前端防刷完全失效。
- 依赖客户端环境:前端代码运行在用户设备上,攻击者可通过浏览器控制台、抓包工具(如 Fiddler、Charles)篡改参数、禁用 JS,突破前端限制。
- 不具备分布式一致性:前端防刷是 “单机级” 控制(如单浏览器的请求频率限制),无法应对多设备、多 IP 的分布式刷量(如攻击者用 100 台设备同时请求)。
- 功能兼容性问题:部分前端防刷逻辑(如 JS 加密、Canvas 验证)可能在老旧浏览器或特殊设备上失效,影响正常用户体验。
