Meta In-Memory KV 面试题 2026:时间戳存储与历史回溯高分指南
Meta In-Memory KV 面试题 2026 是高频真题。并且,它在 2026 年更常出现。来源上,这是我们学员贡献的最新面经。
此外,这题覆盖实现与沟通两线。你要写对代码。你也要讲清权衡。
2026 面试流程深度复盘:Meta In-Memory KV 面试题 2026
首先,面试官常从 set/get 开场。然后追问 compareAndUpdate。但是重点不在语法。重点在语义一致。
接着,会追加 scan 与 scanByPrefix。因此你要主动选 TreeMap。你还要解释前缀上界。
然后,题目会加入 TTL。与此同时,面试官会检查过期一致性。所有读写入口都要先判过期。
因此,Meta In-Memory KV 面试题 2026 的节奏很固定。先基础。再查询。再 TTL。最后历史回溯。
此外,Meta In-Memory KV 面试题 2026 在收尾时,常问空间成本。你要明确说出。历史全量记录更占内存。
核心题目解析
具体来说,核心结构可分两层。当前态用 Map<key, TreeMap<field, Record>>。历史态用 Map<key, Map<field, TreeMap<ts, Record>>>。
此外,Record 至少含四项。它们是 value、expireAt、hasTTL、tombstone。这样才能区分删除与未存在。
与此同时,前缀查询要走有序范围。你可构造 upperPrefix = prefix + Character.MAX_VALUE。再用 subMap(prefix, true, upperPrefix, false)。
flowchart TD
A[API 请求] --> B[按 key 定位主 Map]
B --> C[按 field 定位 TreeMap]
C --> D{操作分支}
D -->|get / scan| E[先做 TTL 检查]
E --> F[返回可见值]
D -->|set / CAS| G[写入当前态]
G --> H[写入历史 TreeMap<ts,Record>]
H --> I[getValueAt 用 floorEntry]
D -->|scanByPrefix| J[subMap 前缀范围]
此外,下面是可面试直用的 Java 参考实现。
import java.util.*;
public class TimeKVStore {
// 单条记录: 支持值、TTL 与删除标记
static class Record {
final String value;
final long expireAt;
final boolean hasTTL;
final boolean tombstone;
Record(String value, long expireAt, boolean hasTTL, boolean tombstone) {
this.value = value;
this.expireAt = expireAt;
this.hasTTL = hasTTL;
this.tombstone = tombstone;
}
static Record alive(String value) {
return new Record(value, 0L, false, false);
}
static Record aliveWithTTL(String value, long expireAt) {
return new Record(value, expireAt, true, false);
}
static Record tombstone() {
return new Record(null, 0L, false, true);
}
boolean expiredAt(long ts) {
return hasTTL && ts >= expireAt;
}
}
// 当前可见状态: key -> (field 有序映射)
private final Map<String, TreeMap<String, Record>> latest = new HashMap<>();
// 历史版本: key -> field -> (timestamp -> record)
private final Map<String, Map<String, TreeMap<Long, Record>>> history = new HashMap<>();
public void set(long ts, String key, String field, String value) {
writeCurrent(key, field, Record.alive(value));
appendHistory(ts, key, field, Record.alive(value));
}
public void setWithTTL(long ts, String key, String field, String value, long ttl) {
long expireAt = ts + ttl;
Record r = Record.aliveWithTTL(value, expireAt);
writeCurrent(key, field, r);
appendHistory(ts, key, field, r);
}
public String get(long ts, String key, String field) {
Record r = visibleNow(ts, key, field, true);
return r == null ? null : r.value;
}
public boolean compareAndUpdate(long ts, String key, String field,
String expected, String newValue) {
String cur = get(ts, key, field);
if (!Objects.equals(cur, expected)) return false;
set(ts, key, field, newValue);
return true;
}
public boolean compareAndUpdateWithTTL(long ts, String key, String field,
String expected, String newValue, long ttl) {
String cur = get(ts, key, field);
if (!Objects.equals(cur, expected)) return false;
setWithTTL(ts, key, field, newValue, ttl);
return true;
}
public boolean compareAndDelete(long ts, String key, String field, String expected) {
String cur = get(ts, key, field);
if (!Objects.equals(cur, expected)) return false;
TreeMap<String, Record> fields = latest.get(key);
if (fields != null) fields.remove(field);
appendHistory(ts, key, field, Record.tombstone());
return true;
}
public List<String> scan(long ts, String key) {
List<String> ans = new ArrayList<>();
TreeMap<String, Record> fields = latest.get(key);
if (fields == null) return ans;
Iterator<Map.Entry<String, Record>> it = fields.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Record> e = it.next();
Record r = e.getValue();
if (r.tombstone) continue;
if (r.expiredAt(ts)) {
it.remove(); // 过期清理, 保证后续路径一致
continue;
}
ans.add(e.getKey() + "=" + r.value);
}
return ans;
}
public List<String> scanByPrefix(long ts, String key, String prefix) {
List<String> ans = new ArrayList<>();
TreeMap<String, Record> fields = latest.get(key);
if (fields == null) return ans;
String upperPrefix = prefix + Character.MAX_VALUE;
NavigableMap<String, Record> range =
fields.subMap(prefix, true, upperPrefix, false);
Iterator<Map.Entry<String, Record>> it = range.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Record> e = it.next();
Record r = e.getValue();
if (r.tombstone) continue;
if (r.expiredAt(ts)) {
it.remove();
continue;
}
ans.add(e.getKey() + "=" + r.value);
}
return ans;
}
// 在 atTimestamp 时刻回溯值
public String getValueAt(long ts, String key, String field, long atTimestamp) {
if (atTimestamp > ts) return null; // 不查未来时间
Map<String, TreeMap<Long, Record>> byField = history.get(key);
if (byField == null) return null;
TreeMap<Long, Record> timeline = byField.get(field);
if (timeline == null) return null;
Map.Entry<Long, Record> floor = timeline.floorEntry(atTimestamp);
if (floor == null) return null;
Record r = floor.getValue();
if (r.tombstone) return null;
if (r.expiredAt(atTimestamp)) return null;
return r.value;
}
private void writeCurrent(String key, String field, Record r) {
latest.computeIfAbsent(key, k -> new TreeMap<>()).put(field, r);
}
private void appendHistory(long ts, String key, String field, Record r) {
history.computeIfAbsent(key, k -> new HashMap<>())
.computeIfAbsent(field, f -> new TreeMap<>())
.put(ts, r);
}
private Record visibleNow(long ts, String key, String field, boolean purgeExpired) {
TreeMap<String, Record> fields = latest.get(key);
if (fields == null) return null;
Record r = fields.get(field);
if (r == null || r.tombstone) return null;
if (r.expiredAt(ts)) {
if (purgeExpired) fields.remove(field);
return null;
}
return r;
}
}
但是,写完代码不够。你还要给复杂度。set/get/CAS 平均是 O(1) 到 O(logF)。scan 与 scanByPrefix 取决于命中字段数。
换句话说,getValueAt 关键在 floorEntry。它是 O(logV)。F 是字段数。V 是版本数。
专家备考策略与高频考点:Meta In-Memory KV 面试题 2026
换句话说,Meta In-Memory KV 面试题 2026 不只考代码。它也考你是否像工程师。你要主动讲边界与取舍。
此外,下面是你必须背熟的核心考点。
- 因此,先讲两层映射建模。
- 此外,解释为何字段层用
TreeMap。 - 但是,强调 CAS 先比较再写入。
- 与此同时,说明 TTL 放在
Record内。 - 因此,所有读写都先判过期。
- 具体来说,历史层按时间有序存。
- 此外,删除要写 tombstone。
- 然后,回溯用
floorEntry(atTimestamp)。 - 但是,结果还要再判 TTL。
- 总而言之,空间换时间要说清。
此外,BQ 建议用 STAR 快速作答。
- 因此,冲突更新题。S: 并发更新频繁。T: 保证正确写入。A: 设计 CAS 语义。R: 错写率下降。
- 此外,过期数据题。S: 脏读影响业务。T: 保持读写一致。A: 全路径先判过期。R: 查询稳定。
- 但是,历史追溯题。S: 审计要回看旧值。T: 支持任意时间点。A: 版本树加 tombstone。R: 可追踪且可解释。
- 与此同时,成本权衡题。S: 内存吃紧。T: 控制空间增长。A: 设保留窗与压缩。R: 性能和成本平衡。
总结与行动号召(CTA)
总而言之,Meta In-Memory KV 面试题 2026 的高分点很清楚。你要先给正确实现。然后给一致性证明。最后讲工程权衡。
因此,如果你要在 2026 面试中稳过这题,现在就做一次限时模拟。
此外,预约辅导请点这里:联系我们的专家进行一对一面试辅导
与此同时,算法扩展阅读可看:权威算法参考