GraphQL 是一种旨在改善 API 开发人员生产效率的查询语言,其核心思想是让客户端决定需要什么数据,而不需要由服务器端决定。但是,在构建 GraphQL 服务器时,如何保证其性能和可伸缩性仍然是一个挑战。本文将介绍一些 GraphQL 服务器的性能优化技术,以便开发人员可以更好地构建高性能的 GraphQL 服务器。
1. 数据库索引
在构建 GraphQL 服务器时,图形查询可能会涉及多个数据库表。这意味着服务器必须执行多个数据库查询来组装图形查询所需的数据。为了优化这个过程,可以使用数据库索引来优化查询性能。将索引添加到查询经常使用的列或复合列可以大大减少查询时间。
例如,在一个电子商务网站中,你可能需要执行以下 GraphQL 查询:
// www.javascriptcn.com code example
{
product(id: "123") {
id
name
category {
id
name
}
}
}在这个查询中,服务器需要执行两个查询:一个是根据 product 表中的 id 列查询产品,另一个是根据 product 表中的 category_id 列查询类别。如果我们在 product 表中为 id 和 category_id 列创建索引,则可以加快查询速度。
2. 批处理
GraphQL 查询可以包含多个子查询,例如:
// www.javascriptcn.com code example
{
product(id: "123") {
id
name
reviews {
id
text
}
}
}在这个查询中,我们需要获取产品信息以及该产品的评论信息。为了避免执行多个查询,我们可以使用批处理来一次性获取所有需要的数据。
例如,我们可以使用 DataLoader 库来批量获取评论数据,而不是为每个产品执行单独的评论查询:
// www.javascriptcn.com code example
const reviewsLoader = new DataLoader((productIds) => {
return Review.findAll({ where: { productId: productIds } });
});
{
Product: {
reviews(product, _, context) {
return context.loaders.reviewsLoader.load(product.id);
}
}
}在这个示例中,我们使用 Sequelize ORM 查询评论数据,并使用 DataLoader 来缓存和批量处理查询。这将大大减少与数据库的通信次数,从而提高查询性能。
3. 缓存
对于查询结果变化不频繁的数据,可以使用缓存来避免每次请求都要重新查询数据库。
例如,在一个博客网站中,我们可能需要执行以下查询:
{
posts {
id
title
content
}
}如果我们每次请求都要查询数据库以获取所有文章,这将会消耗大量的时间和资源。为了避免这种情况,我们可以使用缓存来存储查询结果。
// www.javascriptcn.com code example
const cache = new Map();
{
Query: {
async posts(_, __, { cache }) {
if (cache.has("posts")) {
return cache.get("posts");
}
const posts = await Post.findAll();
cache.set("posts", posts);
return posts;
}
}
}在这个示例中,我们使用 Map 对象来存储查询结果,并在下一次查询时使用缓存。这将会减少对数据库的请求次数,从而提高查询性能。
4. 延迟加载
对于大型查询,可能需要从多个不同的数据库表或服务中获取数据。为了避免每次查询都要获取所有数据,我们可以使用延迟加载来推迟加载不必要的数据。
例如,在一个电子商务网站中,我们可能需要执行以下查询:
// www.javascriptcn.com code example
{
product(id: "123") {
id
name
reviews(first: 10) {
id
text
user {
id
name
email
}
}
}
}在这个查询中,我们需要获取产品、评论以及评论用户的数据。为了避免从所有评论中获取用户数据,我们可以使用延迟加载从评论中获取所需的用户信息。
// www.javascriptcn.com code example
{
Query: {
async product(_, { id }) {
const product = await Product.findByPk(id);
return {
...product.toJSON(),
reviews: () => Review.findAll({ where: { productId: id } }),
};
}
},
Review: {
async user(review) {
return User.findByPk(review.userId);
}
}
}在这个示例中,我们使用延迟加载从评论中获取用户信息。这将能够减少不必要的数据库查询,从而提高性能。
总结
构建高性能的 GraphQL 服务器需要开发人员掌握一些性能优化技术。数据库索引、批处理、缓存和延迟加载等技术可以大大提高 GraphQL 服务器的性能和可伸缩性。希望本文能够帮助你更好地构建高性能的 GraphQL 服务器。
代码示例:
// www.javascriptcn.com code example
// Sequelize ORM 配置
const sequelize = new Sequelize({
dialect: "postgres",
host: process.env.DB_HOST,
port: process.env.DB_PORT,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
logging: false,
});
// Sequelize ORM 数据库模型
class Product extends Sequelize.Model {}
Product.init(
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
},
},
{
sequelize,
modelName: "product",
}
);
class Category extends Sequelize.Model {}
Category.init(
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "category",
}
);
class Review extends Sequelize.Model {}
Review.init(
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
text: {
type: Sequelize.TEXT,
allowNull: false,
},
productId: {
type: Sequelize.INTEGER,
allowNull: false,
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
},
},
{
sequelize,
modelName: "review",
}
);
class User extends Sequelize.Model {}
User.init(
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
email: {
type: Sequelize.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "user",
}
);
Review.belongsTo(User, { foreignKey: "userId" });
User.hasMany(Review, { foreignKey: "userId" });
Product.belongsTo(Category, { foreignKey: "categoryId" });
Category.hasMany(Product, { foreignKey: "categoryId" });
// GraphQL 程序入口
const { ApolloServer } = require("apollo-server");
const { Op } = Sequelize;
const app = new ApolloServer({
typeDefs: `
type Query {
product(id: ID!): Product
products(categoryId: ID): [Product]
}
type Product {
id: ID!
name: String
category: Category
reviews(first: Int): [Review]
}
type Category {
id: ID!
name: String
products: [Product]
}
type Review {
id: ID!
text: String
user: User
}
type User {
id: ID!
name: String
email: String
}
`,
resolvers: {
Query: {
async product(_, { id }) {
const product = await Product.findByPk(id);
return {
...product.toJSON(),
category: () => Category.findByPk(product.categoryId),
reviews: (_, { first = 10 }) =>
Review.findAll({
where: { productId: product.id },
limit: first,
}),
};
},
async products(_, { categoryId }) {
const products = await Product.findAll({
where: categoryId ? { categoryId } : {},
});
return products.map((product) => ({
...product.toJSON(),
category: () => Category.findByPk(product.categoryId),
}));
},
},
Product: {
category(product) {
return Category.findByPk(product.categoryId);
},
},
Category: {
products(category) {
return Product.findAll({ where: { categoryId: category.id } });
},
},
Review: {
user(review) {
return User.findByPk(review.userId);
},
},
},
context: () => ({
Sequelize,
Op,
db: sequelize,
}),
});
// 启动 GraphQL 服务器
app.listen().then(({ url }) => {
console.log(`GraphQL server running at ${url}`);
});Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/6494e27d48841e9894233a9b