全栈工程师修炼指南

设计数据持久层(下):案例介绍

2020-11-28  本文已影响0人  码农架构

搜索引擎

image.png

搜索数据的存储该怎么设计呢?

  1. 添加一个关键字表 KEYWORDS,并且,KEYWORD 列需要添加索引,因此这条关键字的记录可以被迅速找到:
image.png
  1. 一个关联关系表把 KEYWORDS 表和 ARTICLES 表结合起来,KEYWORD_ID 和 ARTICLE_ID 作为联合主键:
image.png
  1. 这其实是一个多对多的关系,即同一个关键字可以出现在多篇文章中,而一篇文章可以包含多个不同的关键字。

这个方法只解决了全表扫描和字符串 % 匹配查询造成的性能问题,并且,在数据量较大时,并没有解决数据量本身在单机模式下造成的性能问题。

Elasticsearch 将关键字使用哈希算法分散到多个不同的被称为“Shard”的虚拟节点,并且把它们部署到不同的机器节点上,且每一个 shard 具备指定数量的冗余副本(Replica),这些副本要求被放置到不同的物理机器节点上。通过这样的方式,我们就可以保证每台机器都只管理稳定且可控的数据量,并且保证了搜索服务数据的可用性。

image.png

例子:

对于每一个关键字,都可以配置指向文章和文章中位置的映射。比如有这样两篇文章:

那么,就有如下映射关系(下表仅用于表示在 Shard 中的数据映射,并非关系数据库表):

image.png

DOCUMENT 这一部分,每一行都可以存放若干个“文章 id : 文中关键字的位置”的组合。

地理信息系统

每个饭馆的位置可以简单考虑为经度和纬度组合的坐标.

image.png
  1. 把这样的地理信息,放到一张 LOCATIONS 表上,就会是这样:
image.png
  1. RESTAURANTS 表:
image.png
  1. 要查出范围内的饭馆,我们就可以写这样的 SQL:

<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">select * from LOCATIONS l, RESTAURANTS r where l.RESTAURANT_ID = r.RESTAURANT_ID and l.LONGITUDE >= 经度下界 and l.LONGITUDE <= 经度上界 and l.LATITUDE >= 纬度下界 and l.LATITUDE <= 纬度上界;</pre>

  1. 这个经度、纬度的上下界,是根据用户所在位置,以及地图缩放程度折算出来的。显然,这需要一个全表扫描,加一个笛卡尔积,复杂度偏高

优化

思路 1:给单一维度加索引

如果经纬度可以分开处理.

  1. 只考虑经度的话,给经度一列建立索引,所有饭馆按照从小到大的顺序排好。这样的话,当给定范围的时候,我们就可以快速找到经度范围内所有满足经度条件的饭馆。
  2. 从时间复杂度的角度来考虑,在不做额外优化的情况下,以在有序经度列上的二分查找为例,这个复杂度是 log(n)。
  3. 当再考虑纬度的时候,假如有 m 家满足经度条件的饭馆,接下去我们就只能挨个去检查这 m 家饭馆,找出它们中满足纬度条件的了,也就是说,总的时间复杂度是 m*log(n)。这种方法比较简单,在数据量不太大的情况下也没有太大问题,因此这已经是很好的方法了。但是,在某些场景下这个 m 还有可能比较大

思路 2:GeoHash

  1. GeoHash,它的大致思路就是降维。即把一个经度和纬度的二维坐标用一个一维的数来表示。具体实现上,有一种常见的办法就是把经度和纬度用一个长位数的数来表示,比如:

<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">经度:101010…… 纬度:100110……</pre>

  1. 接着把二者从左到右挨个位拼接,黑色字符来自经度,蓝色字符来自纬度:
image.png

在这种方式下,从结果的左边最高位开始,取任意长度截断所得到的前缀,可以用来匹配距离目标位置一定距离范围的所有饭馆。当用户选取的地图范围越大,前缀长度就越长,这个匹配精度也就越高,匹配到的饭馆数量也就越少。通过这种方式,区域不断用前缀的方式来细分,相当于给每个子区域一个标记号码

  1. 我们数据库表中的经度和纬度就可以合并为一列,再令这一列为主键,或者做索引,就能够进行单列的范围查询了。
image.png

SQL or NoSQL?

两个前提角度

数据分类:

  1. 商品元数据,即商品的描述、厂家等等信息;
  2. 媒体数据,比如图片和视频;
  3. 库存数据,包括在某个地点的库房某商品还有多少件库存;
  4. 交易信息,比如订单、支付、余额管理;
  5. 用户信息,涉及的功能包括登陆、注册和用户设置。

数据规模:

选择的思路

总结与思考

设计持久层,都有哪些需要考虑的方面呢?

代码层面:

系统层面:

公众号:码农架构
上一篇下一篇

猜你喜欢

热点阅读