您是否遇到过数据库查询变慢的情况?查询缓存是您加速数据库查询所需的技术,它可以通过使用不同的缓存方法来降低成本!想象一下,您构建了一个电商应用。它刚开始规模很小,但正在快速增长。现在,您拥有庞大的产品目录和数百万客户。
这对业务有利,但对技术来说却是一种挑战。您对主数据库(MongoDB/PostgreSQL)的查询开始变慢,即使您已经尝试过优化。即便能挤出一点额外的性能,也不足以满足您的客户。
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。数据使用 Prisma 存储在 Redis 和 MongoDB/PostgreSQL 中。您将在下方找到电商应用前端的截图
Dashboard
:显示带搜索功能的产品列表Shopping Cart
:将产品添加到购物车,然后使用“立即购买”按钮结账
以下是克隆本教程中使用的应用源代码的命令
git clone --branch v4.2.0 https://github.com/redis-developer/redis-microservices-ecommerce-solutions
在我们的示例应用中,产品服务发布了一个用于过滤产品的 API。以下是对该 API 的调用示例
// POST http://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": "http://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/PostgreSQL),根据产品 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,使用不同的策略/模式。有关微服务的更多资源,请查看以下链接
使用 Redis 的微服务
通用