mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-29 15:13:44 +00:00
Update 系统设计基础.md
补充短链系统和 Feed 流。
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
@@ -8,6 +8,8 @@
|
||||
* [五、安全性](#五安全性)
|
||||
* [六、秒杀系统](#六秒杀系统)
|
||||
* [七、海量数据处理](#七海量数据处理)
|
||||
* [八、短链系统](#八短链系统)
|
||||
* [九、Feed 流](#九feed-流)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
@@ -208,8 +210,82 @@ UPDATE goods SET stock = stock - 1 WHERE id = 1001 AND stock > 0;
|
||||
|
||||
如果数据的取值范围很小,例如 1 亿个 10000 到 20000 之间的整数,一共只有 10001 种取值,用长度为 10001 的数组统计每个值出现的次数,一次扫描就能完成排序,也就是计数排序。
|
||||
|
||||
## 八、短链系统
|
||||
|
||||
短链系统把很长的网址换成很短的网址,例如把 https://shop.example.com/item/1024?from=campaign 换成 https://s.example/6LAze,常用于短信和社交媒体。用户访问短链时,服务返回一个跳转,浏览器再去访问原来的长链接。
|
||||
|
||||
<div align="center"> <img src="pics/short-url.png" width="760px"/> </div><br>
|
||||
|
||||
### 短码的生成
|
||||
|
||||
常见的方法有三种:
|
||||
|
||||
- 哈希:对长链接计算哈希(例如 MurmurHash),再把结果转成 Base62。同一个长链接总是得到同一个短码,但不同的长链接也可能得到同一个短码,冲突时要加上后缀重新计算;
|
||||
- 随机:随机生成 6 位字符,由唯一索引检查是否重复,重复就重新生成。已经用掉的短码越多越容易重复,例如已有 10 亿个短链时,新生成的短码约有 1.8% 的概率重复;
|
||||
- 发号器:先取一个全局唯一的自增 ID,再转成 Base62。不会冲突,短码也最短。
|
||||
|
||||
Base62 指的是用 0~9、a~z、A~Z 共 62 个字符表示一个数。例如 ID 100000000 不断除以 62,依次得到余数 14、35、36、47、6,倒过来对应字符 6、L、A、z、e,短码就是 6LAze。6 位 Base62 能表示 62<sup>6</sup> ≈ 568 亿个短码,每天新增 1 亿个也能用一年半。
|
||||
|
||||
发号器通常用 [数据库号段](分布式.md#八分布式-id):每个实例一次领取 1000 个 ID 在内存中使用,用完再领。雪花算法生成的 ID 是 64 位的大整数,转成 Base62 也有 11 位,用在短链中太长。需要注意的是,自增 ID 得到的短码是连续的,100000001 对应 6LAzf,别人可以据此遍历出其他短链;对隐私敏感的链接可以改用随机的方法,或者加上有效期和访问权限。
|
||||
|
||||
### 跳转用 301 还是 302
|
||||
|
||||
301 表示永久跳转,响应默认可以被缓存,浏览器之后会直接跳到长链接;302 表示临时跳转,每次访问都会请求短链服务。在本机用 Edge 访问同一个短链两次:返回 301 时,第二次直接跳到了长链接,服务端只收到一次请求;返回 302 时,两次都请求了服务端。
|
||||
|
||||
需要统计点击量、允许修改目标地址或者设置有效期时用 302;只想减轻服务端的压力时可以用 301。
|
||||
|
||||
### 存储与缓存
|
||||
|
||||
```sql
|
||||
CREATE TABLE short_url (
|
||||
code VARCHAR(8) PRIMARY KEY,
|
||||
long_url VARCHAR(2048) NOT NULL,
|
||||
url_hash BINARY(16) NOT NULL, -- 长链接的 MD5,用于查找同一个长链接是否已经生成过
|
||||
expire_at DATETIME,
|
||||
KEY idx_url_hash (url_hash)
|
||||
);
|
||||
```
|
||||
|
||||
长链接最长可能有几千个字符,直接在 long_url 上建唯一索引会报错 Specified key was too long(InnoDB 索引最长 3072 字节),所以另存一列 16 字节的 MD5 建索引,查到后再比较完整的长链接。
|
||||
|
||||
短链读多写少,热门链接的访问又很集中,适合用 Redis 缓存短码到长链接的映射。不存在的短码会穿透到数据库,可以缓存空值或者使用布隆过滤器,见 [缓存穿透](缓存.md#缓存穿透)。点击统计不要在跳转时同步写数据库,而是把点击事件写入消息队列,异步汇总。
|
||||
|
||||
## 九、Feed 流
|
||||
|
||||
Feed 流指的是把关注的人发布的内容按时间倒序合并后展示,例如朋友圈和微博的首页。每个用户有一个发件箱,保存自己发的帖子;难点在于怎样把几百个人的发件箱合成一个首页。
|
||||
|
||||
<div align="center"> <img src="pics/feed-push-pull.png" width="760px"/> </div><br>
|
||||
|
||||
### 拉模式与推模式
|
||||
|
||||
- 拉模式(读扩散):打开首页时,读取所有关注者的发件箱,合并后取最新的一页。发帖只写一次,但关注的人越多读得越慢;
|
||||
- 推模式(写扩散):发帖时把帖子写入每个粉丝的收件箱,打开首页只需读自己的收件箱。读很快,但粉丝越多写得越多,而且每个粉丝都存一份帖子 ID。
|
||||
|
||||
在本机的 Redis 7.4 上用有序集合实验(分值为发布时间):一个帖子推给 10 万个粉丝,用流水线写入 10 万个收件箱共 532 ms,已有的收件箱每多一条约占 16 字节;读自己的收件箱一页只需 0.12 ms;拉模式从 500 个发件箱各取最新 20 条再合并,一次要 24.5 ms。按这个速度,一个有 1000 万粉丝的大 V 发一条帖子,单个节点要写入约 50 秒,多占用约 150 MB 内存。
|
||||
|
||||
所以实际的系统大多推拉结合:
|
||||
|
||||
- 粉丝少的普通用户使用推模式;
|
||||
- 粉丝很多的大 V 不推,粉丝打开首页时,从自己的收件箱和所关注的少数几个大 V 的发件箱中拉取,再合并;
|
||||
- 只推给最近活跃的粉丝,长期不登录的用户回来时再用拉模式重建收件箱;
|
||||
- 收件箱只保留最近的若干条,例如每次写入后执行 ZREMRANGEBYRANK inbox:1001 0 -1001,只留下分值最大的 1000 条,更早的内容到数据库中查。
|
||||
|
||||
### 分页
|
||||
|
||||
Feed 流的分页不能用偏移量。例如收件箱中有 p1~p30 共 30 条帖子,第 1 页取到 p30~p21;用户翻页之前又有 3 条新帖子进来,这时按偏移量取第 11~20 条,结果是 p23~p14,p23、p22、p21 重复出现了。
|
||||
|
||||
应该用上一页最后一条的分值作为游标,取比它更早的帖子:
|
||||
|
||||
```text
|
||||
> ZRANGE inbox:1001 (21 -inf BYSCORE REV LIMIT 0 10
|
||||
p20 p19 p18 ... p11
|
||||
```
|
||||
|
||||
左括号表示不包含 21 本身,这是 Redis 6.2 起的写法,相当于旧的 ZREVRANGEBYSCORE。分值相同的帖子可能被跳过,可以把分值设为帖子 ID 这类唯一且递增的值,例如雪花算法生成的 ID 本身就按时间递增,见 [分布式 ID](分布式.md#八分布式-id)。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Redis: Scripting with Lua](https://redis.io/docs/latest/develop/programmability/eval-intro/)
|
||||
- 大型网站技术架构:核心原理与案例分析
|
||||
- [Designing Data-Intensive Applications 中文版](https://github.com/vonng/ddia)
|
||||
- [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="540" viewBox="0 0 760 540">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">Feed 流:拉模式在读时合并,推模式在写时分发</text>
|
||||
<text x="24" y="66" class="b">拉模式(读扩散)</text>
|
||||
<text x="24" y="84" class="sub">张三关注了 A、B 和大 V,打开首页时现场合并</text>
|
||||
<rect x="24" y="100" width="108" height="70" rx="5" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="24" y="100" width="108" height="22" rx="5" fill="#7FB2D6" opacity="0.35"/>
|
||||
<text x="78" y="115" class="c b">A 的发件箱</text>
|
||||
<rect x="30" y="128" width="96" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="36" y="140" class="sub mono">a2 10:05</text>
|
||||
<rect x="30" y="148" width="96" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="36" y="160" class="sub mono">a1 09:00</text>
|
||||
<rect x="140" y="100" width="108" height="50" rx="5" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="140" y="100" width="108" height="22" rx="5" fill="#7FB2D6" opacity="0.35"/>
|
||||
<text x="194" y="115" class="c b">B 的发件箱</text>
|
||||
<rect x="146" y="128" width="96" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="152" y="140" class="sub mono">b1 10:20</text>
|
||||
<rect x="256" y="100" width="108" height="70" rx="5" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="256" y="100" width="108" height="22" rx="5" fill="#7FB2D6" opacity="0.35"/>
|
||||
<text x="310" y="115" class="c b">大 V 的发件箱</text>
|
||||
<rect x="262" y="128" width="96" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="268" y="140" class="sub mono">v3 10:30</text>
|
||||
<rect x="262" y="148" width="96" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="268" y="160" class="sub mono">v2 08:10</text>
|
||||
<circle cx="190" cy="229" r="9" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1.2"/>
|
||||
<path d="M173,260 q0,-19 17,-19 q17,0 17,19 z" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1.2"/>
|
||||
<text x="190" y="274" class="c sub b">张三刷新首页</text>
|
||||
<line x1="78" y1="172" x2="176" y2="214" class="arrow "/>
|
||||
<line x1="194" y1="172" x2="182" y2="214" class="arrow "/>
|
||||
<line x1="310" y1="172" x2="188" y2="214" class="arrow "/>
|
||||
<rect x="110" y="288" width="160" height="110" rx="5" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1.2"/>
|
||||
<rect x="110" y="288" width="160" height="22" rx="5" fill="#BFBEBA" opacity="0.35"/>
|
||||
<text x="190" y="303" class="c b">合并后按时间倒序</text>
|
||||
<rect x="116" y="316" width="148" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="122" y="328" class="sub mono">v3 10:30</text>
|
||||
<rect x="116" y="336" width="148" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="122" y="348" class="sub mono">b1 10:20</text>
|
||||
<rect x="116" y="356" width="148" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="122" y="368" class="sub mono">a2 10:05</text>
|
||||
<rect x="116" y="376" width="148" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="122" y="388" class="sub mono">a1 09:00</text>
|
||||
<text x="190" y="424" class="c sub note">关注的人越多,读一次首页越慢</text>
|
||||
<line x1="388" y1="56" x2="388" y2="500" class="frame"/>
|
||||
<text x="408" y="66" class="b">推模式(写扩散)</text>
|
||||
<text x="408" y="84" class="sub">A 发帖时,写入每个粉丝的收件箱</text>
|
||||
<circle cx="580" cy="105" r="9" fill="#C6ECE9" stroke="#7DBCB7" stroke-width="1.2"/>
|
||||
<path d="M563,136 q0,-19 17,-19 q17,0 17,19 z" fill="#C6ECE9" stroke="#7DBCB7" stroke-width="1.2"/>
|
||||
<text x="580" y="150" class="c sub b">A 发了 a3</text>
|
||||
<rect x="430" y="182" width="130" height="90" rx="5" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="430" y="182" width="130" height="22" rx="5" fill="#7FB2D6" opacity="0.35"/>
|
||||
<text x="495" y="197" class="c b">张三的收件箱</text>
|
||||
<rect x="436" y="210" width="118" height="16" rx="3" fill="#E6E3FA" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="442" y="222" class="sub mono">a3 10:40</text>
|
||||
<rect x="436" y="230" width="118" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="442" y="242" class="sub mono">v3 10:30</text>
|
||||
<rect x="436" y="250" width="118" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="442" y="262" class="sub mono">b1 10:20</text>
|
||||
<rect x="600" y="182" width="130" height="70" rx="5" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="600" y="182" width="130" height="22" rx="5" fill="#7FB2D6" opacity="0.35"/>
|
||||
<text x="665" y="197" class="c b">李四的收件箱</text>
|
||||
<rect x="606" y="210" width="118" height="16" rx="3" fill="#E6E3FA" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="612" y="222" class="sub mono">a3 10:40</text>
|
||||
<rect x="606" y="230" width="118" height="16" rx="3" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.6"/>
|
||||
<text x="612" y="242" class="sub mono">a2 10:05</text>
|
||||
<line x1="566" y1="156" x2="506" y2="180" class="arrow "/>
|
||||
<line x1="594" y1="156" x2="654" y2="180" class="arrow "/>
|
||||
<text x="408" y="300" class="sub">读首页只需读自己的收件箱:</text>
|
||||
<text x="408" y="318" class="sub mono">ZREVRANGE inbox:张三 0 19</text>
|
||||
<text x="408" y="350" class="sub note">问题:大 V 有 1000 万粉丝,发一条就要写 1000 万次</text>
|
||||
<text x="408" y="368" class="sub ok">解决:粉丝多的用户不推,粉丝打开首页时再拉取</text>
|
||||
<text x="408" y="386" class="sub ok">合并(推拉结合);只推给最近活跃的粉丝</text>
|
||||
<text x="408" y="420" class="b">本机 Redis 7.4 实测</text>
|
||||
<rect x="408" y="430" width="160" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="488" y="446" class="c sub" style="fill:#444">推:10 万个收件箱各写 1 条</text>
|
||||
<rect x="568" y="430" width="150" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="643" y="446" class="c sub" style="fill:#444">532 ms,内存 +10 MB</text>
|
||||
<rect x="408" y="454" width="160" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="488" y="470" class="c sub" style="fill:#444">推:读自己的收件箱一页</text>
|
||||
<rect x="568" y="454" width="150" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="643" y="470" class="c sub" style="fill:#444">0.12 ms</text>
|
||||
<rect x="408" y="478" width="160" height="24" rx="3" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1"/>
|
||||
<text x="488" y="494" class="c sub" style="fill:#444">拉:合并 500 个发件箱</text>
|
||||
<rect x="568" y="478" width="150" height="24" rx="3" fill="#FFFFFF" stroke="#BBBBBB" stroke-width="1"/>
|
||||
<text x="643" y="494" class="c sub" style="fill:#444">24.5 ms</text>
|
||||
<text x="24" y="526" class="sub">左右两边的帖子和时间是示意;表中的数字是用流水线在本机执行得到的,拉模式包括在客户端合并排序的时间。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
@@ -0,0 +1,100 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420">
|
||||
<style>
|
||||
text { font-family: "PingFang SC", "Microsoft YaHei", Helvetica, Arial, sans-serif; fill: #3F3F3F; font-size: 12px; }
|
||||
.title { font-weight: bold; font-size: 15px; fill: #333; text-anchor: middle; }
|
||||
.b { font-weight: bold; }
|
||||
.c { text-anchor: middle; }
|
||||
.e { text-anchor: end; }
|
||||
.sub { font-size: 11px; fill: #6E6E6E; }
|
||||
.note { fill: #C0605A; }
|
||||
.ok { fill: #6A9F68; }
|
||||
.mono { font-family: Menlo, Consolas, monospace; }
|
||||
.arrow { stroke: #555; stroke-width: 1.1; fill: none; marker-end: url(#a); }
|
||||
.red { stroke: #C0605A; marker-end: url(#r); }
|
||||
.green { stroke: #6A9F68; marker-end: url(#g); }
|
||||
.dash { stroke-dasharray: 5 4; }
|
||||
.line { stroke: #555; stroke-width: 1.1; fill: none; }
|
||||
.frame { fill: none; stroke: #999; stroke-width: 1; stroke-dasharray: 4 3; }
|
||||
.shadow { fill: #DADADA; }
|
||||
</style>
|
||||
<defs>
|
||||
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#555"/>
|
||||
</marker>
|
||||
<marker id="r" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#C0605A"/>
|
||||
</marker>
|
||||
<marker id="g" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M1,1.5 L9.5,5 L1,8.5 z" fill="#6A9F68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="#FFFFFF"/>
|
||||
|
||||
<text x="380" y="28" class="title">短链系统:生成时取 ID 转 Base62,访问时查缓存返回 302</text>
|
||||
<text x="24" y="66" class="b">生成</text>
|
||||
<circle cx="60" cy="93" r="9" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1.2"/>
|
||||
<path d="M43,124 q0,-19 17,-19 q17,0 17,19 z" fill="#E6E3FA" stroke="#B8B0DE" stroke-width="1.2"/>
|
||||
<text x="60" y="140" class="c sub b">运营人员</text>
|
||||
<rect x="247" y="88" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="94.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="94.5" x2="286" y2="94.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<rect x="247" y="104" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="110.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="110.5" x2="286" y2="110.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<rect x="247" y="120" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="126.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="126.5" x2="286" y2="126.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<text x="270" y="148" class="c sub b">短链服务</text>
|
||||
<line x1="86" y1="106" x2="240" y2="106" class="arrow "/>
|
||||
<text x="163" y="98" class="c sub mono">POST /shorten</text>
|
||||
<rect x="360" y="70" width="196" height="70" rx="5" fill="#EAE9E7" stroke="#BFBEBA" stroke-width="1.2"/>
|
||||
<rect x="360" y="70" width="196" height="22" rx="5" fill="#BFBEBA" opacity="0.35"/>
|
||||
<text x="458" y="85" class="c b">发号器</text>
|
||||
<rect x="366" y="98" width="184" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="372" y="110" class="sub mono">号段 100000000~100000999</text>
|
||||
<rect x="366" y="118" width="184" height="16" rx="3" fill="#FFFFFF" stroke="#BFBEBA" stroke-width="0.6"/>
|
||||
<text x="372" y="130" class="sub mono">本次取到 100000000</text>
|
||||
<line x1="296" y1="100" x2="358" y2="96" class="arrow "/>
|
||||
<text x="458" y="162" class="c sub mono" style="fill:#444">100000000 转 Base62 → 6LAze</text>
|
||||
<path d="M635,86 v44 a25,6 0 0 0 50,0 v-44" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<ellipse cx="660" cy="86" rx="25" ry="6" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<path d="M635,108 a25,6 0 0 0 50,0" fill="none" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<text x="660" y="152" class="c sub b">MySQL</text>
|
||||
<line x1="558" y1="108" x2="630" y2="108" class="arrow "/>
|
||||
<text x="736" y="172" class="e sub mono">6LAze →</text>
|
||||
<text x="736" y="188" class="e sub mono">https://shop.example.com/item/1024</text>
|
||||
<line x1="24" y1="210" x2="736" y2="210" class="frame"/>
|
||||
<text x="24" y="238" class="b">访问</text>
|
||||
<rect x="40" y="256" width="40" height="27.2" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<rect x="44" y="260" width="32" height="19.2" rx="1.5" fill="#FFFFFF" stroke="#7FB2D6" stroke-width="0.8"/>
|
||||
<path d="M56,283.2 v5 M64,283.2 v5" stroke="#7FB2D6" stroke-width="1.4"/>
|
||||
<rect x="48" y="288.2" width="24" height="3.5" rx="1.5" fill="#7FB2D6"/>
|
||||
<text x="60" y="302" class="c sub b">浏览器</text>
|
||||
<rect x="247" y="256" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="262.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="262.5" x2="286" y2="262.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<rect x="247" y="272" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="278.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="278.5" x2="286" y2="278.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<rect x="247" y="288" width="46" height="13" rx="3" fill="#C6E4FB" stroke="#7FB2D6" stroke-width="1.2"/>
|
||||
<circle cx="254" cy="294.5" r="2" fill="#7FB2D6"/>
|
||||
<line x1="261" y1="294.5" x2="286" y2="294.5" stroke="#7FB2D6" stroke-width="1"/>
|
||||
<text x="270" y="316" class="c sub b">短链服务</text>
|
||||
<line x1="86" y1="268" x2="240" y2="268" class="arrow "/>
|
||||
<text x="163" y="260" class="c sub mono">① GET s.example/6LAze</text>
|
||||
<path d="M445,256 v44 a25,6 0 0 0 50,0 v-44" fill="#C6ECE9" stroke="#7DBCB7" stroke-width="1.2"/>
|
||||
<ellipse cx="470" cy="256" rx="25" ry="6" fill="#C6ECE9" stroke="#7DBCB7" stroke-width="1.2"/>
|
||||
<path d="M445,278 a25,6 0 0 0 50,0" fill="none" stroke="#7DBCB7" stroke-width="1"/>
|
||||
<text x="470" y="322" class="c sub b">Redis</text>
|
||||
<line x1="296" y1="270" x2="440" y2="270" class="arrow "/>
|
||||
<text x="368" y="262" class="c sub">② 查 6LAze</text>
|
||||
<line x1="440" y1="292" x2="298" y2="292" class="arrow "/>
|
||||
<text x="368" y="306" class="c sub">命中:返回长链接</text>
|
||||
<text x="640" y="266" class="c sub">未命中时查 MySQL</text>
|
||||
<text x="640" y="282" class="c sub">再写入缓存</text>
|
||||
<line x1="500" y1="276" x2="588" y2="276" class="arrow dash"/>
|
||||
<path d="M240,300 C190,330 130,330 88,300" class="arrow green"/>
|
||||
<text x="164" y="346" class="c sub mono ok">③ 302 Found</text>
|
||||
<text x="164" y="362" class="c sub mono ok">Location: https://shop.example.com/...</text>
|
||||
<text x="24" y="404" class="sub">浏览器收到 302 后再访问长链接。用 301 时浏览器会缓存跳转,之后的点击不再经过短链服务,无法统计点击量。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
Reference in New Issue
Block a user