你是否曾遇到过数据库查询速度变慢的情况?查询缓存是您需要的技术,它可以利用不同的缓存方法来加速数据库查询,同时降低成本!假设您构建了一个电子商务应用程序。它起初很小,但正在快速增长。现在,您拥有庞大的产品目录和数百万用户。
这对业务来说是好事,但对技术来说却是一件难事。您对主数据库(MongoDB/ Postgressql)的查询开始变慢,即使您已经尝试过优化它们。即使您能稍微提高一些性能,也不足以满足您的客户需求。
Redis 是一个内存数据存储,以缓存而闻名。Redis 允许您减少对主数据库的负载,同时加速数据库读取。
在任何电子商务应用程序中,都有一种特定类型的查询被最常请求。如果您猜到是产品搜索查询,那么您就对了!
为了改进电子商务应用程序中的产品搜索,您可以实现以下缓存模式之一
如果您使用Redis Cloud,则缓存旁路会更容易,因为它支持 JSON 和搜索。您还可以获得其他功能,例如实时性能、高可扩展性、弹性和容错性。您还可以调用高可用性功能,例如 Active-Active 地理冗余。
本教程重点介绍缓存旁路模式。这种设计模式的目标是为更好的读取操作设置最佳缓存(按需加载)。在缓存方面,您可能熟悉“缓存未命中”,即您在缓存中找不到数据,“缓存命中”,即您可以在缓存中找到数据。让我们看看缓存旁路模式如何在 Redis 中针对“缓存未命中”和“缓存命中”进行工作。
此图说明了在缓存旁路模式中发生“缓存未命中”时执行的步骤。为了理解其工作原理,请考虑以下过程
现在您已经了解了“缓存未命中”的运行方式,让我们来了解一下“缓存命中”。以下是同一张图,但“缓存命中”步骤用绿色突出显示。
缓存旁路模式在以下情况下很有用
如果您使用Redis Cloud和使用 JDBC 驱动程序的数据库,您可以利用Redis 智能缓存,它可以让您在不更改代码的情况下为应用程序添加缓存。 点击此处了解更多信息!
本教程其余部分中讨论的电子商务微服务应用程序使用以下架构
products service
:处理从数据库查询产品并将其返回给前端orders service
:处理订单验证和创建order history service
:处理查询客户的订单历史记录payments service
:处理订单的付款处理digital identity service
:处理数字身份存储和身份评分计算api gateway
:将服务统一到单个端点mongodb/ postgresql
:充当主数据库,存储订单、订单历史记录、产品等。redis
:充当流处理器和缓存数据库您不需要在演示应用程序中使用 MongoDB/ Postgresql 作为主数据库;您也可以使用其他prisma 支持的数据库。这只是一个示例。
这个电子商务微服务应用程序包含一个前端,使用 Next.js 和 TailwindCSS构建。应用程序后端使用 Node.js。数据存储在 Redis 和 MongoDB/Postgressql 中,并使用 Prisma。您将在下面找到电子商务应用程序前端的截图
Dashboard
: 显示带有搜索功能的产品列表Shopping Cart
: 将产品添加到购物车,然后使用“立即购买”按钮结账
以下是用于克隆本教程中使用的应用程序源代码的命令
git clone --branch v4.2.0 https://github.com/redis-developer/redis-microservices-ecommerce-solutions
在我们的示例应用程序中,产品服务发布了一个用于过滤产品的 API。以下是 API 调用的样子
// POST https://localhost:3000/products/getProductsByFilter
{
"productDisplayName": "puma"
}
{
"data": [
{
"productId": "11000",
"price": 3995,
"productDisplayName": "Puma Men Slick 3HD Yellow Black Watches",
"variantName": "Slick 3HD Yellow",
"brandName": "Puma",
"ageGroup": "Adults-Men",
"gender": "Men",
"displayCategories": "Accessories",
"masterCategory_typeName": "Accessories",
"subCategory_typeName": "Watches",
"styleImages_default_imageURL": "https://host.docker.internal:8080/images/11000.jpg",
"productDescriptors_description_value": "<p style=\"text-align: justify;\">Stylish and comfortable, ...",
"createdOn": "2023-07-13T14:07:38.020Z",
"createdBy": "ADMIN",
"lastUpdatedOn": "2023-07-13T14:07:38.020Z",
"lastUpdatedBy": null,
"statusCode": 1
}
//...
],
"error": null,
"isFromCache": false
}
{
"data": [
//...same data as above
],
"error": null,
"isFromCache": true // now the data comes from the cache rather DB
}
以下代码显示了用于在主数据库中搜索产品的函数
async function getProductsByFilter(productFilter: Product) {
const prisma = getPrismaClient();
const whereQuery: Prisma.ProductWhereInput = {
statusCode: DB_ROW_STATUS.ACTIVE,
};
if (productFilter && productFilter.productDisplayName) {
whereQuery.productDisplayName = {
contains: productFilter.productDisplayName,
mode: 'insensitive',
};
}
const products: Product[] = await prisma.product.findMany({
where: whereQuery,
});
return products;
}
您只需调用主数据库 (MongoDB/Postgressql) 即可根据产品的 displayName
属性上的过滤器查找产品。您可以设置多个列以实现更好的模糊搜索,但为了本教程的目的,我们进行了简化。
直接使用主数据库而不使用 Redis 可以工作一段时间,但最终会变慢。这就是您可能使用 Redis 来加速的原因。旁路缓存模式可以帮助您平衡性能和成本。
旁路缓存的基本决策树如下所示。
当前端请求产品时
以下是用于实现决策树的代码
const getHashKey = (_filter: Document) => {
let retKey = '';
if (_filter) {
const text = JSON.stringify(_filter);
retKey = crypto.createHash('sha256').update(text).digest('hex');
}
return 'CACHE_ASIDE_' + retKey;
};
router.post(API.GET_PRODUCTS_BY_FILTER, async (req: Request, res: Response) => {
const body = req.body;
// using node-redis
const redis = getNodeRedisClient();
//get data from redis
const hashKey = getHashKey(req.body);
const cachedData = await redis.get(hashKey);
const docArr = cachedData ? JSON.parse(cachedData) : [];
if (docArr && docArr.length) {
result.data = docArr;
result.isFromCache = true;
} else {
// get data from primary database
const dbData = await getProductsByFilter(body); //method shown earlier
if (body && body.productDisplayName && dbData.length) {
// set data in redis (no need to wait)
redis.set(hashKey, JSON.stringify(dbData), {
EX: 60, // cache expiration in seconds
});
}
result.data = dbData;
}
res.send(result);
});
您需要决定什么过期时间或生存时间 (TTL) 最适合您的特定用例。
现在您已经了解了如何使用 Redis 和最常见的缓存模式之一(旁路缓存)进行缓存。您可以根据需要逐步采用 Redis 和不同的策略/模式。有关微服务主题的更多资源,请查看以下链接