Authored by yyq

mysql cache

@@ -11,11 +11,12 @@ const TABLE_ACT_PRIZE_PRODUCT = 'act_prize_product'; @@ -11,11 +11,12 @@ const TABLE_ACT_PRIZE_PRODUCT = 'act_prize_product';
11 const TABLE_ACT_PRIZE_PRODUCT_CONTENT = 'act_prize_product_content'; 11 const TABLE_ACT_PRIZE_PRODUCT_CONTENT = 'act_prize_product_content';
12 const TABLE_ACT_PRIZE_PRODUCT_USER = 'act_prize_product_user'; 12 const TABLE_ACT_PRIZE_PRODUCT_USER = 'act_prize_product_user';
13 13
14 -const CACHE_TIMES = 60 * 5; // 缓存时间 14 +const MINUTE_TIMES = 60;
  15 +const PRODUCT_CACHE_TIMES = MINUTE_TIMES * 5; // 商品(列表&详情)缓存时间
  16 +const RECENT_CODE_CACHE_TIME = MINUTE_TIMES; // 最近获取记录缓存时间
15 const MAX_JOIN_TIMES = 2; // 最大活动参与次数 17 const MAX_JOIN_TIMES = 2; // 最大活动参与次数
16 18
17 const userTimesCache = new MemoryCache(); 19 const userTimesCache = new MemoryCache();
18 -const productCache = new MemoryCache(300);  
19 20
20 function handelResult(result) { 21 function handelResult(result) {
21 return { 22 return {
@@ -57,16 +58,6 @@ module.exports = class extends global.yoho.BaseModel { @@ -57,16 +58,6 @@ module.exports = class extends global.yoho.BaseModel {
57 status = parseInt(status, 10); 58 status = parseInt(status, 10);
58 page = parseInt(page, 10) || 1; 59 page = parseInt(page, 10) || 1;
59 60
60 - const listCacheKey = `list_${actId}_${status}_${page}_${status}`;  
61 -  
62 - if (!extra.noCache) {  
63 - let cacheList = productCache.get(listCacheKey);  
64 -  
65 - if (!_.isUndefined(cacheList)) {  
66 - return handelResult(cacheList);  
67 - }  
68 - }  
69 -  
70 status = _.isNaN(status) ? '> 0' : `= ${status}`; 61 status = _.isNaN(status) ? '> 0' : `= ${status}`;
71 62
72 let limit = `${(page - 1) * pageSize},${page * pageSize}`; 63 let limit = `${(page - 1) * pageSize},${page * pageSize}`;
@@ -74,11 +65,9 @@ module.exports = class extends global.yoho.BaseModel { @@ -74,11 +65,9 @@ module.exports = class extends global.yoho.BaseModel {
74 return mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT} 65 return mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
75 where act_id = :actId and status ${status} limit ${limit}`, { 66 where act_id = :actId and status ${status} limit ${limit}`, {
76 actId 67 actId
77 - }).then(result => {  
78 - productCache.set(listCacheKey, result, CACHE_TIMES);  
79 -  
80 - return handelResult(result);  
81 - }); 68 + }, {
  69 + cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
  70 + }).then(handelResult);
82 } 71 }
83 72
84 /** 73 /**
@@ -114,21 +103,15 @@ module.exports = class extends global.yoho.BaseModel { @@ -114,21 +103,15 @@ module.exports = class extends global.yoho.BaseModel {
114 return Promise.resolve(handelResult(resData)); 103 return Promise.resolve(handelResult(resData));
115 } 104 }
116 105
117 - const contentCacheKey = `content_${actPrizeId}`;  
118 -  
119 - if (!extra.noCache) {  
120 - let cacheContent = productCache.get(contentCacheKey);  
121 -  
122 - if (!_.isUndefined(cacheContent)) {  
123 - return handelResult(cacheContent);  
124 - }  
125 - }  
126 -  
127 return Promise.all([ 106 return Promise.all([
128 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT} 107 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
129 - where id = :actPrizeId limit 1;`, {actPrizeId}), 108 + where id = :actPrizeId limit 1;`, {actPrizeId}, {
  109 + cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
  110 + }),
130 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_CONTENT} 111 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_CONTENT}
131 - where act_prize_id = :actPrizeId;`, {actPrizeId}) 112 + where act_prize_id = :actPrizeId;`, {actPrizeId}, {
  113 + cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
  114 + })
132 ]).then(result => { 115 ]).then(result => {
133 let [product, content] = result; 116 let [product, content] = result;
134 117
@@ -139,20 +122,27 @@ module.exports = class extends global.yoho.BaseModel { @@ -139,20 +122,27 @@ module.exports = class extends global.yoho.BaseModel {
139 }); 122 });
140 } 123 }
141 124
142 - productCache.set(contentCacheKey, resData, CACHE_TIMES);  
143 -  
144 return resData; 125 return resData;
145 }).then(handelResult); 126 }).then(handelResult);
146 } 127 }
147 128
148 /** 129 /**
149 * 0元购抽奖码最近获取记录 130 * 0元购抽奖码最近获取记录
  131 + * @param actId
150 * @returns {*} 132 * @returns {*}
151 */ 133 */
152 - getCodeRecent() { 134 + getCodeRecent(actId) {
  135 + actId = parseInt(actId, 10) || 0;
  136 +
153 return mysqlCli.query(`select user_name, user_thumb, create_time 137 return mysqlCli.query(`select user_name, user_thumb, create_time
154 - from ${TABLE_ACT_PRIZE_PRODUCT_USER}  
155 - order by u.create_time desc limit 10;`).then(handelResult); 138 + from ${TABLE_ACT_PRIZE_PRODUCT_USER} where act_id = :actId
  139 + order by u.create_time desc limit 10;`, {
  140 + actId
  141 + }, {
  142 + cache: RECENT_CODE_CACHE_TIME
  143 + }).then(result => {
  144 + return handelResult(result);
  145 + });
156 } 146 }
157 147
158 /** 148 /**
@@ -184,7 +174,7 @@ module.exports = class extends global.yoho.BaseModel { @@ -184,7 +174,7 @@ module.exports = class extends global.yoho.BaseModel {
184 174
185 let info = await Promise.all([ 175 let info = await Promise.all([
186 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT} 176 mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
187 - where id = :actPrizeId limit 1;`, {actPrizeId}), 177 + where id = :actPrizeId limit 1;`, {actPrizeId}, {cache: MINUTE_TIMES / 2}),
188 mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER} 178 mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
189 where act_prize_id = :actPrizeId;`, {actPrizeId}) 179 where act_prize_id = :actPrizeId;`, {actPrizeId})
190 ]); 180 ]);
@@ -241,7 +231,7 @@ module.exports = class extends global.yoho.BaseModel { @@ -241,7 +231,7 @@ module.exports = class extends global.yoho.BaseModel {
241 */ 231 */
242 async sendPrizeCode(uid, actPrizeId, extra = {}) { 232 async sendPrizeCode(uid, actPrizeId, extra = {}) {
243 // 查询用户参与次数 233 // 查询用户参与次数
244 - const TimesCacheKey = `${actPrizeId}_${uid}`; 234 + const TimesCacheKey = `times_${actPrizeId}_${uid}`;
245 235
246 let userJoinTimes = userTimesCache.get(TimesCacheKey); 236 let userJoinTimes = userTimesCache.get(TimesCacheKey);
247 237
@@ -252,6 +242,7 @@ module.exports = class extends global.yoho.BaseModel { @@ -252,6 +242,7 @@ module.exports = class extends global.yoho.BaseModel {
252 uid 242 uid
253 }); 243 });
254 244
  245 + // 用户参与次数超限(业务缓存)
255 if (userJoinTimes >= MAX_JOIN_TIMES) { 246 if (userJoinTimes >= MAX_JOIN_TIMES) {
256 userTimesCache.set(TimesCacheKey, userJoinTimes); 247 userTimesCache.set(TimesCacheKey, userJoinTimes);
257 } 248 }
@@ -67,6 +67,7 @@ @@ -67,6 +67,7 @@
67 "plupload": "^2.3.3", 67 "plupload": "^2.3.3",
68 "qiniu": "^7.0.4", 68 "qiniu": "^7.0.4",
69 "qiniu-js": "^1.0.22", 69 "qiniu-js": "^1.0.22",
  70 + "querystring": "^0.2.0",
70 "request": "^2.81.0", 71 "request": "^2.81.0",
71 "request-promise": "^4.2.1", 72 "request-promise": "^4.2.1",
72 "semver": "^5.3.0", 73 "semver": "^5.3.0",
@@ -6,74 +6,77 @@ @@ -6,74 +6,77 @@
6 6
7 const _ = require('lodash'); 7 const _ = require('lodash');
8 8
9 -function _clearArray(list, key) {  
10 - _.remove(list, (n) => {  
11 - return n === key;  
12 - });  
13 -}  
14 9
15 -function _clearObject(obj, key) {  
16 - delete obj[key];  
17 -}  
18 -  
19 -function _getNowTime() {  
20 - return new Date().getTime() / 1000;  
21 -}  
22 -  
23 -module.exports = function memoryCache(maxLength = 1000) {  
24 - let cache = {};  
25 - let mapList = []; 10 +module.exports = class memoryCache {
  11 + constructor(options) {
  12 + this.options = Object.assign({}, options);
  13 + this.cache = {};
  14 + this.map = [];
  15 +
  16 + this.options.maxLength = parseInt(this.options.maxLength, 10) || 1000;
  17 + }
  18 + _deleteCache(key) {
  19 + delete this.cache[key];
  20 + }
  21 + _deleteMap(key) {
  22 + _.remove(this.map, (n) => {
  23 + return n === key;
  24 + });
  25 + }
  26 + _getNowTime() {
  27 + return new Date().getTime() / 1000;
  28 + }
26 29
27 // 获取缓存数据 30 // 获取缓存数据
28 - this.get = (key) => {  
29 - if (!cache.hasOwnProperty(key)) { 31 + get(key) {
  32 + if (!this.cache.hasOwnProperty(key)) {
30 return; 33 return;
31 } 34 }
32 35
33 - let info = cache[key]; 36 + let info = this.cache[key];
34 37
35 // 校验过期时间 38 // 校验过期时间
36 - if (info.exptime && info.exptime - _getNowTime() < 0) { 39 + if (info.exptime && info.exptime - this._getNowTime() < 0) {
37 this.clear(key); 40 this.clear(key);
38 return; 41 return;
39 } 42 }
40 43
41 return info.value; 44 return info.value;
42 - }; 45 + }
43 46
44 // 设置缓存数据 47 // 设置缓存数据
45 - this.set = (key, value, exptime) => {  
46 - cache.hasOwnProperty(key) && _clearArray(mapList, key); 48 + set(key, value, exptime) {
  49 + this.cache.hasOwnProperty(key) && this._deleteMap(key);
47 50
48 - mapList.push(key);  
49 - cache[key] = { 51 + this.map.push(key);
  52 + this.cache[key] = {
50 value, 53 value,
51 - exptime: exptime ? (exptime + _getNowTime()) : 0 // 过期时间 54 + exptime: exptime ? (exptime + this._getNowTime()) : 0 // 过期时间
52 }; 55 };
53 56
  57 + const maxLength = this.options.maxLength;
  58 +
54 // 清除老旧数据 59 // 清除老旧数据
55 - if (mapList.length > maxLength) {  
56 - let len = mapList.length - maxLength; 60 + if (this.map.length > maxLength) {
  61 + let len = this.map.length - maxLength;
57 62
58 for (let i = 0; i < len; i++) { 63 for (let i = 0; i < len; i++) {
59 - _clearObject(cache, mapList.shift()); 64 + this._deleteCache(this.map.shift());
60 } 65 }
61 } 66 }
62 - }; 67 + }
63 68
64 // 清除单条缓存数据 69 // 清除单条缓存数据
65 - this.clear = (key) => {  
66 - if (cache.hasOwnProperty(key)) {  
67 - _clearArray(mapList, key);  
68 - _clearObject(cache, key); 70 + clear(key) {
  71 + if (this.cache.hasOwnProperty(key)) {
  72 + this._deleteMap(key);
  73 + this._deleteCache(key);
69 } 74 }
70 - }; 75 + }
71 76
72 // 清除所有缓存数据 77 // 清除所有缓存数据
73 - this.clearAll = () => {  
74 - cache = {};  
75 - mapList = [];  
76 - };  
77 -  
78 - return this; 78 + clearAll() {
  79 + this.cache = {};
  80 + this.map = [];
  81 + }
79 }; 82 };
1 const mysql = require('mysql'); 1 const mysql = require('mysql');
2 const _ = require('lodash'); 2 const _ = require('lodash');
  3 +const md5 = require('yoho-md5');
  4 +const qs = require('querystring');
  5 +const MemoryCache = require('./memory-cache');
3 6
4 class SqlHelper { 7 class SqlHelper {
5 constructor(database) { 8 constructor(database) {
6 this.config = global.yoho.config.mysql; 9 this.config = global.yoho.config.mysql;
7 this.logger = global.yoho.logger; 10 this.logger = global.yoho.logger;
  11 + this.memoryCache = new MemoryCache();
8 database = database || 'mysql'; 12 database = database || 'mysql';
9 this.createPool(database); 13 this.createPool(database);
10 } 14 }
@@ -37,8 +41,8 @@ class SqlHelper { @@ -37,8 +41,8 @@ class SqlHelper {
37 }); 41 });
38 }); 42 });
39 } 43 }
40 - query(sql, params) {  
41 - return this.execute(sql, params); 44 + query(sql, params, options) {
  45 + return this.execute(sql, params, options);
42 } 46 }
43 delete(sql, params) { 47 delete(sql, params) {
44 return this.execute(sql, params).then(result => { 48 return this.execute(sql, params).then(result => {
@@ -55,7 +59,21 @@ class SqlHelper { @@ -55,7 +59,21 @@ class SqlHelper {
55 return result.insertId; 59 return result.insertId;
56 }); 60 });
57 } 61 }
58 - execute(sql, params) { 62 + execute(sql, params, options = {}) {
  63 + let cache = parseInt(options.cache, 10) || 0;
  64 + let cacheKey;
  65 +
  66 + // 读取缓存
  67 + if (cache) {
  68 + cacheKey = md5(`${sql}${qs.stringify(params)}${cache}`);
  69 +
  70 + let cacheResult = this.memoryCache.get(cacheKey);
  71 +
  72 + if (!_.isUndefined(cacheResult)) {
  73 + return Promise.resolve(cacheResult);
  74 + }
  75 + }
  76 +
59 return new Promise((resolve, reject) => { 77 return new Promise((resolve, reject) => {
60 this.getConnection().then(connection => { 78 this.getConnection().then(connection => {
61 connection.query(sql, params, (queryErr, result) => { 79 connection.query(sql, params, (queryErr, result) => {
@@ -64,6 +82,8 @@ class SqlHelper { @@ -64,6 +82,8 @@ class SqlHelper {
64 this.logger.error(queryErr); 82 this.logger.error(queryErr);
65 reject(queryErr); 83 reject(queryErr);
66 } else { 84 } else {
  85 + // 写入缓存
  86 + cache && this.memoryCache.set(cacheKey, result, cache);
67 resolve(result); 87 resolve(result);
68 } 88 }
69 }); 89 });
@@ -7986,6 +7986,10 @@ querystring@0.2.0: @@ -7986,6 +7986,10 @@ querystring@0.2.0:
7986 version "0.2.0" 7986 version "0.2.0"
7987 resolved "http://npm.yohops.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 7987 resolved "http://npm.yohops.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
7988 7988
  7989 +querystring@^0.2.0:
  7990 + version "0.2.0"
  7991 + resolved "http://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
  7992 +
7989 querystringify@0.0.x: 7993 querystringify@0.0.x:
7990 version "0.0.4" 7994 version "0.0.4"
7991 resolved "http://npm.yohops.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" 7995 resolved "http://npm.yohops.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c"