Skip to content

🗨️ 评论缓存设计

1. 业务场景

在高并发系统中,评论经常被查看与点赞,我们在展示评论列表的时候,按点赞数和评论时间综合排序,对于评论业务,应该如何设计缓存策略?


2.技术选型

评论是一种需要持久化的数据,主储存靠考虑使用 TiDB 等高性能关系数据库,通过合理的表设计来解决缓存大 key 的问题。

1️⃣ 评论主题表 Comment Topic

作用:评论的“桶”,即评论的整体信息。

sql
comment_topic

- topic_id # 视频 / 帖子 / 商品
- comment_count # 总评论数统计
- hot_count # 热度评分
- status # 状态等其它字段

为什么要单独一层?

  1. 评论总数、关闭评论、风控

  2. 快速判断:有没有评论

  3. 避免每次都扫评论表这是“评论的元数据层”

2️⃣ 评论索引表 Comment Index

存放评论的索引与树形结构,表示评论的回复、父子等关系

sql
comment_index

- topic_id # 主题id,根据业务决定,比如视频评论就是视频编号
- comment_id # 评论记录id
- parent_id # 父评论id,如果为 0 即为顶级评论
- order_key # 排序字段等
- root # 回复评论,如果为 0 即为非回复评论

不存 content,一条记录非常小,专门为:分页/排序/层级业务设计

3️⃣ 评论内容表 Comment Content
sql
comment_content

- comment_id # 评论id
- user_id # 用户id
- content # 文本内容
- extra # 图片 / 表情 / @

这一层的特点为 IO 重、字段多、更新少,天然适合缓存


3.总结

这套技术架构核心在表设计上,将评论内容与评论结构进行了解耦,在实际实现的时候,我们只缓存评论内容,可以考虑使用异步编排来进一步优化整体响应时间。


实例-租车网站

1. 主题表 (这里是车辆信息)

评论主题表为评论区的主题聚合信息,即评论对应的实体。

sql
-- 车辆信息表
CREATE TABLE `car`
(
    `id`               bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `brand_id`         bigint unsigned NOT NULL COMMENT '品牌ID',
    `name`             varchar(50)     NOT NULL COMMENT '车辆名',
    `number`           varchar(50)     NOT NULL COMMENT '车牌号',
    `min_rental_days`  int             NOT NULL COMMENT '最小租赁天数',
    `daily_rent`       decimal(10, 2)  NOT NULL COMMENT '日租金(人民币元)',

    `car_type`         varchar(50)              DEFAULT NULL COMMENT '车型',
    `power_type`       varchar(50)              DEFAULT NULL COMMENT '动力类型',
    `purchase_time`    date                     DEFAULT NULL COMMENT '车辆购买日期',
    `horsepower`       int                      DEFAULT NULL COMMENT '马力',
    `torque`           int                      DEFAULT NULL COMMENT '最大扭矩',
    `fuel_consumption` int                      DEFAULT NULL COMMENT '百公里油耗(L/100km)',
    `endurance`        int                      DEFAULT NULL COMMENT '理论续航km',
    `description`      varchar(1536)            DEFAULT NULL COMMENT '描述',
    `size`             varchar(50)              DEFAULT NULL COMMENT '尺寸,长×宽×高',
    `seat`             int                      DEFAULT NULL COMMENT '座位数',
    `weight`           int                      DEFAULT NULL COMMENT '车重(kg)',
    `volume`           int                      DEFAULT NULL COMMENT '储物容积(L)',
    `acceleration`     decimal(4, 1)            DEFAULT NULL COMMENT '百公里加速(s)',
    `images`           varchar(1536)            DEFAULT NULL COMMENT '图片url列表,逗号分隔,最多9张图片',

    `status`           tinyint         NOT NULL DEFAULT '0' COMMENT '车辆状态:0=正常,1=不可租',
    `hot_score`        int             NOT NULL DEFAULT '0' COMMENT '热度评分',
    `avg_score`        int             NOT NULL DEFAULT '0' COMMENT '车辆用户平均评分',

    `create_time`      datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`      datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted`          tinyint         NOT NULL DEFAULT '0' COMMENT '逻辑删除:0=正常,1=已删除',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_0900_ai_ci COMMENT ='车辆信息表';

2. 评论索引表

评论索引表只构建评论的结构索引,不储存实际的评论细节。

sql
-- 评论索引表
CREATE TABLE `comment_index`
(
    `id`                bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `user_id`           bigint unsigned NOT NULL COMMENT '对应用户ID',
    `car_id`            bigint unsigned NOT NULL COMMENT '对应车辆ID',
    `parent_comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '父级评论id,默认0即为顶级评论',
    `follow_comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '回复评论id,默认0即非回复评论',

    `create_time`       datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`       datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted`           tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除:0=正常,1=已删除',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_0900_ai_ci COMMENT ='评论索引表';

3. 评论详情表

评论详情再储存具体的评论数据,实现了评论结构和评论内容的解耦,这样的话,可以很好的防止在缓存过程中出现大 key 的问题。

sql
-- 评论详情表
CREATE TABLE `comment_detail`
(
    `id`                bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `index_id`          bigint unsigned NOT NULL COMMENT '对应索引ID',
    `content`           varchar(1024) NOT NULL COMMENT '评论内容',
    `like_count`        int unsigned NOT NULL DEFAULT '0' COMMENT '点赞数',
    `score`             int unsigned DEFAULT NULL COMMENT '近期订单评分',
    `extra_images`      JSON DEFAULT NULL COMMENT '评论图片URL列表(JSON数组)',
    `create_time`       datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`       datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted`           tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除:0=正常,1=已删除',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_0900_ai_ci COMMENT ='评论详情表';

4. 外部关联表

引入一个额外的业务背景:如果用户对于某个车辆,有完成且打分的订单,用户评论会额外显示最近一次的订单的打分。

具体的业务实现如下:

用户发送评论时,查询数据库,如果评论的车辆之前有订单评分分数,把最近的一次订单评分插入到评论详情的 score 字段中 (create_time desc,limit 1)。即,一个当时评分的快照。

sql
-- 订单表
CREATE TABLE `rental_order`
(
 `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
 `user_id` bigint unsigned NOT NULL COMMENT '对应用户ID',
 `car_id` bigint unsigned NOT NULL COMMENT '对应车辆ID',
 `trade_no` varchar(255) NOT NULL COMMENT '支付宝订单编号',
 `start_rental_time` date NOT NULL COMMENT '车辆起租日期',
 `end_rental_time` date NOT NULL COMMENT '车辆还车日期',
 `price` decimal(10, 2) NOT NULL COMMENT '订单总额(人民币元)',
 `address` varchar(255) NOT NULL COMMENT '取还车地址',
 `status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态:0=新建/待支付,1=已支付,2=租赁中,3=已完成,4=已取消 5=待退款 6=已退款',
 `score` int DEFAULT NULL COMMENT '订单评分0-10,计入车辆均分',
 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 `deleted` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除:0=正常,1=已删除',
 PRIMARY KEY (`id`)
) ENGINE = InnoDB
 AUTO_INCREMENT = 1
 DEFAULT CHARSET = utf8mb4
 COLLATE = utf8mb4_0900_ai_ci COMMENT ='订单信息表';