https://github.com/cclient/sequelize-sharding
https://www.npmjs.com/package/sequelize-sharding

实际有效的代码就几行,主要时间都花在这篇博客和readme.md上

————————————————

公司里有node项目用sequelize插件操作mysql

mysql部分表涉及到分表方案,而sequelizel 默认不支持分表

mysql服务端运维层面,java客户端应用层面,对分库分表的支持比较成熟,而nodejs客户端就比较欠缺了

sequelize默认不支持,只能选择手动扩展

思路和java方面的一致,都很简单,就是在dao操作前后作一个切面,定制个改要操作的库/表,不只是分库分表,读写分离,异构表优化,多租户资源隔离,都是按这种思路作的,sequelize也一致

简单梳理了一下sequelize的执行结构

用户操作直接面对的是Model

Model定义类似

export class User {
constructor(id: number,name: string,created_on: Date,updated_on: Date) {
this.id = id
this.name = name
this.created_on = created_on
this.updated_on = updated_on
}
id: number
name: string
created_on: Date
updated_on: Date
} const USER_ORM_MAP={
id: { type: Sequelize.INTEGER, autoIncrement: true,primaryKey: true },
name : Sequelize.STRING,
created_on : Sequelize.DATE,
updated_on : Sequelize.DATE
} const DUser = sequelize.define("user", USER_ORM_MAP,{ freezeTableName: true,createdAt:false,updatedAt:false,deletedAt:false});

所有的dao操作都过Model类的实例DUser完成

export async function  getUsersByIds(ids:Array<string>) :Promise<Array<User>>
{
if(site_ids.length==0){
return [];
}
let hasUsers= await DUser.findAll({where:{id:ids}})
return hasUsers as Array<User>;
}

Model源码
https://github.com/sequelize/sequelize/blob/b37985d550e8aeb2bc518f3bd5dcb09c95a142d8/lib/model.js
内有两个核心对象

static get QueryInterface() {
return this.sequelize.getQueryInterface();
} static get QueryGenerator() {
return this.QueryInterface.QueryGenerator;
}

Model对sql的操作通过QueryGenerator实现

https://github.com/sequelize/sequelize/blob/b37985d550e8aeb2bc518f3bd5dcb09c95a142d8/test/unit/sql/insert.test.js

const Support   = require('../support'),
DataTypes = require('../../../lib/data-types'),
expectsql = Support.expectsql,
current = Support.sequelize,
sql = current.dialect.QueryGenerator; expectsql(sql.insertQuery(User.tableName, {user_name: 'triggertest'}, User.rawAttributes, options),
{
query: {
mssql: 'declare @tmp table ([id] INTEGER,[user_name] NVARCHAR(255));INSERT INTO [users] ([user_name]) OUTPUT INSERTED.[id],INSERTED.[user_name] into @tmp VALUES ($1);select * from @tmp;',
postgres: 'INSERT INTO "users" ("user_name") VALUES ($1) RETURNING *;',
default: 'INSERT INTO `users` (`user_name`) VALUES ($1);'
},
bind: ['triggertest']
});

源码不用再看,到这里就够了

sql = current.dialect.QueryGenerator;
sql.insertQuery(User.tableName, {user_name: 'triggertest'}, User.rawAttributes, options)

再看Model的源码 tableName的部分相关信息

* @param {string}                  [options.tableName] Defaults to pluralized model name, unless freezeTableName is true, in which case it uses model name verbatim

if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? this.name : Utils.underscoredIf(Utils.pluralize(this.name), this.underscored);
} else {
this.tableName = this.options.tableName;
} static getTableName() { // testhint options:none
return this.QueryGenerator.addSchema(this);
}

默认不公开tableName字段,也不提供setTableName,tableName完全在定义时构造

理论上更改User.tableName,即可完成换表操作

测试

if (!module.parent) {
getUsersByIds([1,2,3,4]).then(u1=>{
DUser.tableName="user2";
}).then(()=>{
getUsersByIds([1,2,3,4]).then(bbb=>{
})
})
}

打印日志

sequelize deprecated String based operators are now deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/tutorial/querying.html#operators node_modules/sequelize/lib/sequelize.js:237:13

Executing (default): SELECT `id`, `name`, `created_on`, `updated_on` FROM `user` AS `user` WHERE `user`.`id` IN ('1', '2');

Executing (default): SELECT `id`, `name`, `created_on`, `updated_on` FROM `user2` AS `user` WHERE `user`.`id` IN ('1', '2');

验证成功

也幸好tableName并未设为不可修改,不然改sequelize源码还得多花点工夫

剩下就是功能开发了

在User操作之前

let customTableName=getCustomTableName(user);
User.tableName=customTableName;
User.insert(user);

这样作代码侵入性较高,少点改改还可以,大规模应用则太不优雅

优雅的方案
1 定制修改 sequelize 代码,使支持分表,对sequelize源码不是非常熟悉,满足某列动态生成的的需求,最多1天搞定,如果想优雅的提到源码里,要花的工夫就多了
2 定制切面 shimmer,这是nodejs aop编程的工具,目的是aop,实现方式和java不同,java是运行时通过反射动态构造类,nodejs是在包裹原始包的的对象,像是装饰者模式
3 思路和2类似,其实sequelize提了原生的切面,用原生切面即可

sequelize提供几项Hooks
https://github.com/sequelize/sequelize/blob/master/docs/hooks.md
原本是为数据操作提供入口

User.addHook('beforeValidate', (user, options) => {
user.mood = 'happy';
});

主要是操作对象是user实体,hooks算是sequelize默认提供的部分切面

我们可以看看在部分hooks上能否实现对User.tableName的更改

查看所有 hooks https://github.com/sequelize/sequelize/blob/master/lib/hooks.js

先在beforeFind上作测试

if (!module.parent) {
User.addHook('beforeFind', (user, options) => {
User.tableName="user2";
console.log("beforeFind","set custom tableName",User.tableName)
}) getUsersByIds([1,2,3,4]).then(u1=>{
}).then(()=>{
getUsersByIds([1,2,3,4]).then(bbb=>{
})
})
}

看日志 果然生效

最终选型,先用方案3快速实现,方案3完成后,有精力用方案1实现,方案2因为方案3的存在,没什么应用的价值

----

源码写的不是很细致,只是满足现阶段最初级的需求,更细化和其他拓展需求,可以用同样的思路实现

去掉异常检测的代码,可以去掉所有依赖

实现用ts,看sequelize原生类结构清晰,测试则是js,js原生代码很久不写了,凑合吧

最后其实分库分表分区的应用,有点过气的感觉(应用还是很广,但因为各方面都很成熟完备,没有再讨论优化的空间,显得很冷清),有空可以单写一篇出来,技术性的文章太多了,主要想扯点非技术性的东西,给过气的东西一个总结

sequelize 应用hook 实现对分表的访问的更多相关文章

  1. Oracle错误:动态执行表不可访问,本会话自动统计被禁止,关闭自动统计之后的问题

    使用PL/SQL时, 每次第一次打开表的时候会提示"动态执行表不可访问,本会话的自动统计被禁止"的错误,一消息如下: V$SESSION,V$SESSTAT,V$STATNAME没 ...

  2. PLSQL Developer报“动态执行表不可访问,本会话的自动统计被禁止”的解决方案

    现象与提示: 第一次用PLSQL Developer连接数据库,若用sys用户登录并操作则正常,若用普通用户比如haishu登录并创建一个表则报错"动态执行表不可访问,本会话的自动统计被禁止 ...

  3. Oracl 动态执行表不可访问,本会话的自动统计被禁止

    oracle ---建立SQL窗体 写入 select * from tableA; 弹出错误窗口 : 动态执行表不可访问,本会话的自动统计被禁止.在执行菜单里你可以禁止统计,或在v$session, ...

  4. Oracle\PLSQL Developer报“动态执行表不可访问,本会话的自动统计被禁止”的解决方案

    现象: 第一次用PLSQL Developer连接数据库,若用sys用户登录并操作则正常,若用普通用户比如haishu登录并创建一个表则报错“动态执行表不可访问,本会话的自动统计被禁止.在执行菜单里你 ...

  5. 使用 PLSQL 提示动态执行表不可访问,本会话的自动统计被禁止

    使用PLSQL,第一次执行表的select操作的时候,提示"动态执行表不可访问,本会话的自动统计被禁止",如上图: 这种问题,一看就是当前连接用户没有对sys用户下的表v$sess ...

  6. PLSQL报错:"动态执行表不可访问,本会话的自动统计被禁止"

      PLSQL报错:"动态执行表不可访问,本会话的自动统计被禁止" CreationTime--2018年7月16日19点26分 Author:Marydon 1.情景展示 2.解 ...

  7. Oracle动态执行表不可访问解决方法

    在scott 用户下,执行查询语句是出现“Oracle动态执行表不可访问” 经查,是因为用户权限不够所致,修改scott用户权限语句如下: grant select on V_$session to ...

  8. PL/SQL 报错:动态执行表不可访问,本会话的自动统计被禁止。 在执行菜单里你可以禁止统计,或在v$session,v$sesstat 和vSstatname表里获得选择权限。

    现象: 第一次用PL/SQL Developer连接数据库,若用sys用户登录并操作则正常,若用普通用户比如haishu登录并创建一个表则报错“动态执行表不可访问,本会话的自动统计被禁止.在执行菜单里 ...

  9. Oracle动态执行表不可访问

    在scott 用户下,执行查询语句是出现"Oracle动态执行表不可访问" 经查,是因为用户权限不够所致,修改scott用户权限语句如下: grant select on V_$s ...

随机推荐

  1. POJ 1013:Counterfeit Dollar

    Counterfeit Dollar Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 42028   Accepted: 13 ...

  2. 18 12 2 数据库 sql 的增删改查

    ---恢复内容开始--- 1  开始进入MySQL 的安装  https://www.cnblogs.com/ayyl/p/5978418.html  膜拜大神的博客 2  默认安装的时候     m ...

  3. SpringCloud学习之手把手教你用IDEA搭建入门项目【番外篇】(一)

    之前的文章里,我曾经搭建了一个Springcloud项目,但是那个时候我对于SpringCloud架构的很多组件不甚清楚,只是通过查找资料然后动手稀里糊涂的把一个项目成功搭建起来了,其中有很多不合理和 ...

  4. 学生选课系统(Java语言期末前测试)

      测试具体要求: 2.系统要求与功能设计 2.1 页面要求 (1)能够在Tomcat服务器中正确部署,并通过浏览器查看: (2)网站页面整体风格统一: (3)首页(登录页)要求实现不同用户登录后,进 ...

  5. UVA 11732 链表+字典树

    因为字符集比较大,所以就不能用简单字典树,在字典树里面,用链表进行存储.这个倒是不难,练了下手 统计的时候还是有点难搞,因为要算所有的两两比较的次数之和,对分叉处进行计算,注意细节 #include ...

  6. Android studio个人常用快捷键

    个人常用重点: 电脑模式不一样需要加上fn键进行切换 Alt+回车/Enter  导入包,实现接口的方法.自动修正 Ctrl+X 删除行 Ctrl+D 复制行 Ctrl+Shift+Space 自动补 ...

  7. Ubuntu的man中文包安装

    apt-get install manpages-zh vi /etc/manpath.config :,$s#/usr/share/man#/usr/share/man/zh_CN#g 第一个命令: ...

  8. PHP时间戳常用转换

    //设置中国时区 date_default_timezone_set('PRC'); //今天的时间搓 $today_start = strtotime(date('Y-m-d',time()).' ...

  9. c语言中多维数组和指针的关系

    如图: 执行结果: 说明:由执行结果可知,三个输出的结果相等(可能在不同的平台执行结果不相同,但三个的结果是相等的),数组multi的地址与数组multi[0]的地址相同,都等于存储的第一个整数的地址 ...

  10. Python中的encode和decode

    原文地址:http://www.cnblogs.com/tingyugetc/p/5727383.html 1.Python3中对文本和二进制数据进行了比较清晰的区分,文本总是 unicode ,由  ...