数据缓存策略
数据缓存策略的基本概念
数据缓存策略的核心在于减少重复计算和网络请求,提升数据访问效率。ECharts作为可视化库,频繁处理动态数据更新和交互操作,合理的缓存机制能显著优化性能。常见场景包括高频触发的图表重绘、大数据量渲染、异步数据加载等。
内存缓存实现方式
ECharts可通过内存对象存储已处理数据,避免重复解析。例如使用WeakMap存储系列数据与对应的渲染结果:
const renderCache = new WeakMap();
function getCachedRender(series) {
if (!renderCache.has(series)) {
const renderResult = heavyCompute(series.data);
renderCache.set(series, renderResult);
}
return renderCache.get(series);
}
对于时间序列数据可采用LRU缓存策略:
class TimeSeriesCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
get(key) {
if (!this.cache.has(key)) return null;
const value = this.cache.get(key);
// 刷新访问时间
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.size >= this.maxSize) {
// 删除最久未使用的
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
}
}
本地存储缓存方案
对于需要持久化的配置数据,可结合localStorage实现:
const CACHE_KEY = 'echarts_config_cache';
function saveConfigToCache(config) {
try {
const serialized = JSON.stringify({
timestamp: Date.now(),
data: config
});
localStorage.setItem(CACHE_KEY, serialized);
} catch (e) {
console.warn('LocalStorage quota exceeded');
}
}
function loadConfigFromCache() {
const cached = localStorage.getItem(CACHE_KEY);
if (!cached) return null;
try {
const parsed = JSON.parse(cached);
// 验证缓存有效期(示例设置24小时过期)
if (Date.now() - parsed.timestamp > 86400000) {
localStorage.removeItem(CACHE_KEY);
return null;
}
return parsed.data;
} catch {
return null;
}
}
数据分片缓存技术
处理超大规模数据集时,可采用分片加载与缓存:
class DataChunkManager {
constructor(chunkSize = 10000) {
this.chunkSize = chunkSize;
this.chunkCache = new Map();
}
async loadChunk(dataset, chunkIndex) {
const cacheKey = `${dataset.id}_${chunkIndex}`;
if (this.chunkCache.has(cacheKey)) {
return this.chunkCache.get(cacheKey);
}
const start = chunkIndex * this.chunkSize;
const end = start + this.chunkSize;
const chunkData = await fetchChunk(dataset.url, start, end);
this.chunkCache.set(cacheKey, chunkData);
return chunkData;
}
}
缓存失效机制设计
有效的失效策略是缓存系统的关键,ECharts中常用方案:
- 基于数据版本号的验证:
function checkDataVersion(current, cached) {
return current.version === cached?.metadata?.version;
}
- 可视化参数变化检测:
function paramsChanged(currentOpts, cachedOpts) {
const keys = ['xAxis', 'yAxis', 'series'];
return keys.some(key =>
!shallowEqual(currentOpts[key], cachedOpts[key])
);
}
- 混合失效策略实现示例:
class SmartCache {
constructor() {
this.dataCache = new Map();
this.renderCache = new Map();
}
shouldInvalidate(dataId, renderOpts) {
const cachedData = this.dataCache.get(dataId);
const cachedRender = this.renderCache.get(dataId);
return !cachedData ||
!cachedRender ||
dataChanged(cachedData.checksum, currentData) ||
renderOptsChanged(cachedRender.params, renderOpts);
}
}
性能优化实践案例
某实时监控系统采用三级缓存架构:
- 内存热数据缓存(最近5分钟数据)
const liveDataCache = {
_data: [],
maxAge: 300000, // 5分钟
push(newPoint) {
this._data.push({
timestamp: Date.now(),
value: newPoint
});
this.cleanup();
},
cleanup() {
const cutoff = Date.now() - this.maxAge;
this._data = this._data.filter(item => item.timestamp > cutoff);
},
get() {
this.cleanup();
return this._data.map(item => item.value);
}
};
- IndexedDB历史数据缓存
const dbPromise = new Promise(resolve => {
const request = indexedDB.open('EChartsHistoryDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('history')) {
db.createObjectStore('history', { keyPath: 'timeRange' });
}
};
request.onsuccess = (e) => resolve(e.target.result);
});
async function queryHistory(start, end) {
const db = await dbPromise;
const tx = db.transaction('history', 'readonly');
const store = tx.objectStore('history');
return new Promise(resolve => {
const request = store.get(`${start}-${end}`);
request.onsuccess = () => resolve(request.result?.data || null);
});
}
- 服务端缓存协商
async function fetchDataWithCache(url) {
const cached = sessionStorage.getItem(`api:${url}`);
const headers = {};
if (cached) {
const { etag } = JSON.parse(cached);
headers['If-None-Match'] = etag;
}
const response = await fetch(url, { headers });
if (response.status === 304) {
return JSON.parse(cached).data;
}
const newData = await response.json();
sessionStorage.setItem(`api:${url}`, JSON.stringify({
etag: response.headers.get('ETag'),
data: newData
}));
return newData;
}
特殊场景处理方案
树形数据缓存
class TreeDataCache {
constructor() {
this.nodeMap = new Map();
}
cacheNode(path, node) {
this.nodeMap.set(path.join('/'), {
data: node,
lastUsed: Date.now()
});
}
getNode(path) {
const key = path.join('/');
if (this.nodeMap.has(key)) {
const entry = this.nodeMap.get(key);
entry.lastUsed = Date.now();
return entry.data;
}
return null;
}
}
地理坐标数据优化
const geoCoordCache = new Proxy({}, {
get(target, property) {
if (!(property in target)) {
target[property] = echarts.getMap(property);
}
return target[property];
}
});
// 使用示例
function renderMap(chart, mapName) {
const geoJson = geoCoordCache[mapName];
chart.setOption({
geo: {
map: mapName,
type: 'map',
...
}
});
}
缓存监控与调试
开发阶段可注入监控逻辑:
class CacheTracker {
constructor() {
this.hits = 0;
this.misses = 0;
}
track(condition) {
condition ? this.hits++ : this.misses++;
if ((this.hits + this.misses) % 100 === 0) {
console.log(`Cache hit rate: ${this.hits/(this.hits+this.misses)*100}%`);
}
}
}
const renderCacheTracker = new CacheTracker();
function cachedRender(data) {
const cached = getFromCache(data.id);
renderCacheTracker.track(!!cached);
return cached || computeRender(data);
}
生产环境可通过performance API测量:
function measureCachePerformance() {
const mark = name => performance.mark(`cache_${name}`);
const measure = (name, start, end) => {
performance.measure(name, start, end);
return performance.getEntriesByName(name).pop();
};
mark('start_uncached');
renderWithoutCache();
mark('end_uncached');
mark('start_cached');
renderWithCache();
mark('end_cached');
const uncached = measure('uncached', 'start_uncached', 'end_uncached');
const cached = measure('cached', 'start_cached', 'end_cached');
console.log(`Performance improvement: ${(uncached.duration - cached.duration).toFixed(2)}ms`);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn