基于mongoose和redis的数据库缓存方案dbcached

介绍

这是一个基于mongoose和redis的数据库缓存方案dbcached, 主要目的为减少查询直接访问数据库的次数.
只针对单表系统, 不处理表关联, 不处理字段选取. 比较适合记录数据内容不是大文章类型, restful方案的项目.

链接地址https://github.com/windsome/dbcached

应用程序调用api时,首先从redis缓存中获取数据,未获取到向数据库获取,并更新到redis缓存,通过api返回.
此程序库有两套接口,

  1. 直接调用mongoose的数据库方法.
  2. 使用redis缓存.

实现方案

createDbOps返回的是直接对数据库操作的接口. createCachedOps返回的是带缓存的接口,主要优化的是查询缓存方面.
redis缓存中,存在3类键用来加速.

  • d键, 单条记录的数据, 将数据转成字符串, (有性能损耗)
    • key_d = generateRedisKey(model, ‘d’, item._id), 如: “device##d##612edab50a3d97e9130af795”
    • 设置 $r.setexAsync(key_d, EX_SECONDS, JSON.stringify(item))
  • s键, 记录查询结果id列表的键
    • key_s = generateRedisKey(model, ‘s’, where, sort), 如: “device##s##{“name”:”$regex-/Jack/i"}##"
    • 设置 $r.zaddAsync(key_s, …argsArray);
    • 可以根据返回的index作为score值,记录分页数据.
  • c键, 记录查询结果的个数. db的count查询比较费时间, 所以用了matcher算法去自行增减.
    • key_c = generateRedisKey(model, ‘c’, where, sort)
    • 设置 $r.setexAsync(key_c, EX_SECONDS, data_c)

主要优化逻辑在cached/index的findCreator函数中, 主要过程就是判断该查询是否存在,数据是否满足,满足直接返回cache, 否则进行数据查询, 并更新到cache中.
创建/更新/删除, 通过matcher判断对哪些查询产生影响, 并更新相应的查询.

具体用例请参考test目录.
test/mockdata.js下的cfg是配置,修改成自己的.
schemas是测试表结构.
run-cache-find.js是测试文件, 执行node -r esm run-cache-find.js看结果.
注意: 需要先运行yarn compile编译到lib目录. 该测试引用的是lib目录文件.

安装使用

## 安装
yarn add dbcache

## db方法
import { createDbOps } from 'dbcache'

import schemas from './test/schemas';
const dbops = createDbOps('mongodb://admin:admin@localhost:27017/testcache?authSource=admin', schemas);

let items = await dbops.find('device', {where:{name:'王老板'}});
console.log('items', items);

await dbops.destroy(); //释放.


## redis方法
import { createDbOps, createCachedOps } from 'dbcache'

import schemas from './test/schemas';
const dbops = createDbOps('mongodb://admin:admin@localhost:27017/testcache?authSource=admin', schemas);
let ops = await createCachedOps({
  url: 'redis://localhost:6379/1',
  dbops,
});

let items = await ops.find('device', {where:{name:'王老板'}});
console.log('items', items);

await ops.destroy() // 释放
await dbops.destroy() // 释放

接口

export interface DbOps {
  createOne: (model: string, data: JsonData) => Promise<JsonData | null>;
  createMany: (model: string, items: JsonData[]) => Promise<JsonData[] | null>;
  find: (model: string, options: QueryOptions) => Promise<JsonData[]>;
  updateOne: (
    model: string,
    where: JsonData,
    data: JsonData,
    options?: JsonData,
  ) => Promise<JsonData | null>;
  updateMany: (
    model: string,
    where: JsonData,
    data: JsonData,
    options?: JsonData,
  ) => Promise<JsonData[]>;
  deleteOne: (
    model: string,
    where: JsonData,
    options?: JsonData,
  ) => Promise<number>;
  deleteMany: (
    model: string,
    where: JsonData,
    options?: JsonData,
  ) => Promise<number>;
  count: (model: string, options: QueryOptions) => Promise<number>;

  deleteOneById: (model: string, id: string) => Promise<number>; //deleteOne
  updateOneById: (
    model: string,
    id: string,
    data: JsonData,
    options?: JsonData,
  ) => Promise<JsonData | null>; // updateOne
  findOne: (
    model: string,
    where: JsonData,
    options?: JsonData,
  ) => Promise<JsonData>; // retrieve
  findOneById: (
    model: string,
    id: string,
    options?: JsonData,
  ) => Promise<JsonData>; // findOne

  destroy: () => Promise<boolean>;
}

存在的问题

  1. db返回数据做了过多费性能处理.
  • 从db接口返回的数据都做了 JSON.parse(JSON.stringify(item/items))处理,使得返回的ObjectId都转成了字符串, 这会损失性能, 未来需要优化.
  • 当前这么做的原因是,无法确切知道ObjectId的类型,用typeof()获得的是[object object],无法与其他的区分, 导致在做matcher时, 无法匹配此类型.
  • 未来找到界定ObjectId类型的方法后,可以参考regexp正则表达式的处理方法,进行正反转换.
  1. 批量更新,删除数据时,检测哪些查询过期了,目前是将每个查询与数据运算,判断数据是否满足查询. 未来,可以直接将查询条件求交集,如果有交集则表示该查询应该更新,count也需要重新查询更新.
  2. 目前未防止缓存数据直接穿透到数据库. 当某个查询过期时, 目前直接将该查询的s键删除无效, 未来可以将该键插入待更新队列, 由更新任务异步去更新.
  3. 分页查询, 部分页面过期了, 导致数据穿透, 是否有优化空间?
  4. 数据库操作应该转成队列执行, 相连接的相同查询且中间没有更新/创建/删除命令的可以合并成一个,减少查询次数. 接口则等待数据返回.

注意事项

  1. 用node运行测试程序时,可能遇到不能识别import的问题. 需要yarn add -D esm, 然后运行node -r esm <js文件>.

  2. 当需要npm publish时, 且机器上默认使用阿里源, 需要指定源推送到npmjs上.
    npm publish --registry https://registry.npmjs.org

  3. 发布或更新也可以遵循如下操作

  • nrm use npm 切换到npm源
  • 如果更新:npm version major升大版本,或者npm version minor/patch为升中小版本.
  • 发布 npm publish
  • npm好像1.0.0以上的版本才可以用
  1. 可以用nrm源管理工具,方便切换源.
    https://github.com/Pana/nrm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值