一直比较好奇像知乎、公众号等这类发布文章社区他们的数据是怎么存储的,而且在发布文章和检索的时候效率都特别高。

那他们是如何实现的呢?

其实他们的主要问答数据还是采用关系型数据库,比如Mysql或者Oracle等主流数据库来存储,同时结合了一些检索和缓存的组件或者技术,比如Elasticsearch和Redis。

那有的同学可能就会问了,一个回答上万字是都储存在一个字段里吗?

答:是,但也不完全是(哥们,你是薛定谔的“是”?)。

接下来我们就以知乎为例,拆解一下从文章发布到数据存储再到检索都经历了什么。

1.数据存储

用户发布的完整回答会作为一个整体,通过一个长文本LONGTEXT或者TEXT字段的形式存储在一张主表里,这样做的好处是通过事务保证了数据的一致性,并且在读取的时候逻辑简单,通过一个问题id就能拿到所有答案。

我们假设有一张 answers 表,其中会有一个 content 字段。这个字段存储了用户提交的完整的内容(可能是 Markdown 或其他格式)。

CREATE TABLE `answers` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `question_id` bigint(20) NOT NULL,
  `author_id` bigint(20) NOT NULL,
  `content` LONGTEXT COLLATE utf8mb4_unicode_ci NOT NULL, -- 这就是存上万字的地方
  `excerpt` varchar(500) DEFAULT NULL, -- 摘要,用于列表页展示
  `vote_count` int(11) DEFAULT '0',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_question_id` (`question_id`),
  KEY `idx_author_id` (`author_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

但是,数据存储到关系型数据库就会产生一些问题,比如频繁读取上万字符的答案时候会消耗大量IO和带宽,巨大的content字段会拖累查询速度。

那该如何优化呢?

2.存储优化

建立索引:

这个不用多说了吧。

读写分离:

主库用于写,从库用于读。

列表与详情分离:

我们会发现在访问知乎这类网站首页的时候,只会展示问题列表和答案的前几百个字,这就是为了提升效率,查询的时候不包含 content 字段,只查询 id, author_id, excerpt, vote_count等数据,excerpt比较重要,就是我们提到的用于快速在首页列表展示的。

当用户点击答案详情的时候,才会去查询这个完整答案,显然查询列表跟详情不是一个量级的。

垂直分表:

将content这种大字段单独放到一个表里,需要的时候直接查单独表,降低主表数据量提升插入和查询效率。

-- 主表(元数据,小字段,高频访问)
CREATE TABLE `answers` (
  `id` bigint(20) PRIMARY KEY,
  `question_id` bigint(20),
  `author_id` bigint(20),
  `excerpt` varchar(500),
  `vote_count` int(11),
  `content_id` bigint(20) -- 指向内容表的外键
);

-- 内容表(大文本,低频全量访问)
CREATE TABLE `answer_contents` (
  `id` bigint(20) PRIMARY KEY,
  `answer_id` bigint(20) UNIQUE,
  `content` LONGTEXT, -- 大文本单独存放
  `format` varchar(10) -- 格式标记,如 'md' 或 'html'
);

3.检索优化

像知乎和公众号这种社区,文章或者问题的数量都是上亿级别的,那该如何从海量的数据做检索呢?

Elasticsearch:

异步索引:当用户发布回答并且写入数据库之后,系统后台会通过一个消息队列比如Kafka发送一个异步消息,将问题和答案同步到Elasticsearch。

建立倒排索引:Elasticsearch收到数据之后会对文本进行分词,建立“关键词->文档id”的倒排索引。

缓存:

Redis:比如一些比较热门的回答和文章会被存储在Redis中,这样其他用户再访问的时候直接从缓存取就行了,减少其他组件压力。

CDN:像一些回答中的图片或者视频会在用户编辑答案的时候上传到资源服务器和CDN,最终CDN会通过匹配用户位置来分发资源。

当然了,以上这些方案对于这种体量的内容社区简直是冰山一角,像一些内容质量、内容推荐、算法排序的优化都是必须的,如果有大佬从事这方面的工作或者对这类问题感兴趣的同学欢迎评论区与大家分享~

文章持续更新,可以关注下方公众号或者微信搜一搜「 迷迭香编程 」学习交流|项目源码|效率工具|学习小组,第一时间阅读,获取更完整的链路资料。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐