Sequelize 在 Egg.js 上实践与优化
Sequelize 是一个 Node.js 中的 ORM(对象关系映射工具),它允许我们使用 JavaScript 代码来操作关系数据库。而 Egg.js 则是一款企业级的基于 Node.js 的开发框架。本文主要介绍如何在 Egg.js 中使用 Sequelize,并在此基础上进行一些优化。
一、Sequelize 在 Egg.js 中的基本使用
1.1 安装 Sequelize 和 Sequelize-cli
在项目根文件夹中执行以下命令:
npm install sequelize sequelize-cli mysql2
1.2 配置 Sequelize
在 config/config.default.js 中配置 sequelize。
// javascriptcn.com 代码示例 module.exports = { sequelize: { dialect: 'mysql', host: 'localhost', port: 3306, database: 'my_db', username: 'root', password: '123456', define: { paranoid: true, underscored: true, freezeTableName: true, charset: 'utf8mb4', dialectOptions: { collate: 'utf8mb4_general_ci' } } } }
其中,dialect 表示使用的数据库种类,host 表示数据库地址,port 表示数据库端口,database 表示数据库名称,username 和 password 表示登录数据库的用户名和密码。
1.3 使用 Sequelize
通过 sequelize.import() 方法就可以导入 Sequelize 的 Model。下面是一个简单的例子:
定义模型:
// javascriptcn.com 代码示例 // app/model/user.js module.exports = app => { const { STRING, INTEGER } = app.Sequelize; const User = app.model.define('user', { id: { type: INTEGER(10).UNSIGNED, primaryKey: true, autoIncrement: true, comment: '自增id' }, name: STRING(30), age: INTEGER, }, { timestamps: false, comment: "用户表", tableName: "users", indexes: [ { unique: true, fields: ["name"] }, ] }) return User; }
使用模型:
// javascriptcn.com 代码示例 // app/controller/user.js const Controller = require('egg').Controller; class UserController extends Controller { async create() { const { ctx } = this; const user = await ctx.model.User.create({ name: 'test', age: 18, }); ctx.body = { user, }; } async list() { const { ctx } = this; const users = await ctx.model.User.findAll(); ctx.body = { users, }; } } module.exports = UserController;
1.4 Sequelize-hook
sequelize-hook 是对 Sequelize 的增强,它提供了一组模型钩子方法(类似于 Mongoose 的中间件),可以在模型的生命周期中进行一些操作。比如为每个模型增加 createdAt 和 updatedAt 字段,简化 SQL 编写,如果模型中有 deleteAt 的话可以自动伪删除数据。
安装 sequelize-hook:
npm install sequelize-hook
使用 sequelize-hook:
// javascriptcn.com 代码示例 // app/model/user.js module.exports = app => { const { STRING, INTEGER } = app.Sequelize; const hooks = require('sequelize-hooks') const User = app.model.define('user', { id: { type: INTEGER(10).UNSIGNED, primaryKey: true, autoIncrement: true, comment: '自增id' }, name: STRING(30), age: INTEGER, }, { timestamps: false, comment: "用户表", tableName: "users", indexes: [ { unique: true, fields: ["name"] }, ], hooks: { beforeCreate: hooks.touchCreatedAt, beforeUpdate: hooks.touchUpdatedAt, beforeBulkCreate: hooks.touchBulkCreatedAt, beforeBulkUpdate: hooks.touchBulkUpdatedAt, beforeDestroy: hooks.allowDestroyIfUndeleted, beforeBulkDestroy: hooks.allowBulkDestroyIfUndeleted } }) return User; }
二、Sequelize 在 Egg.js 中的进阶使用
2.1 大批量插入数据
当需要插入大量数据时,我们通常使用 bulkCreate 方法,它可以一次插入多条数据,避免多次向数据库发起请求,提高性能。但是在 Egg.js 中,bulkCreate 的性能仍然不尽如人意,原因是它在数据量较大的情况下会卡死 Node.js 的事件循环,从而导致应用不能处理任何请求。解决方法是使用 Sequelize-bulk-create,它可以让 bulkCreate 异步执行,不会阻塞事件循环。
安装 Sequelize-bulk-create:
npm install mysql2 sequelize sequelize-bulk-create
使用 Sequelize-bulk-create:
// javascriptcn.com 代码示例 // app/model/user.js module.exports = app => { const { STRING, INTEGER } = app.Sequelize; const hooks = require('sequelize-hooks') const SequelizeBulkCreate = require('sequelize-bulk-create').create; const User = app.model.define('user', { id: { type: INTEGER(10).UNSIGNED, primaryKey: true, autoIncrement: true, comment: '自增id' }, name: STRING(30), age: INTEGER, }, { timestamps: false, comment: "用户表", tableName: "users", indexes: [ { unique: true, fields: ["name"] }, ], hooks: { beforeCreate: hooks.touchCreatedAt, beforeUpdate: hooks.touchUpdatedAt, beforeBulkCreate: hooks.touchBulkCreatedAt, beforeBulkUpdate: hooks.touchBulkUpdatedAt, beforeDestroy: hooks.allowDestroyIfUndeleted, beforeBulkDestroy: hooks.allowBulkDestroyIfUndeleted } }) // 插入大批量的数据 User.bulkCreateAsync = function (list, options = {}) { return SequelizeBulkCreate(this, list, options); } return User; }
调用方式:
// javascriptcn.com 代码示例 // app/controller/user.js const Controller = require('egg').Controller; class UserController extends Controller { async batch_create() { const { ctx } = this; const users = [{ name: 't1' },{ name: 't2' },{ name: 't3' }] await ctx.model.User.bulkCreateAsync(users) ctx.body = { users, }; } } module.exports = UserController;
2.2 Sequelize 配合 GraphQL
在 Egg.js 中配置 GraphQL 需要用到 graphql、graphql-tools、apollo-server-koa 这几个库。
安装依赖:
npm i graphql graphql-tools apollo-server-koa
编写 GraphQL 类型:
// javascriptcn.com 代码示例 // app/graphql/schema.js const { gql } = require('apollo-server-koa'); const typeDefs = gql` type User { id: ID! name: String! age: Int! } type Query { user(id: ID!): User! users: [User]! } `; module.exports = typeDefs;
定义 Query:
// javascriptcn.com 代码示例 // app/graphql/resolvers.js const resolvers = { Query: { async user(_, {id}, {ctx}) { const user = await ctx.model.User.findByPk(id) return user.toJSON() }, async users(_, __, {ctx}) { const users = await ctx.model.User.findAll() return users.map(u=>u.toJSON()) }, }, }; module.exports = resolvers;
配置 ApolloServer:
// javascriptcn.com 代码示例 // app/graphql/server.js const { ApolloServer } = require('apollo-server-koa'); const typeDefs = require('./schema'); const resolvers = require('./resolvers'); const server = new ApolloServer({ typeDefs, resolvers, introspection: true, playground: true, context: async ({ ctx }) => { return { ctx }; }, }); module.exports = server;
在 app.js 中使用 ApolloServer:
// app.js const server = require('./app/graphql/server'); server.applyMiddleware({ app });
三、Sequelize 在 Egg.js 中的性能优化
3.1 数据库连接池
数据库连接池可以优化数据库访问性能。Sequelize 默认提供了数据库连接池,只需在 config/config.default.js 中添加以下配置即可。
// javascriptcn.com 代码示例 module.exports = { sequelize: { xx: {}, pool: { max: 5, min: 0, idle: 10000 }, xx: {} }, };
其中,pool.max 表示连接池最大连接数,pool.min 表示连接池最小连接数,pool.idle 表示连接保持活跃状态的时间。
3.2 数据库读写分离
应用在高并发、大流量的情况下,数据库读写压力可能会非常大。在这种情况下,可以考虑对数据库进行读写分离优化。Sequelize 并不支持自动的读写分离,需要借助一些第三方库,比如 sequelize-read-support,它提供了透明的读写分离功能。读写分离使用 master-slave 架构,写操作只在 master 数据库中进行,读操作则可在 slave 数据库中进行。
安装依赖:
npm install mysql2 sequelize-read-support
使用方式:
// javascriptcn.com 代码示例 // config/config.default.js const ReadSupport = require('sequelize-read-support'); module.exports = { sequelize: { xx: {}, retry: { max: 3 }, database: 'sequelize_test', username: 'www', password: 'www', host: 'localhost', read: [ { host: 'localhost', username: 'www', password: 'www'}, { host: '192.168.1.3', username: 'www', password: 'www'}, { host: '192.168.1.2', username: 'www', password: 'www'} ], write: { host: 'localhost', username: 'www', password: 'www' }, dialect: 'mysql', define: { timestamps: false, freezeTableName: true }, logging: console.log, pool: { max: 10, min: 0, idle: 10000 }, operatorsAliases: Sequelize.Op, sync: { force: false }, retry: { max: 3 } } }; const support = new ReadSupport(app.model); const User = app.model.User; support.addModel(User, { debug: true });
通过将配置中的 read 选项值设置为一个数组,可以实现在多个数据库之间进行均衡的负载分配。write 选项值表示写入操作的数据库,可以分离出来以分担主库的压力。在 Egg.js 框架中使用 Sequelize 时,可以借助 sequelize-read-support 实现方便的读写分离。
总结
本文介绍了 Sequelize 在 Egg.js 中的基本用法以及一些优化技巧,包括大批量插入数据、响应 GraphQL 查询、数据库连接池和读写分离。在实际开发过程中,这些技巧能够大大提高应用的性能和可
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6548d7307d4982a6eb318c91