Skip to content

源码:labs/java/backend-data/database-cache-simulation/src/DatabaseCacheSimulation.java ​

  • 原始路径:labs/java/backend-data/database-cache-simulation/src/DatabaseCacheSimulation.java
  • 类型:java
java
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 移动端数据库与缓存仿真:SQLite WAL vs 传统锁 + 多级缓存。
 *
 * 对应 docs 06-backend-data/02-database-cache-overview.md:
 * 1. 传统 SQLite:写入锁整个库 → 写时读被阻塞(读不阻塞写?不,传统是互斥)
 * 2. WAL 模式:写进独立 -wal 日志,原库只读 → 读不阻塞写、写不阻塞读
 * 3. 多级缓存:内存 → 磁盘(SQLite) → 远端 API 的分层读取与命中统计
 *
 * 纯 Java CLI(无外部依赖)。
 * 运行:javac -d out src/DatabaseCacheSimulation.java && java -cp out DatabaseCacheSimulation
 */
public class DatabaseCacheSimulation {

    // 模拟"数据库文件":WAL 模式下写入先进 wal,读直接读原库
    static final ConcurrentHashMap<String, String> DB_FILE = new ConcurrentHashMap<>();
    static final ConcurrentHashMap<String, String> WAL = new ConcurrentHashMap<>();
    static boolean walEnabled = true;

    public static void main(String[] args) {
        System.out.println("========== 1. 传统锁模式 vs WAL 模式:读写并发 ==========");
        // 传统模式:写锁整库,读必须等写完成(串行)
        System.out.println("  [传统模式] 写锁整库 → 读等待写完成 → 读写互斥,并发低");
        // WAL 模式:写进 -wal,读直接读原库
        System.out.println("  [WAL 模式] 写追加进 -wal 日志,读读原库 → 读写并行,并发高");
        walEnabled = true;
        System.out.println("  本 demo 用 WAL 语义:write() 先写 WAL(立即返回),read() 读 DB_FILE 不受影响");

        System.out.println();
        System.out.println("========== 2. WAL 写入流程 ==========");
        write("user:1", "Alice_v2");
        System.out.println("  write: 追加到 WAL {user:1=Alice_v2},原库尚未合并 → 写入方无需等待读方");
        System.out.println("  并发读 user:1 -> " + read("user:1") + "(读的是原库旧值或合并后新值,读不阻塞)");

        System.out.println();
        System.out.println("========== 3. 多级缓存分层读取(内存 → 磁盘 → 远端)==========");
        // 分层:内存缓存 > SQLite(磁盘) > 远端 API
        ConcurrentHashMap<String, String> memoryCache = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, String> diskDb = new ConcurrentHashMap<>();
        AtomicInteger remoteHits = new AtomicInteger();

        String key = "feed:10086";
        for (int i = 0; i < 5; i++) {
            String v = layeredGet(key, memoryCache, diskDb, remoteHits);
        }
        System.out.println("  5 次读 feed:10086 → 远端(DB/API) 回源次数 = " + remoteHits.get()
                + "(首次回源后内存/磁盘命中,后续 0 回源)");

        System.out.println();
        System.out.println("========== 4. 结论 ==========");
        System.out.println("  · SQLite 开 WAL:读不阻塞写、写不阻塞读,大批量写入配合事务");
        System.out.println("  · 移动端分层缓存:内存(最快) → 磁盘(SQLite) → 远端(兜底),命中率优先内存");
    }

    /** WAL 语义写入:先进 WAL(模拟顺序追加日志) */
    static void write(String key, String value) {
        WAL.put(key, value);   // 顺序追加,无锁
        DB_FILE.put(key, value); // 简化:立即合并(真实是 checkpoint 时机合并)
    }

    /** WAL 语义读取:直接读原库(不阻塞) */
    static String read(String key) {
        return DB_FILE.get(key);
    }

    /** 分层读取:内存 → 磁盘 → 远端 */
    static String layeredGet(String key,
                             ConcurrentHashMap<String, String> memory,
                             ConcurrentHashMap<String, String> disk,
                             AtomicInteger remoteHits) {
        String v = memory.get(key);
        if (v != null) return v;
        v = disk.get(key);
        if (v != null) {
            memory.put(key, v); // 磁盘命中 → 提升到内存
            return v;
        }
        // 远端回源(真实场景是网络请求)
        remoteHits.incrementAndGet();
        v = "remote_data_for_" + key;
        disk.put(key, v);
        memory.put(key, v);
        return v;
    }
}

站点构建时间:2026/8/24 23:43:17