Recently, I wrote an interface to get a file directory using Node when I was doing a project. In order to reduce the pressure on the server, I thought of using Redis to do temporary cache, but because I have to change the server recently and I don’t bother to install it, and the amount of data to be stored on my personal website is not too much, can I maintain a cache object by myself?
An idea struck me:
When we start the node server.js service, the service itself runs in memory, so we can write an object that manages itself. Of course, the disadvantages are obvious: 1. The performance is certainly not as good as Redis itself, 2. When the service goes down, the data cache goes away.
Redis | V8 | |
---|---|---|
Implementation language | c | c++ |
perform | C Direct operating system | Compile js code for machine code execution |
Which is faster and more efficient, C or V8(C ++)? Ahem, don’t do that. Don’t circle it. If you are interested, you can search for relevant articles.
Features of Redis:
- Store the data
- To get the data
- With cache time
- Delete after expiration
Self implemented functions:
- Data cache, set expiration time;
- Limit the maximum memory. If the memory exceeds the limit, clean up.
- Reduce the frequency of program execution, data brushing.
Implement a simple cache object
Defining the cache object
const length = Symbol('_length');
const data = Symbol('data');
const cache = {
// Set data to type Symbol to avoid directly viewing/adding/deleting/modifying attributes
[data]: {
// key: { createTime: 1627001616489, value: 'data', overTime: 1000, count: 0 }
[length]: 0
},
// Get the length of the entire cache object
length() {
return this[data][length]; }}Copy the code
If you want to use it on the browser side, replace the data object with Storage, and the idea is the same. Implement a storage with an expiration time
Start with a utility function: number generator
function *createNum() {
let n = 0
while (true) {
yieldn; n++; }}const iter = createNum();
iter.next().value; / / -- > 0
iter.next().value; / / -- > 1
iter.next().value; / / -- > 2
Copy the code
Storage data format
key | type | instructions |
---|---|---|
createTime | Date.now() |
Data storage time |
value | any | Store the object |
overTime | number | Expiration time |
count | number | Record the index. It is possible to store a lot of data at the same time. The smaller the index, the earlier it was stored (use the above utility functions). |
Store & Get
const cache = {
[data]: {
[length]: 0
},
// Store the data
set(key, value, overTime) {
if (key === null || key === undefined || key === ' ') return;
this[data][length] ++;
this[data][key] = {
createTime: Date.now(), // Create time
value, // Store the data
overTime: overTime, // Expiration time
count: iter.next().value, // Index to record the data storage order}},// Get the data
get(key) {
if (!this[data][key]) return;
const time = Date.now();
const obj = this[data][key];
time - obj.createTime > obj.overTime && this.delete(key);
return this[data][key]? .value;/ /? . Prevent error when storing value === undefined}}Copy the code
Attach the full code
cache.js
const { 'log': c, warn } = console;
const length = Symbol('_length');
const data = Symbol('data');
function *createNum() {
let n = 0
while (true) {
yieldn; n++; }}const iter = createNum();
const cache = {
// Set data to type Symbol to avoid directly viewing/adding/deleting/modifying attributes
[data]: {
[length]: 0
},
/** * Set cache data *@param {string | symbol} Key If you do not want to override this attribute, set the key to type Symbol *@param {*} value
* @param {number} OverTime Expiration time. Unit: ms */
set(key, value, overTime) {
if (key === null || key === undefined || key === ' ') return;
this[data][length] ++;
this[data][key] = {
createTime: Date.now(),
value,
overTime: overTime,
count: iter.next().value, // It is possible to store a lot of data in the same millisecond, record an index, the smaller it is, the earlier it is stored}},// Get the data
get(key) {
if (!this[data][key]) return;
const time = Date.now();
const obj = this[data][key];
time - obj.createTime > obj.overTime && this.delete(key);
return this[data][key]? .value;/ /? . Prevent error when storing value === undefined
},
// Delete the data
delete(key) {
if (!this[data][key]) return;
delete this[data][key];
this[data][length] --;
},
// Delete all data
clear() {
this[data] = {}
},
// data.length
length() {
return this[data][length];
},
gainAll() {
return this[data];
},
// Get the size of the data in bytes and calculate it roughly (without differentiating between Chinese and English and symbol type key.length)
size() {
const syms = Object.getOwnPropertySymbols(this[data]);
let symSize = 0;
syms.forEach(val= > {
const symValue = this[data][val];
symSize += JSON.stringify(symValue).length
})
const objSize = JSON.stringify(this[data]).length;
returnsymSize + objSize; }}export default cache;
Copy the code
import cache from './cache.js';
const { 'log': c } = console;
cache.set('the east emperor'.'Can't play'.1000);
cache.set('ying zheng'.'No money to buy'.1000);
cache.set('cloud with'.'the thief slipped'.1213);
cache.set('the best'.'Two shots for one.'.500);
cache.set('Chung Wo-yan'.'Take my hammer.'.43);
cache.set('cloud sakura'.'the thief slipped'.1213);
cache.set('the best'.'Two shots for one.'.400);
setTimeout(() = > {
cache.get('cloud sakura');
cache.get('the best');
c(cache.gainAll());
c(cache.size());
}, 600)
Copy the code
You think that’s the end of it? No, the core features haven’t been implemented yet. The above code only in the data storage to check the data is not expired, that storage is not also should clean up the next memory! Also, limit the size of memory and delete outdated data and the oldest data. The waves behind the Yangtze river rush the waves before, and the new generation changes the old on earth! ^v^
Limit size and clean up memory
import cache from './cache.js';
export default class Redis {
constructor(maxCache) {
this.maxCache = maxCache || 1024 * 1024 * 2; // The maximum number of caches is 2M by default
}
/** * store data. If it already exists and is not expired, you will get it directly *@param {string} Key Specifies the key for storing data@param {*} The value suggestion type is a function (which must return data) that can request data and does not perform * when cached@param {date} OverTime Expiration time *@returns Returns the stored value */
async deposit(key, value, overTime) {
const data = cache.get(key)
if (data) {
return data;
} else {
typeof value === 'function' ? value = await value() : value;
cache.set(key, value, overTime); // Save data before clearing memory to prevent overflow
const size = cache.size(); // Obtain the memory size of the data stored in the container
// If the actual number of cached data is larger than the set number, clear the data
size > this.maxCache && this.clearCache();
returnvalue; }}// Clean up data (expired, old)
clearCache() {
this.deleteOverValue(); // Clear expired data
const size = cache.size();
if (size < this.maxCache) return; // Determine the size of the memory again, if the memory exceeds, do the following operations
const obj = cache.gainAll();
const arr = [];
for (const prop of Object.entries(obj)) {
arr.push({key: prop[0], ...prop[1]});
}
const newArr = choiceSort(arr);
this.deleteFristValue(newArr);
}
// Delete expired data
deleteOverValue() {
const obj = cache.gainAll();
const curTime = Date.now();
for (const prop of Object.entries(obj)) {
const createTime = prop[1].createTime;
const overTime = prop[1].overTime;
if (curTime - createTime > overTime) {
cache.delete(prop[0]); }}}// Delete the earliest cached data
deleteFristValue(arr) {
const key = arr[0].key;
arr.shift();
cache.delete(key);
const size = cache.size();
if (size <= this.maxCache) return;
this.deleteFristValue(arr); // If the memory of the container is still larger than the specified memory, continue to delete}}// Sort the array
function choiceSort(arr) {
const len = arr.length;
if (arr == null || len == 0) return [];
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1; j++) {
const minValue = arr[j];
if (minValue.count > arr[j + 1].count) {
[arr[j], arr[j + 1]] = [arr[j + 1], minValue]; [a, b] --> [b, a]}}}return arr;
}
Copy the code
Knowledge supplement: parameter replacement
var a = 1, b = 2;
// 1. This is what we did before ES6
var c = b;
b = a;
a = c;
console.log(a, b); / / -- > 2 to 1
// 2. ES6+
[a, b] = [b, a];
console.log(a, b); / / -- > 2 to 1
Copy the code
Testing:
import cache from './cache.js';
import Redis from './redis.js';
const redis = new Redis(500); // Limit the memory size
const { 'log': c } = console;
redis.deposit('the east emperor'.'Can't play'.1000);
redis.deposit('ying zheng'.'No money to buy'.1000);
redis.deposit('Hidden in the Ming Dynasty'.'Have a plate'.2000);
redis.deposit('the sable cicada'.'Don't love Lu Bu'.2000);
redis.deposit('Yang Jian'.() = > 'Set the dog on you'.1000);
redis.deposit('Milady'.() = > ['1' creeps.'batman 2'.'batman 3'].600);
redis.deposit(Symbol('AKe'), () = > 'Can't see me'.1000);
redis.deposit('the monkey'.async() = > {return await 'who'
}, 600);
redis.deposit('yuan song'.() = >[{name: 'I could be anyone'},].600);
redis.deposit('Master Lu Ban'.() = > 'Lu Ban no. 7 Father'.1000);
redis.deposit('cloud with'.() = > "Come with me to the Dali Temple.".300);
setTimeout(async () => {
redis.deposit('Luban No. 7'.() = > 'Short legs, fast frequency'.1000);
c(cache.gainAll());
c('size:', cache.size());
const monkey = await redis.deposit('the monkey');
c('the monkey:', monkey)
}, 500)
Copy the code
In node, I’m using KOA
app.get('/label'.async (ctx, next) => {
const time = 1000 * 60 * 20;
const data = await redis.deposit('label'.async() = > {return await getFileCatalogue();
}, time);
ctx.body = data;
next();
})
Copy the code