MST

星途 面试题库

面试题:Flutter SQLite高并发查询优化及缓存策略

在一个Flutter构建的多人在线游戏中,大量玩家同时在线查询游戏数据(如排行榜、个人成就等),SQLite数据库面临高并发查询压力。请设计一套完整的查询优化方案,包括但不限于使用缓存策略、数据库连接池等技术,同时要考虑数据一致性和缓存更新机制。阐述详细的设计思路和关键代码结构。
26.3万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 缓存策略
    • 本地缓存:在Flutter客户端使用内存缓存存储经常访问的数据,如最近查看的排行榜和个人成就数据。例如使用shared_preferences存储简单的缓存数据,对于复杂数据结构(如排行榜列表),可以在内存中维护缓存对象。
    • 服务器端缓存:在服务器端(假设使用Node.js或其他后端技术)使用Redis作为缓存层。Redis具有高性能读写能力,适合处理高并发场景。当客户端请求数据时,首先检查Redis缓存中是否存在相应数据,如果存在则直接返回,减少对SQLite数据库的查询压力。
  2. 数据库连接池
    • 使用连接池技术管理与SQLite数据库的连接。在Flutter中,可以使用sqflite插件,它支持连接池功能。连接池可以避免每次查询都创建新的数据库连接,减少连接开销,提高查询效率。
  3. 数据一致性和缓存更新机制
    • 缓存失效策略:设置缓存过期时间,例如排行榜数据每5分钟更新一次缓存,个人成就数据在用户登录时更新缓存。当缓存过期后,再次请求数据时,从SQLite数据库查询最新数据并更新缓存。
    • 主动更新缓存:当游戏数据发生变化(如玩家获得新成就),不仅要更新SQLite数据库,还要主动更新Redis缓存和客户端本地缓存,确保数据一致性。

关键代码结构

  1. Flutter客户端缓存代码示例(使用shared_preferences
import 'package:shared_preferences/shared_preferences.dart';

class LocalCache {
  static Future<void> saveData(String key, dynamic value) async {
    final prefs = await SharedPreferences.getInstance();
    if (value is String) {
      await prefs.setString(key, value);
    } else if (value is int) {
      await prefs.setInt(key, value);
    } else if (value is bool) {
      await prefs.setBool(key, value);
    }
  }

  static Future<dynamic> getData(String key) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.get(key);
  }
}
  1. 服务器端Redis缓存代码示例(使用Node.js和ioredis库)
const Redis = require('ioredis');
const redis = new Redis();

async function getFromRedis(key) {
  return await redis.get(key);
}

async function setToRedis(key, value, expiration = 300) {
  await redis.setex(key, expiration, value);
}
  1. Flutter数据库连接池代码示例(使用sqflite
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._init();
  static Database? _database;

  DatabaseHelper._init();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB('game.db');
    return _database!;
  }

  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);

    return await openDatabase(path, version = 1, onCreate: _createDB);
  }

  Future<void> _createDB(Database db, int version) async {
    // 创建表等数据库初始化操作
    const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT';
    const textType = 'TEXT NOT NULL';
    const intType = 'INTEGER NOT NULL';

    await db.execute('''
      CREATE TABLE achievements (
        id $idType,
        player_id $intType,
        achievement_name $textType
      )
    ''');
  }

  Future<List<Map<String, dynamic>>> query(String table) async {
    final db = await instance.database;
    return await db.query(table);
  }
}
  1. 缓存更新和数据一致性维护代码示例
    • Flutter客户端:当接收到服务器推送的游戏数据更新消息时,更新本地缓存和请求服务器更新Redis缓存。
// 假设收到成就更新消息
void handleAchievementUpdate(Map<String, dynamic> achievementData) {
  // 更新本地缓存
  LocalCache.saveData('achievement_${achievementData['player_id']}', achievementData);
  // 发送请求到服务器更新Redis缓存
  // 这里假设使用http库发送请求
  http.post(Uri.parse('http://your-server-url/update_redis_achievement'),
      body: achievementData);
}
  • 服务器端:当接收到客户端更新请求时,更新SQLite数据库和Redis缓存。
// 假设使用Express框架
const express = require('express');
const app = express();
app.use(express.json());

app.post('/update_redis_achievement', async (req, res) => {
  const achievementData = req.body;
  // 更新SQLite数据库
  // 这里假设使用sqlite3库操作SQLite数据库
  const sqlite3 = require('sqlite3').verbose();
  const db = new sqlite3.Database('game.db');
  const query = 'UPDATE achievements SET achievement_name =? WHERE player_id =?';
  db.run(query, [achievementData.achievement_name, achievementData.player_id], function (err) {
    if (err) {
      console.error(err);
      res.status(500).send('Database update failed');
    } else {
      // 更新Redis缓存
      setToRedis(`achievement_${achievementData.player_id}`, JSON.stringify(achievementData));
      res.status(200).send('Achievement updated successfully');
    }
  });
  db.close();
});