
本文探讨在共享主机环境下,通过 localstorage 或 indexeddb 缓存题目与用户答案再批量提交,以显著降低数据库压力和服务器请求频次,特别适用于 2000 并发用户的轻量级测验系统。
本文探讨在共享主机环境下,通过 localstorage 或 indexeddb 缓存题目与用户答案再批量提交,以显著降低数据库压力和服务器请求频次,特别适用于 2000 并发用户的轻量级测验系统。
在构建面向大众的在线测验应用时,尤其是部署于资源受限的共享主机环境,频繁的单题提交(如每答一题即发送一次 AJAX 请求)会迅速成为性能瓶颈:2000 并发用户若每人完成 20 题,将产生高达 4 万次独立 HTTP 请求,极易触发 PHP 超时、MySQL 连接数耗尽或 Apache 进程排队等问题。
推荐方案:客户端暂存 + 批量提交
✅ 优先选用 IndexedDB(而非 localStorage)
虽然 localStorage 简单易用,但其存在明显局限:
- 仅支持字符串键值对,需手动
JSON.stringify()/JSON.parse()序列化复杂数据(如含时间戳、题型、选项状态的对象); - 容量通常仅 5–10 MB,且阻塞主线程(同步 API),大量读写易导致界面卡顿;
- 无事务机制,多步操作(如“保存答案+更新进度+标记完成”)缺乏原子性保障。
IndexedDB 则专为结构化客户端存储设计,具备以下优势:
- 支持对象存储、索引查询、事务控制;
- 异步非阻塞,适合处理百题级测验数据;
- 容量更大(通常为硬盘空间的 50%,远超 localStorage);
- 浏览器兼容性良好(Chrome 23+/Firefox 16+/Edge 12+/Safari 10.1+)。
以下是一个精简实用的 IndexedDB 封装示例,用于持久化题目与用户作答:
// 初始化测验数据库
function initQuizDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('QuizDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('answers')) {
const store = db.createObjectStore('answers', { keyPath: 'questionId' });
store.createIndex('timestamp', 'timestamp', { unique: false });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 保存单题答案(自动合并更新)
async function saveAnswer(questionId, answerData) {
const db = await initQuizDB();
const tx = db.transaction('answers', 'readwrite');
const store = tx.objectStore('answers');
await store.put({
questionId,
answer: answerData,
timestamp: Date.now(),
completed: false
});
}
// 提交全部答案(调用前确保网络就绪)
async function submitAllAnswers() {
const db = await initQuizDB();
const tx = db.transaction('answers', 'readonly');
const store = tx.objectStore('answers');
const allAnswers = await new Promise(resolve => {
const req = store.getAll();
req.onsuccess = () => resolve(req.result);
});
// 批量提交至后端(推荐 POST /api/quiz/submit)
const response = await fetch('/api/quiz/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ answers: allAnswers })
});
if (response.ok) {
// 清空本地存储
const clearTx = db.transaction('answers', 'readwrite');
await clearTx.objectStore('answers').clear();
}
return response;
}
补充优化建议:
? 服务端缓存层(如 Redis):即使采用客户端暂存,仍建议在共享主机允许范围内部署轻量 Redis 实例(部分主机支持 Memcached 或 Redis 插件),用于缓存热门题库 JSON、用户会话状态及提交结果,避免重复查询 MySQL。
? 防丢失机制:在 beforeunload 事件中触发 submitAllAnswers() 的兜底保存,并提示用户“正在保存进度…”;同时定期(如每 30 秒)自动快照至 IndexedDB,防止意外刷新丢失。
? 错误降级策略:若 IndexedDB 不可用(如 Safari 无痕模式限制),自动回退至 localStorage + 更保守的序列化逻辑,保障基础功能不中断。
综上,在 2000 并发场景下,以 IndexedDB 为主、localStorage 为备、服务端 Redis 为辅的三层缓存架构,可将服务器请求数从数万级降至千级以内,显著提升响应稳定性与可扩展性,是共享主机环境中兼顾性能、成本与开发效率的务实之选。











