Authored by yyq

mysql cache

... ... @@ -11,11 +11,12 @@ const TABLE_ACT_PRIZE_PRODUCT = 'act_prize_product';
const TABLE_ACT_PRIZE_PRODUCT_CONTENT = 'act_prize_product_content';
const TABLE_ACT_PRIZE_PRODUCT_USER = 'act_prize_product_user';
const CACHE_TIMES = 60 * 5; // 缓存时间
const MINUTE_TIMES = 60;
const PRODUCT_CACHE_TIMES = MINUTE_TIMES * 5; // 商品(列表&详情)缓存时间
const RECENT_CODE_CACHE_TIME = MINUTE_TIMES; // 最近获取记录缓存时间
const MAX_JOIN_TIMES = 2; // 最大活动参与次数
const userTimesCache = new MemoryCache();
const productCache = new MemoryCache(300);
function handelResult(result) {
return {
... ... @@ -57,16 +58,6 @@ module.exports = class extends global.yoho.BaseModel {
status = parseInt(status, 10);
page = parseInt(page, 10) || 1;
const listCacheKey = `list_${actId}_${status}_${page}_${status}`;
if (!extra.noCache) {
let cacheList = productCache.get(listCacheKey);
if (!_.isUndefined(cacheList)) {
return handelResult(cacheList);
}
}
status = _.isNaN(status) ? '> 0' : `= ${status}`;
let limit = `${(page - 1) * pageSize},${page * pageSize}`;
... ... @@ -74,11 +65,9 @@ module.exports = class extends global.yoho.BaseModel {
return mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where act_id = :actId and status ${status} limit ${limit}`, {
actId
}).then(result => {
productCache.set(listCacheKey, result, CACHE_TIMES);
return handelResult(result);
});
}, {
cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
}).then(handelResult);
}
/**
... ... @@ -114,21 +103,15 @@ module.exports = class extends global.yoho.BaseModel {
return Promise.resolve(handelResult(resData));
}
const contentCacheKey = `content_${actPrizeId}`;
if (!extra.noCache) {
let cacheContent = productCache.get(contentCacheKey);
if (!_.isUndefined(cacheContent)) {
return handelResult(cacheContent);
}
}
return Promise.all([
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1;`, {actPrizeId}),
where id = :actPrizeId limit 1;`, {actPrizeId}, {
cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
}),
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_CONTENT}
where act_prize_id = :actPrizeId;`, {actPrizeId})
where act_prize_id = :actPrizeId;`, {actPrizeId}, {
cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
})
]).then(result => {
let [product, content] = result;
... ... @@ -139,20 +122,27 @@ module.exports = class extends global.yoho.BaseModel {
});
}
productCache.set(contentCacheKey, resData, CACHE_TIMES);
return resData;
}).then(handelResult);
}
/**
* 0元购抽奖码最近获取记录
* @param actId
* @returns {*}
*/
getCodeRecent() {
getCodeRecent(actId) {
actId = parseInt(actId, 10) || 0;
return mysqlCli.query(`select user_name, user_thumb, create_time
from ${TABLE_ACT_PRIZE_PRODUCT_USER}
order by u.create_time desc limit 10;`).then(handelResult);
from ${TABLE_ACT_PRIZE_PRODUCT_USER} where act_id = :actId
order by u.create_time desc limit 10;`, {
actId
}, {
cache: RECENT_CODE_CACHE_TIME
}).then(result => {
return handelResult(result);
});
}
/**
... ... @@ -184,7 +174,7 @@ module.exports = class extends global.yoho.BaseModel {
let info = await Promise.all([
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1;`, {actPrizeId}),
where id = :actPrizeId limit 1;`, {actPrizeId}, {cache: MINUTE_TIMES / 2}),
mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId;`, {actPrizeId})
]);
... ... @@ -241,7 +231,7 @@ module.exports = class extends global.yoho.BaseModel {
*/
async sendPrizeCode(uid, actPrizeId, extra = {}) {
// 查询用户参与次数
const TimesCacheKey = `${actPrizeId}_${uid}`;
const TimesCacheKey = `times_${actPrizeId}_${uid}`;
let userJoinTimes = userTimesCache.get(TimesCacheKey);
... ... @@ -252,6 +242,7 @@ module.exports = class extends global.yoho.BaseModel {
uid
});
// 用户参与次数超限(业务缓存)
if (userJoinTimes >= MAX_JOIN_TIMES) {
userTimesCache.set(TimesCacheKey, userJoinTimes);
}
... ...
... ... @@ -67,6 +67,7 @@
"plupload": "^2.3.3",
"qiniu": "^7.0.4",
"qiniu-js": "^1.0.22",
"querystring": "^0.2.0",
"request": "^2.81.0",
"request-promise": "^4.2.1",
"semver": "^5.3.0",
... ...
... ... @@ -6,74 +6,77 @@
const _ = require('lodash');
function _clearArray(list, key) {
_.remove(list, (n) => {
return n === key;
});
}
function _clearObject(obj, key) {
delete obj[key];
}
module.exports = class memoryCache {
constructor(options) {
this.options = Object.assign({}, options);
this.cache = {};
this.map = [];
function _getNowTime() {
this.options.maxLength = parseInt(this.options.maxLength, 10) || 1000;
}
_deleteCache(key) {
delete this.cache[key];
}
_deleteMap(key) {
_.remove(this.map, (n) => {
return n === key;
});
}
_getNowTime() {
return new Date().getTime() / 1000;
}
module.exports = function memoryCache(maxLength = 1000) {
let cache = {};
let mapList = [];
}
// 获取缓存数据
this.get = (key) => {
if (!cache.hasOwnProperty(key)) {
get(key) {
if (!this.cache.hasOwnProperty(key)) {
return;
}
let info = cache[key];
let info = this.cache[key];
// 校验过期时间
if (info.exptime && info.exptime - _getNowTime() < 0) {
if (info.exptime && info.exptime - this._getNowTime() < 0) {
this.clear(key);
return;
}
return info.value;
};
}
// 设置缓存数据
this.set = (key, value, exptime) => {
cache.hasOwnProperty(key) && _clearArray(mapList, key);
set(key, value, exptime) {
this.cache.hasOwnProperty(key) && this._deleteMap(key);
mapList.push(key);
cache[key] = {
this.map.push(key);
this.cache[key] = {
value,
exptime: exptime ? (exptime + _getNowTime()) : 0 // 过期时间
exptime: exptime ? (exptime + this._getNowTime()) : 0 // 过期时间
};
const maxLength = this.options.maxLength;
// 清除老旧数据
if (mapList.length > maxLength) {
let len = mapList.length - maxLength;
if (this.map.length > maxLength) {
let len = this.map.length - maxLength;
for (let i = 0; i < len; i++) {
_clearObject(cache, mapList.shift());
this._deleteCache(this.map.shift());
}
}
}
};
// 清除单条缓存数据
this.clear = (key) => {
if (cache.hasOwnProperty(key)) {
_clearArray(mapList, key);
_clearObject(cache, key);
clear(key) {
if (this.cache.hasOwnProperty(key)) {
this._deleteMap(key);
this._deleteCache(key);
}
}
};
// 清除所有缓存数据
this.clearAll = () => {
cache = {};
mapList = [];
};
return this;
clearAll() {
this.cache = {};
this.map = [];
}
};
... ...
const mysql = require('mysql');
const _ = require('lodash');
const md5 = require('yoho-md5');
const qs = require('querystring');
const MemoryCache = require('./memory-cache');
class SqlHelper {
constructor(database) {
this.config = global.yoho.config.mysql;
this.logger = global.yoho.logger;
this.memoryCache = new MemoryCache();
database = database || 'mysql';
this.createPool(database);
}
... ... @@ -37,8 +41,8 @@ class SqlHelper {
});
});
}
query(sql, params) {
return this.execute(sql, params);
query(sql, params, options) {
return this.execute(sql, params, options);
}
delete(sql, params) {
return this.execute(sql, params).then(result => {
... ... @@ -55,7 +59,21 @@ class SqlHelper {
return result.insertId;
});
}
execute(sql, params) {
execute(sql, params, options = {}) {
let cache = parseInt(options.cache, 10) || 0;
let cacheKey;
// 读取缓存
if (cache) {
cacheKey = md5(`${sql}${qs.stringify(params)}${cache}`);
let cacheResult = this.memoryCache.get(cacheKey);
if (!_.isUndefined(cacheResult)) {
return Promise.resolve(cacheResult);
}
}
return new Promise((resolve, reject) => {
this.getConnection().then(connection => {
connection.query(sql, params, (queryErr, result) => {
... ... @@ -64,6 +82,8 @@ class SqlHelper {
this.logger.error(queryErr);
reject(queryErr);
} else {
// 写入缓存
cache && this.memoryCache.set(cacheKey, result, cache);
resolve(result);
}
});
... ...
... ... @@ -7986,6 +7986,10 @@ querystring@0.2.0:
version "0.2.0"
resolved "http://npm.yohops.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
querystring@^0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
querystringify@0.0.x:
version "0.0.4"
resolved "http://npm.yohops.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c"
... ...