zoukankan      html  css  js  c++  java
  • sequelize 应用hook 实现对分表的访问

    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原生代码很久不写了,凑合吧

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

  • 相关阅读:
    在linux中安装JAVA的环境和安卓的环境(1)
    如何安装Tomcat
    Android开发历程_2(实现简单的乘法计算)
    Android开发历程_1(从1个activity跳转到另一个activity)
    Java 征途:行者的地图
    android系统架构之虚拟机
    Android四大组件及生命周期
    GridView属性大全
    安卓中各种用到的监听器
    移动端控制台排查方法
  • 原文地址:https://www.cnblogs.com/zihunqingxin/p/10011710.html
Copyright © 2011-2022 走看看