Organized 50 interview questions into 12 categories: - 01-分布式系统 (9 files): 分布式事务, 分布式锁, 一致性哈希, CAP理论, etc. - 02-数据库 (2 files): MySQL索引优化, MyBatis核心原理 - 03-缓存 (5 files): Redis数据结构, 缓存问题, LRU算法, etc. - 04-消息队列 (1 file): RocketMQ/Kafka - 05-并发编程 (4 files): 线程池, 设计模式, 限流策略, etc. - 06-JVM (1 file): JVM和垃圾回收 - 07-系统设计 (8 files): 秒杀系统, 短链接, IM, Feed流, etc. - 08-算法与数据结构 (4 files): B+树, 红黑树, 跳表, 时间轮 - 09-网络与安全 (3 files): TCP/IP, 加密安全, 性能优化 - 10-中间件 (4 files): Spring Boot, Nacos, Dubbo, Nginx - 11-运维 (4 files): Kubernetes, CI/CD, Docker, 可观测性 - 12-面试技巧 (1 file): 面试技巧和职业规划 All files renamed to Chinese for better accessibility and organized into categorized folders for easier navigation. Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
28 KiB
28 KiB
时间轮算法
数据结构原理
什么是时间轮?
时间轮(Timing Wheel)是一种用于定时任务调度的数据结构,通过环形队列和层级结构实现高效的时间管理。它特别适合处理大量延迟任务和周期性任务,在分布式系统中广泛应用。
时间轮的核心概念
- 轮槽(Slot):时间轮的基本单位,每个槽代表一个时间片
- 指针(Pointer):当前时间槽的指针,顺时针移动
- 任务(Task):需要执行的任务,包含执行时间信息
- 层级结构:多级时间轮处理不同时间跨度的任务
时间轮的工作原理
- 任务添加:根据任务延迟时间计算放入的槽位
- 任务执行:指针到达槽位时,执行该槽位所有任务
- 指针移动:每过一个时间片,指针移动到下一个槽位
- 延迟计算:任务延迟时间 = 当前时间到执行时间的差值
图解说明
单层时间轮示例(槽位数=8,时间片=1秒):
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | | | | | | |
|task1|task2| |task3| | |task4| |
| | | | | | | | |
+-----+-----+-----+-----+-----+-----+-----+-----+
↑
指针
当前时间:0秒
多层时间轮示例
多层时间轮(3层):
Layer 3 (1小时/槽): [0][1][2][3][4][5] -> 当前:0
Layer 2 (1分钟/槽): [0][1][2][3][4][5][6][7] -> 当前:0
Layer 1 (1秒/槽): [0][1][2][3][4][5][6][7] -> 当前:0
Layer 0 (100ms/槽): [0][1][2][3][4][5][6][7] -> 当前:0
任务添加流程
添加任务(延迟 350ms):
1. Layer 0: 350ms / 100ms = 3.5 -> 放入槽 4
2. 如果当前指针 > 槽位,放入上一层
3. 继续处理,直到找到合适的层级
Java 代码实现
基础时间轮实现
import java.util.*;
import java.util.concurrent.*;
public class TimingWheel {
private final int slotSize; // 槽位数量
private final long timePerSlot; // 每个槽位的时间间隔(毫秒)
private final List<TimerTask>[] slots; // 时间轮槽
private final AtomicInteger currentSlot; // 当前槽位索引
private final ExecutorService executor; // 任务执行器
private final TimingWheel overflowWheel; // 上层时间轮
@SuppressWarnings("unchecked")
public TimingWheel(int slotSize, long timePerSlot, ExecutorService executor) {
this.slotSize = slotSize;
this.timePerSlot = timePerSlot;
this.slots = (List<TimerTask>[]) new List[slotSize];
this.currentSlot = new AtomicInteger(0);
this.executor = executor;
this.overflowWheel = null;
for (int i = 0; i < slotSize; i++) {
slots[i] = new ArrayList<>();
}
// 启动时间轮线程
new Thread(this::rotate).start();
}
// 添加任务
public void addTask(TimerTask task) {
if (task.getDelay() <= timePerSlot) {
int targetSlot = (currentSlot.get() + (int)(task.getDelay() / timePerSlot)) % slotSize;
slots[targetSlot].add(task);
} else {
// 处理跨槽位任务
if (overflowWheel == null) {
// 创建上层时间轮
overflowWheel = new TimingWheel(slotSize, timePerSlot * slotSize, executor);
}
overflowWheel.addTask(task);
}
}
// 时间轮旋转
private void rotate() {
while (true) {
try {
Thread.sleep(timePerSlot);
// 获取当前槽位
int slot = currentSlot.getAndIncrement() % slotSize;
// 执行槽位中的任务
List<TimerTask> tasks = slots[slot];
if (!tasks.isEmpty()) {
List<TimerTask> taskList = new ArrayList<>(tasks);
tasks.clear();
for (TimerTask task : taskList) {
executor.submit(() -> task.execute());
}
}
// 处理溢出任务
if (overflowWheel != null) {
overflowWheel.rotateTasks();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
// 旋转溢出任务
private void rotateTasks() {
// 从当前槽位取出的任务
}
}
// 定时任务接口
interface TimerTask {
long getDelay(); // 获取延迟时间(毫秒)
void execute(); // 执行任务
}
// 具体任务实现
class DelayedTask implements TimerTask {
private final long delay;
private final Runnable task;
public DelayedTask(long delay, Runnable task) {
this.delay = delay;
this.task = task;
}
@Override
public long getDelay() {
return delay;
}
@Override
public void execute() {
task.run();
}
}
增强型时间轮实现
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
public class EnhancedTimingWheel {
private final int tier; // 当前层级
private final int slotSize; // 槽位数量
private final long timePerSlot; // 每个槽位的时间间隔
private final long wheelTimeout; // 轮超时时间
private final List<TimerTaskEntry>[] slots; // 时间轮槽
private final AtomicInteger currentSlot; // 当前槽位索引
private final ExecutorService executor; // 任务执行器
private final EnhancedTimingWheel overflowWheel; // 上层时间轮
private final AtomicBoolean running = new AtomicBoolean(false);
@SuppressWarnings("unchecked")
public EnhancedTimingWheel(int tier, int slotSize, long timePerSlot,
ExecutorService executor) {
this.tier = tier;
this.slotSize = slotSize;
this.timePerSlot = timePerSlot;
this.wheelTimeout = slotSize * timePerSlot;
this.executor = executor;
this.slots = (List<TimerTaskEntry>[]) new List[slotSize];
this.currentSlot = new AtomicInteger(0);
for (int i = 0; i < slotSize; i++) {
slots[i] = new CopyOnWriteArrayList<>();
}
if (tier > 0) {
this.overflowWheel = new EnhancedTimingWheel(tier - 1, slotSize,
timePerSlot * slotSize, executor);
} else {
this.overflowWheel = null;
}
}
// 任务条目
public static class TimerTaskEntry {
private final TimerTask task;
private final long expiration;
private TimerTaskEntry next;
private volatile boolean cancelled = false;
public TimerTaskEntry(TimerTask task, long expiration) {
this.task = task;
this.expiration = expiration;
}
public boolean isExpired() {
return expiration <= System.currentTimeMillis();
}
public void cancel() {
cancelled = true;
}
public boolean isCancelled() {
return cancelled;
}
}
// 启动时间轮
public void start() {
if (running.compareAndSet(false, true)) {
new Thread(this::rotate).start();
}
}
// 添加任务
public void addTask(TimerTask task, long delay) {
long expiration = System.currentTimeMillis() + delay;
TimerTaskEntry entry = new TimerTaskEntry(task, expiration);
if (delay < wheelTimeout) {
int targetSlot = (int)((expiration / timePerSlot) % slotSize);
slots[targetSlot].add(entry);
} else {
if (overflowWheel != null) {
overflowWheel.addTask(task, delay);
} else {
// 延迟太长,直接安排执行
executor.submit(() -> {
try {
Thread.sleep(delay);
task.execute();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
}
// 取消任务
public boolean cancelTask(TimerTask task) {
// 简化的实现,实际需要遍历所有槽位
return false;
}
// 时间轮旋转
private void rotate() {
while (running.get()) {
try {
Thread.sleep(timePerSlot);
int slot = currentSlot.getAndIncrement() % slotSize;
List<TimerTaskEntry> tasks = slots[slot];
if (!tasks.isEmpty()) {
List<TimerTaskEntry> expiredTasks = new ArrayList<>();
List<TimerTaskEntry> activeTasks = new ArrayList<>();
long currentTime = System.currentTimeMillis();
for (TimerTaskEntry entry : tasks) {
if (entry.isExpired() && !entry.isCancelled()) {
expiredTasks.add(entry);
} else {
activeTasks.add(entry);
}
}
// 清空当前槽位
tasks.clear();
tasks.addAll(activeTasks);
// 执行过期任务
for (TimerTaskEntry entry : expiredTasks) {
executor.submit(() -> {
try {
entry.task.execute();
} catch (Exception e) {
// 处理异常
}
});
}
}
// 处理溢出任务
if (overflowWheel != null) {
overflowWheel.rotateExpiredTasks();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
// 旋转过期任务
private void rotateExpiredTasks() {
// 实现溢出任务的处理
}
// 停止时间轮
public void stop() {
running.set(false);
}
// 获取统计信息
public TimingWheelStats getStats() {
TimingWheelStats stats = new TimingWheelStats();
stats.tier = tier;
stats.slotSize = slotSize;
stats.currentSlot = currentSlot.get();
stats.totalTasks = Arrays.stream(slots).mapToInt(List::size).sum();
return stats;
}
public static class TimingWheelStats {
public int tier;
public int slotSize;
public int currentSlot;
public int totalTasks;
}
}
分布式时间轮实现
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class DistributedTimingWheel {
private final String nodeId;
private final int slotSize;
private final long timePerSlot;
private final ExecutorService executor;
private final Map<String, TimerTaskEntry> taskMap = new ConcurrentHashMap<>();
private final TimerWheel[] wheels;
private final AtomicBoolean running = new AtomicBoolean(false);
public DistributedTimingWheel(String nodeId, int slotSize, long timePerSlot,
ExecutorService executor) {
this.nodeId = nodeId;
this.slotSize = slotSize;
this.timePerSlot = timePerSlot;
this.executor = executor;
this.wheels = new TimerWheel[3]; // 3层时间轮
// 初始化时间轮
for (int i = 0; i < 3; i++) {
long slotTime = timePerSlot * (long) Math.pow(slotSize, i);
wheels[i] = new TimerWheel(i, slotSize, slotTime, executor);
}
}
// 添加分布式任务
public void addDistributedTask(String taskId, TimerTask task, long delay) {
String taskKey = nodeId + ":" + taskId;
TimerTaskEntry entry = new TimerTaskEntry(task, System.currentTimeMillis() + delay);
taskMap.put(taskKey, entry);
// 添加到最合适的时间轮
for (int i = wheels.length - 1; i >= 0; i--) {
if (delay <= wheels[i].getWheelTimeout()) {
wheels[i].addTask(entry);
break;
}
}
}
// 取消任务
public boolean cancelTask(String taskId) {
String taskKey = nodeId + ":" + taskId;
TimerTaskEntry entry = taskMap.remove(taskKey);
if (entry != null) {
entry.cancel();
return true;
}
return false;
}
// 启动时间轮
public void start() {
if (running.compareAndSet(false, true)) {
for (TimerWheel wheel : wheels) {
wheel.start();
}
}
}
// 停止时间轮
public void stop() {
if (running.compareAndSet(true, false)) {
for (TimerWheel wheel : wheels) {
wheel.stop();
}
}
}
// 内部时间轮类
private class TimerWheel {
private final int tier;
private final int slotSize;
private final long timePerSlot;
private final long wheelTimeout;
private final List<TimerTaskEntry>[] slots;
private final AtomicInteger currentSlot;
private final ExecutorService executor;
private final AtomicBoolean running = new AtomicBoolean(false);
@SuppressWarnings("unchecked")
public TimerWheel(int tier, int slotSize, long timePerSlot,
ExecutorService executor) {
this.tier = tier;
this.slotSize = slotSize;
this.timePerSlot = timePerSlot;
this.wheelTimeout = slotSize * timePerSlot;
this.executor = executor;
this.slots = (List<TimerTaskEntry>[]) new List[slotSize];
this.currentSlot = new AtomicInteger(0);
for (int i = 0; i < slotSize; i++) {
slots[i] = new CopyOnWriteArrayList<>();
}
}
public void addTask(TimerTaskEntry entry) {
long remainingDelay = entry.getExpiration() - System.currentTimeMillis();
if (remainingDelay <= timePerSlot) {
int targetSlot = (int)((entry.getExpiration() / timePerSlot) % slotSize);
slots[targetSlot].add(entry);
} else {
// 转发到更高层级的时间轮
if (tier < wheels.length - 1) {
wheels[tier + 1].addTask(entry);
}
}
}
public void start() {
if (running.compareAndSet(false, true)) {
new Thread(this::rotate).start();
}
}
public void stop() {
running.set(false);
}
private void rotate() {
while (running.get()) {
try {
Thread.sleep(timePerSlot);
int slot = currentSlot.getAndIncrement() % slotSize;
List<TimerTaskEntry> tasks = slots[slot];
if (!tasks.isEmpty()) {
List<TimerTaskEntry> expiredTasks = new ArrayList<>();
List<TimerTaskEntry> activeTasks = new ArrayList<>();
for (TimerTaskEntry entry : tasks) {
if (entry.isExpired()) {
expiredTasks.add(entry);
} else {
activeTasks.add(entry);
}
}
tasks.clear();
tasks.addAll(activeTasks);
for (TimerTaskEntry entry : expiredTasks) {
executor.submit(() -> {
try {
entry.getTask().execute();
// 从任务映射中移除
taskMap.remove(entry.getTaskId());
} catch (Exception e) {
// 处理异常
}
});
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public long getWheelTimeout() {
return wheelTimeout;
}
}
}
时间复杂度分析
操作时间复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 添加任务 | O(1) | 直接计算槽位并添加 |
| 取消任务 | O(1) | 标记任务为取消状态 |
| 执行任务 | O(1) | 指针到达槽位时执行 |
| 旋转时间轮 | O(1) | 移动指针,检查槽位 |
空间复杂度
- O(n) - 存储 n 个任务
- 时间轮槽位使用固定空间 O(slotSize)
性能特点
- 时间轮旋转:每次旋转 O(1) 时间复杂度
- 任务执行:平均每个任务执行 O(1) 时间
- 内存使用:固定大小的时间轮,内存可控
- 并发性能:使用 CopyOnWriteArrayList 保证线程安全
实际应用场景
1. 分布式任务调度
- 定时任务:周期性任务执行
- 延迟任务:延迟执行的任务
- 任务重试:失败任务的重试机制
// 分布式任务调度示例
public class TaskScheduler {
private final DistributedTimingWheel timingWheel;
private final ExecutorService executor;
public void scheduleTask(String taskId, Runnable task, long delay) {
timingWheel.addDistributedTask(taskId, () -> {
try {
task.run();
} catch (Exception e) {
// 任务执行失败,可以重试
scheduleRetry(taskId, task, delay);
}
}, delay);
}
private void scheduleRetry(String taskId, Runnable task, long delay) {
// 实现重试逻辑
}
}
2. 消息队列超时处理
- 消息超时:处理超时的消息
- 死信队列:移除超时未消费的消息
- 重试机制:消息重试调度
// 消息队列超时处理
public class MessageQueueTimeoutHandler {
private final EnhancedTimingWheel timingWheel;
public void handleMessageTimeout(String messageId, long timeout) {
timingWheel.addTask(() -> {
// 处理超时消息
handleTimeoutMessage(messageId);
}, timeout);
}
private void handleTimeoutMessage(String messageId) {
// 将消息移到死信队列
System.out.println("Message " + messageId + " timed out");
}
}
3. 缓存过期清理
- 缓存过期:清理过期的缓存数据
- 惰性删除:被动删除过期数据
- 定时删除:主动删除过期数据
// 缓存过期清理
public class CacheManager {
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
private final EnhancedTimingWheel timingWheel;
public void put(String key, Object value, long ttl) {
CacheEntry entry = new CacheEntry(value, System.currentTimeMillis() + ttl);
cache.put(key, entry);
// 添加到时间轮
timingWheel.addTask(() -> {
cache.remove(key);
}, ttl);
}
private static class CacheEntry {
private final Object value;
private final long expiration;
public CacheEntry(Object value, long expiration) {
this.value = value;
this.expiration = expiration;
}
}
}
4. 连接池管理
- 连接超时:检测和关闭超时连接
- 空闲连接:清理长时间空闲的连接
- 连接保活:定期检查连接状态
// 连接池管理
public class ConnectionPool {
private final Map<String, Connection> connections = new ConcurrentHashMap<>();
private final EnhancedTimingWheel timingWheel;
public void addConnection(String id, Connection connection, long timeout) {
connections.put(id, connection);
// 添加超时检测
timingWheel.addTask(() -> {
Connection conn = connections.get(id);
if (conn != null && conn.isIdle()) {
conn.close();
connections.remove(id);
}
}, timeout);
}
}
5. API 限流
- 限流窗口:控制 API 调用频率
- 时间窗口:基于时间窗口的限流
- 滑动窗口:实现滑动限流算法
// API 限流实现
public class RateLimiter {
private final EnhancedTimingWheel timingWheel;
private final Map<String, AtomicInteger> counters = new ConcurrentHashMap<>();
public boolean allowRequest(String userId, long windowSize, int limit) {
String counterKey = userId + ":" + System.currentTimeMillis() / windowSize;
AtomicInteger counter = counters.computeIfAbsent(counterKey, k -> new AtomicInteger(0));
timingWheel.addTask(() -> counters.remove(counterKey), windowSize);
return counter.incrementAndGet() <= limit;
}
}
与其他调度方式的对比
| 调度方式 | 时间复杂度 | 内存使用 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|---|
| 时间轮 | O(1) | O(n) | 大量延迟任务 | 高效、内存可控 | 实现复杂 |
| 优先队列 | O(log n) | O(n) | 任务数量较少 | 实现简单 | 性能较差 |
| 线程池 | O(1) | O(n) | 短期任务 | 使用简单 | 资源消耗大 |
| 定时器 | O(1) | O(1) | 少量任务 | API 简单 | 不适合大量任务 |
| NIO | O(1) | O(n) | 高并发 | 性能极高 | 实现复杂 |
时间轮的优势
- 高效性:O(1) 时间复杂度的任务调度
- 内存可控:固定大小的时间轮结构
- 实时性:精确的任务执行时间控制
- 扩展性:支持多层级处理复杂任务
- 并发友好:线程安全的实现
时间轮的劣势
- 实现复杂:相比其他方式实现较复杂
- 精度限制:受时间片大小限制
- 内存开销:需要维护多个时间轮
- 任务取消:取消任务需要额外处理
常见面试问题
Q1: 时间轮和优先队列有什么区别?为什么选择时间轮?
答: 主要区别:
- 时间复杂度:时间轮 O(1),优先队列 O(log n)
- 内存使用:时间轮固定大小,优先队列动态增长
- 任务处理:时间轮轮询,优先队列堆排序
- 实现复杂度:时间轮较复杂,优先队列简单
选择时间轮的原因:
- 任务数量大时性能更好
- 内存使用更可控
- 适合处理大量延迟任务
- 支持高并发场景
Q2: 如何处理时间轮的精度问题?
答: 精度优化策略:
- 调整时间片:根据需求选择合适的时间片大小
- 多层级时间轮:小时间片处理短期任务,大时间片处理长期任务
- 实时校准:定期校准系统时间
- 任务优先级:高优先级任务单独处理
- 补偿机制:记录实际执行时间进行补偿
Q3: 如何处理时间轮中的任务取消?
答: 取消机制实现:
- 标记机制:在任务条目中设置取消标志
- 垃圾回收:定期清理已取消的任务
- 主动查询:提供取消任务的接口
- 批量清理:在时间轮旋转时批量清理
- 超时自动清理:长时间未执行的任务自动清理
// 任务取消实现
public boolean cancelTask(String taskId) {
String taskKey = nodeId + ":" + taskId;
TimerTaskEntry entry = taskMap.get(taskKey);
if (entry != null) {
entry.cancel();
// 可以选择立即从任务映射中移除
taskMap.remove(taskKey);
return true;
}
return false;
}
Q4: 时间轮在分布式环境中如何保证一致性?
答: 分布式一致性方案:
- 任务迁移:任务在节点间迁移时保持一致性
- 时间同步:所有节点使用同步时钟
- 任务分发:根据任务类型和节点负载分发任务
- 故障恢复:节点故障时任务重新分发
- 共识机制:使用一致性协议保证任务不丢失
Q5: 如何优化时间轮的性能?
答: 性能优化策略:
- 时间轮层级:合理设置时间轮层级数量
- 槽位数量:根据任务分布调整槽位数量
- 任务批处理:批量处理相似任务
- 线程优化:使用多线程处理任务执行
- 内存优化:使用更高效的数据结构
- 缓存优化:优化热点数据的访问
Q6: 时间轮如何处理大量任务?
答: 大量任务处理方案:
- 任务分片:将任务分散到不同时间轮
- 分层处理:短期任务和长期任务分别处理
- 任务合并:相似任务合并执行
- 负载均衡:多个时间轮实例并行工作
- 资源管理:动态调整时间轮资源分配
Q7: 时间轮和事件驱动模式有什么关系?
答: 关系说明:
- 结合使用:时间轮可以作为事件驱动系统的一部分
- 触发机制:时间轮可以触发事件的执行
- 定时事件:时间轮专门处理定时事件
- 互补关系:事件驱动处理即时事件,时间轮处理延迟事件
- 性能协同:两者结合可以提高系统整体性能
Q8: 如何实现任务的优先级处理?
答: 优先级实现方案:
- 多个时间轮:不同优先级使用不同时间轮
- 优先队列:在时间轮中使用优先队列
- 任务标记:高优先级任务特殊标记
- 抢占执行:高优先级任务可以抢占低优先级任务
- 动态调整:根据任务优先级动态调整执行顺序
// 带优先级的时间轮
public class PriorityTimingWheel {
private final Map<Integer, EnhancedTimingWheel> priorityWheels = new ConcurrentHashMap<>();
public void addTask(TimerTask task, long delay, int priority) {
EnhancedTimingWheel wheel = priorityWheels.computeIfAbsent(
priority,
p -> new EnhancedTimingWheel(3, 8, 100, executor)
);
wheel.addTask(task, delay);
}
}
Q9: 时间轮的内存如何管理?
答: 内存管理策略:
- 固定大小:时间轮槽位数量固定
- 对象复用:复用任务对象减少 GC
- 分代管理:不同生命周期对象分别管理
- 内存监控:监控内存使用情况
- 自动扩容:在必要时自动扩展时间轮
Q10: 时间轮的适用场景和限制是什么?
答: 适用场景:
- 大量延迟任务:需要处理大量延迟任务
- 高并发环境:需要高并发处理能力
- 内存受限环境:内存使用需要可控
- 实时性要求:需要精确的时间控制
- 分布式系统:需要分布式任务调度
限制:
- 精度限制:受时间片大小限制
- 实现复杂:相比其他实现较复杂
- 单机限制:单机处理能力有限
- 任务取消:取消任务处理复杂
- 时间同步:分布式环境需要时间同步
总结
时间轮算法是一种高效的时间调度算法,特别适合处理大量延迟任务和周期性任务。通过合理的设计和优化,可以在各种场景下实现高性能的任务调度。理解时间轮的原理和实现方式,对于设计和实现高性能的分布式系统具有重要意义。