场景问题
最近在做大模型的加载,加载文件的时候会解析出这个文件有多少个mesh
, line
, parameter
, 然后都会存在一个变量中去维护这个关系:
const detailedList = {
mesh: Array,
line: Array,
parameter: Array
}
这样会导致这个内存吃紧,毕竟直接存了好几G的数据信息。
那还有一种说法是, 文件加载过来直接就渲染不就行了吗?
但是这样会导致DC
过多, CPU
吃不消。
IndexDB
使用indexDB
来充当一个类似内存
的功能, 我把数据选择性的存在indexDB中,需要的时候再拿,这样会大大缓解内存压力。indexDB类似NOSQL,并不是MYSQL这种二维表结构, 取数据的速度是飞快的。
构造一个Store
和大部分前端项目一样,我也构造了一个store.
enum StoreType {
MEMORY,
INDEXDB
}
class Store {
/**
* 存放在内存中的数据
*/
private memory: Map<string, any>
private readonly dbVersion: number;
/**
* indexDB
*/
private db: IDBDatabase;
private isReady: boolean;
/**
* 储存的value多大才使用indexDB储存,反之使用map
*/
private readonly limitForDB: number;
/**
* 达到储存上限后一次性删除多少条数据
*/
private readonly deleteCount: number;
/**
* 储存上限
*/
private readonly upperStorageLimit: number;
constructor(dbVersion = 1, limitForDB?: number) {
this.isReady = false
this.dbVersion = dbVersion
this.initIndexDB()
this.memory = new Map()
this.limitForDB = limitForDB || 30
this.upperStorageLimit = 10000
this.deleteCount = 20
}
set(key: string, value: any, type?: StoreType) {
const intoMemory = () => {
this.memory.set(key, value)
}
const intoIndexDB = () => {
const objectStore = this.getObjectStore();
objectStore.put({id: key, value}, key);
this.calculationTimesAndDelete();
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? intoIndexDB() : intoMemory()
} else {
if (Store.sizeOf(value) > this.limitForDB) {
// indexDB加之前删除map中存在的key
this.clear(key, StoreType.MEMORY)
intoIndexDB()
} else {
// 加入map之前删除indexDB中存在的key
this.clear(key, StoreType.INDEXDB)
intoMemory()
}
}
}
get(key: string, type?: StoreType): Promise<any> {
return new Promise((resolve, reject) => {
const fromMemory = (key) => {
resolve(this.memory.get(key))
}
const fromIndexDB = (key) => {
const objectStore = this.getObjectStore();
const index = objectStore.index("id");
const res = index.get(key);
res.onsuccess = (e) => {
// @ts-ignore
resolve(e.target.result.value)
}
res.onerror = (e) => {
reject(e)
}
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? fromIndexDB(key) : fromMemory(key)
} else {
this.memory.has(key) ? fromMemory(key) : fromIndexDB(key)
}
})
}
/**
* 查询数据库条数
*/
calculationTimesAndDelete() {
const result = this.getObjectStore().count()
result.onsuccess = (e) => {
// @ts-ignore
if (e.result > this.upperStorageLimit) {
this.deleteFromPrevious()
}
}
}
/**
* 达到阈值后删除最开始建立的数据
* @description 防止数据膨胀
*/
deleteFromPrevious() {
const objectStore = this.getObjectStore()
const request = objectStore.openCursor();
let count = 0
request.onsuccess = (event) => {
// @ts-ignore
const cursor = event.target.result
if (cursor) {
if (count === this.deleteCount) return;
count++;
objectStore.delete(cursor.key)
cursor.continue()
}
}
}
getObjectStore() {
const transaction = this.db.transaction(["store"], "readwrite");
return transaction.objectStore("store")
}
clear(key: string, type?: StoreType) {
const fromMemoryDel = () => this.memory.delete(key)
const fromIndexDBDel = () => {
const objectStore = this.getObjectStore();
objectStore.delete(key)
}
if (typeof type !== 'undefined') {
type === StoreType.INDEXDB ? fromIndexDBDel() : fromMemoryDel()
} else {
this.memory.has(key) ? fromMemoryDel() : fromIndexDBDel()
}
}
initIndexDB() {
// @ts-ignore
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB
if (!indexedDB) {
alert('indexDB被浏览器禁止!')
throw new Error('indexDB forbidden in the browser!')
}
const request = indexedDB.open("elephantFiles", this.dbVersion)
request.onerror = function () {
throw new Error('Error creating/accessing IndexedDB database');
}
request.onsuccess = event => {
// @ts-ignore
this.db = event.target.result as IDBDatabase;
this.isReady = true
}
request.onupgradeneeded = (event) => {
console.log('database upgradeneeded!')
// @ts-ignore
const db = event.target.result as IDBDatabase;
if (!db.objectStoreNames.contains('store')) {
const store = db.createObjectStore('store')
store.createIndex('id', 'id')
}
}
}
/**
* @description 不确认是否初始化完毕时 将查询赋值等操作包裹在此
*/
ready(callback: Function) {
const step = () => {
if (this.isReady) {
callback && callback()
return;
}
window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
}
/**
* @description 估算字节大小, 忽略key的长度, 如果是object只计算value, 比JSON.stringify更快
*/
static sizeOf(value): number {
const typeSizes = {
"undefined": () => 0,
"boolean": () => 4,
"number": () => 8,
"string": item => 2 * item.length,
"object": item => !item ? 0 : Object
.keys(item)
.reduce((total, key) => size(key) + size(item[key]) + total, 0)
};
const size = value => typeSizes[typeof value](value);
return size(value)
}
}
export {StoreType, Store}
使用
const a = new Store();
a.ready(() => {
a.set('a', { k: 'dsdsdsdsdssdsd', p: [123, 'sdsdsdsdsdsdsdvvvvvvvvv'] })
a.set('b', 123)
a.get('b')
a.get('a')
})
根据客户的客户端条件适当调节limitForDB
和upperStorageLimit
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。