字節(jié)二面:100Wqps短鏈系統(tǒng),如何設(shè)計(jì)?
點(diǎn)擊關(guān)注公眾號,Java干貨
及時送達(dá)
??
來源:技術(shù)自由圈
- 1、短URL系統(tǒng)的背景
-
2、短URL系統(tǒng)的原理
- 六十二進(jìn)制表示法
- 128進(jìn)制表示法
-
3、短 URL 系統(tǒng)的功能分析
- 系統(tǒng)核心實(shí)現(xiàn),包含三個大的功能
- 發(fā)號與存儲模塊
- 映射模塊
-
4、發(fā)號器的高并發(fā)架構(gòu)
- 方案1:使用地址的hash 編碼作為ID
- 方案2:數(shù)據(jù)庫自增長ID
- 方案3:分布式、高性能的中間件生成ID
- 方案4:UUID、GUID生成ID
- 方式5:snowflake算法(雪花算法)生成ID
- 高并發(fā)ID的技術(shù)選型
- 5、數(shù)據(jù)存儲的高并發(fā)架構(gòu)
-
6、二義性檢查的高并發(fā)架構(gòu)
- 每次進(jìn)行二義性檢查,走redis 布隆過濾器。
- 規(guī)則是:存在不一定存在,說不存在一定不存在
- 那么對于 surl,處理的方案是:
- 可以使用緩存架構(gòu),甚至多級緩存架構(gòu)
-
7、映射模塊(/轉(zhuǎn)換模塊)高并發(fā)架構(gòu)
- 簡單的緩存方案
- 補(bǔ)充服務(wù)間的重定向301 和 302 的不同
- 8、架構(gòu)的魅力
圖片這段時間,在整理面試專欄時看到這么一個字節(jié)跳動的二面真題:100Wqps短鏈系統(tǒng),怎么設(shè)計(jì)?
這道題,看上去業(yè)務(wù)簡單,其實(shí),覆蓋的知識點(diǎn)非常多:
- 高并發(fā)、高性能分布式 ID
- Redis Bloom Filter 高并發(fā)、低內(nèi)存損耗的 過濾組件知識
- 分庫、分表海量數(shù)據(jù)存儲
- 多級緩存的知識
- HTTP傳輸知識
- 二進(jìn)制、十六進(jìn)制、六十二進(jìn)制知識
總體來說,高并發(fā)、高性能系統(tǒng)的核心領(lǐng)域,都覆蓋了。所以,陳某分析下來,得到一個結(jié)論:是一個超級好的問題。
1、短URL系統(tǒng)的背景
短網(wǎng)址替代長URL,在互聯(lián)網(wǎng)網(wǎng)上傳播和引用。
例如QQ微博的url.cn,新郎的sinaurl.cn等。
在QQ、微博上發(fā)布網(wǎng)址的時候,會自動判別網(wǎng)址,并將其轉(zhuǎn)換,例如:http://url.cn/2hytQx
為什么要這樣做的,無外乎幾點(diǎn):
-
縮短地址長度,留足更多空間的給 有意義的內(nèi)容
URL是沒有意義的,有的原始URL很長,占用有效的屏幕空間。
微博限制字?jǐn)?shù)為140字一條,那么如果這個連接非常的長,以至于將近要占用我們內(nèi)容的一半篇幅,這肯定是不能被允許的,鏈接變短,對于有長度限制的平臺發(fā)文,可編輯的文字就變多了, 所以短網(wǎng)址應(yīng)運(yùn)而生了。
-
可以很好的對原始URL內(nèi)容管控。
有一部分網(wǎng)址可以會涵蓋XX,暴力,廣告等信息,這樣我們可以通過用戶的舉報,完全管理這個連接將不出現(xiàn)在我們的應(yīng)用中,應(yīng)為同樣的URL通過加密算法之后,得到的地址是一樣的。
-
可以很好的對原始URL進(jìn)行行為分析
我們可以對一系列的網(wǎng)址進(jìn)行流量,點(diǎn)擊等統(tǒng)計(jì),挖掘出大多數(shù)用戶的關(guān)注點(diǎn),這樣有利于我們對項(xiàng)目的后續(xù)工作更好的作出決策。
-
短網(wǎng)址和短ID相當(dāng)于間接提高了帶寬的利用率、節(jié)約成本
-
鏈接太長在有些平臺上無法自動識別為超鏈接
-
短鏈接更加簡潔好看且安全,不暴露訪問參數(shù)。而且,能規(guī)避關(guān)鍵詞、域名屏蔽等手段
2、短URL系統(tǒng)的原理
短URL系統(tǒng)的核心:將長的 URL 轉(zhuǎn)化成短的 URL 。
客戶端在訪問系統(tǒng)時,短URL的工作流程如下:
- 先使用短地址A訪問 ?短鏈Java 服務(wù)
- 短鏈Java 服務(wù) 進(jìn)行 地址轉(zhuǎn)換和映射,將 ?短URL系統(tǒng)映射到對應(yīng)的長地址URL
- 短鏈Java 服務(wù) ?返回302 重定向 給客戶端
- 然后客戶端再重定向到原始服務(wù)
如下圖所示:

那么,原始URL如何變短呢?簡單來說, 可以將原始的地址,使用編號進(jìn)行替代
編號如何進(jìn)一步變短呢?可以使用更大的進(jìn)制來表示
六十二進(jìn)制表示法
顧名思義短網(wǎng)址就是非常短的網(wǎng)址,比如http://xxx.cn/EYyCO9T,其中核心的部分 EYyCO9T ?只有7位長度。
其實(shí)這里的7位長度是使用62進(jìn)制來表示的,就是常用的0-9、a-z、A-Z,也就是10個數(shù)字+26個小寫+26個大寫=62位。
那么7位長度62進(jìn)制可以表示多大范圍呢?
62^7?=?3,521,614,606,208?(合計(jì)3.5萬億),
說明:
10進(jìn)制?最大只能生成?10?^?6?-?1?=999999個
16進(jìn)制?最大只能生成?16?^?6?-?1?=16777215個
16進(jìn)制里面已經(jīng)包含了?A?B?C?D?E?F?這幾個字母
62進(jìn)制?最大竟能生成 62 ^ 6 - 1 =56800235583個?基本上夠了。
A-Z?a-z?0-9?剛好等于62位
注意:
int(4個字節(jié))?,存儲的范圍是-21億到21億
long(8個字節(jié)),存儲的范圍是-900萬萬億?到?900萬萬億
至于短網(wǎng)址的長度,可以根據(jù)自己需要來調(diào)整,如果需要更多,可以增加位數(shù),
即使6位長度62^6也能達(dá)到568億的范圍,
這樣的話只要算法得當(dāng),可以覆蓋很大的數(shù)據(jù)范圍。
在編碼的過程中,可以按照自己的需求來調(diào)整62進(jìn)制各位代表的含義。
一個典型的場景是, 在編碼的過程中,如果不想讓人明確知道轉(zhuǎn)換前是什么,可以進(jìn)行弱加密,
比如A站點(diǎn)將字母c表示32、B站點(diǎn)將字母c表示60,就相當(dāng)于密碼本了。
128進(jìn)制表示法
標(biāo)準(zhǔn)ASCII 碼也叫基礎(chǔ)ASCII碼,使用7 位二進(jìn)制數(shù)(剩下的1位二進(jìn)制為0),包含128個字符,
看到這里你或許會說,使用128進(jìn)制(如果有的話)豈不是網(wǎng)址更短,
是的,
7 位二進(jìn)制數(shù)(剩下的1位二進(jìn)制為0)表示所有的大寫和小寫字母,數(shù)字0 到9、標(biāo)點(diǎn)符號,以及在美式英語中使用的特殊控制字符 [1] ?。
注意:
128個進(jìn)制就可能會出現(xiàn)大量的不常用字符
比如?#?%?&?*?這些,
這樣的話,對于短鏈接而言,通用性和記憶性就變差了,
所以,62進(jìn)制是個權(quán)衡折中。
3、短 URL 系統(tǒng)的功能分析
假設(shè)短地址長度為8位,62的8次方足夠一般系統(tǒng)使用了
系統(tǒng)核心實(shí)現(xiàn),包含三個大的功能
- 發(fā)號
- 存儲
- 映射
可以分為兩個模塊:發(fā)號與存儲模塊、映射模塊
發(fā)號與存儲模塊
- 發(fā)號:使用發(fā)號器發(fā)號 , 為每個長地址分配一個號碼ID,并且需要防止地址二義,也就是防止同一個長址多次請求得到的短址不一樣
- 存儲:將號碼與長地址存放在DB中,將號碼轉(zhuǎn)化成62進(jìn)制,用于表示最終的短地址,并返回給用戶
映射模塊
用戶使用62進(jìn)制的短地址請求服務(wù) ,
- 轉(zhuǎn)換:將62進(jìn)制的數(shù)轉(zhuǎn)化成10進(jìn)制,因?yàn)樵蹅兿到y(tǒng)內(nèi)部是long 類型的10進(jìn)制的數(shù)字ID
- 映射:在DB中尋找對應(yīng)的長地址
- 通過302重定向,將用戶請求重定向到對應(yīng)的地址上
4、發(fā)號器的高并發(fā)架構(gòu)
回顧一下發(fā)號器的功能:
- 為每個長地址分配一個號碼ID
- 并且需要防止地址歧義
以下對目前流行的分布式ID方案做簡單介紹
方案1:使用地址的hash 編碼作為ID
可以通過 原始Url的 hash編碼,得到一個 整數(shù),作為 短鏈的ID
哈希算法簡單來說就是將一個元素映射成另一個元素,
哈希算法可以簡單分類兩類,
- 加密哈希,如MD5,SHA256等,
- 非加密哈希,如MurMurHash,CRC32,DJB等。
MD5算法
MD5消息摘要算法(MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數(shù),
可以產(chǎn)生出一個128位(16字節(jié))的散列值(hash value),
MD5算法將數(shù)據(jù)(如一段文字)運(yùn)算變?yōu)榱硪还潭ㄩL度值,是散列算法的基礎(chǔ)原理。
由美國密碼學(xué)家 Ronald Linn Rivest設(shè)計(jì),于1992年公開并在 RFC 1321 中被加以規(guī)范。
CRC算法
循環(huán)冗余校驗(yàn)(Cyclic Redundancy Check)是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或電腦文件等數(shù)據(jù),
產(chǎn)生簡短固定位數(shù)校驗(yàn)碼的一種散列函數(shù),由 W. Wesley Peterson 于1961年發(fā)表。
生成的數(shù)字在傳輸或者存儲之前計(jì)算出來并且附加到數(shù)據(jù)后面,然后接收方進(jìn)行檢驗(yàn)確定數(shù)據(jù)是否發(fā)生變化。
由于本函數(shù)易于用二進(jìn)制的電腦硬件使用、容易進(jìn)行數(shù)學(xué)分析并且尤其善于檢測傳輸通道干擾引起的錯誤,因此獲得廣泛應(yīng)用。
MurmurHash
MurmurHash 是一種非加密型哈希函數(shù),適用于一般的哈希檢索操作。
由 Austin Appleby 在2008年發(fā)明,并出現(xiàn)了多個變種,與其它流行的哈希函數(shù)相比,對于規(guī)律性較強(qiáng)的鍵,MurmurHash的隨機(jī)分布特征表現(xiàn)更良好。
這個算法已經(jīng)被很多開源項(xiàng)目使用,比如libstdc++ (4.6版)、Perl、nginx (不早于1.0.1版)、Rubinius、 libmemcached、maatkit、Hadoop、Redis,Memcached,Cassandra,HBase,Lucene等。
MurmurHash 計(jì)算可以是 128位、64位、32位,位數(shù)越多,碰撞概率越少。
所以,可以把長鏈做 MurmurHash 計(jì)算,可以得到的一個整數(shù)哈希值 ,
所得到的短鏈,類似于下面的形式
固定短鏈域名+哈希值?=?www.weibo.com/888888888
如何縮短域名?傳輸?shù)臅r候,可以把 MurmurHash之后的數(shù)字為10進(jìn)制,可以把數(shù)字轉(zhuǎn)成62進(jìn)制
www.weibo.com/abcdef
那么,使用地址的hash 編碼作為ID的問題是啥呢?
會出現(xiàn)碰撞,所以這種方案不適合。
方案2:數(shù)據(jù)庫自增長ID
屬于完全依賴數(shù)據(jù)源的方式,所有的ID存儲在數(shù)據(jù)庫里,是最常用的ID生成辦法,在單體應(yīng)用時期得到了最廣泛的使用,建立數(shù)據(jù)表時利用數(shù)據(jù)庫自帶的auto_increment作主鍵,或是使用序列完成其他場景的一些自增長ID的需求。
但是這種方式存在在高并發(fā)情況下性能問題,要解決該問題,可以通過批量發(fā)號來解決,
提前為每臺機(jī)器發(fā)放一個ID區(qū)間 [low,high],然后由機(jī)器在自己內(nèi)存中使用 AtomicLong 原子類去保證自增,減少對DB的依賴,
每臺機(jī)器,等到自己的區(qū)間即將滿了,再向 DB 請求下一個區(qū)段的號碼,
為了實(shí)現(xiàn)寫入的高并發(fā),可以引入 ?隊(duì)列緩沖+批量寫入架構(gòu),
等區(qū)間滿了,再一次性將記錄保存到DB中,并且異步進(jìn)行獲取和寫入操作, 保證服務(wù)的持續(xù)高并發(fā)。
比如可以每次從數(shù)據(jù)庫獲取10000個號碼,然后在內(nèi)存中進(jìn)行發(fā)放,當(dāng)剩余的號碼不足1000時,重新向MySQL請求下10000個號碼,在上一批號碼發(fā)放完了之后,批量進(jìn)行寫入數(shù)據(jù)庫。
但是這種方案,更適合于單體的 DB 場景,在分布式DB場景下, 使用 MySQL的自增主鍵, 會存在不同DB庫之間的ID沖突,又要使用各種辦法去解決,
總結(jié)一下, MySQL的自增主鍵生成ID的優(yōu)缺點(diǎn)和使用場景:
-
優(yōu)點(diǎn):
非常簡單,有序遞增,方便分頁和排序。
-
缺點(diǎn):
分庫分表后,同一數(shù)據(jù)表的自增ID容易重復(fù),無法直接使用(可以設(shè)置步長,但局限性很明顯);
性能吞吐量整個較低,如果設(shè)計(jì)一個單獨(dú)的數(shù)據(jù)庫來實(shí)現(xiàn) 分布式應(yīng)用的數(shù)據(jù)唯一性,
即使使用預(yù)生成方案,也會因?yàn)槭聞?wù)鎖的問題,高并發(fā)場景容易出現(xiàn)單點(diǎn)瓶頸。
-
適用場景:
單數(shù)據(jù)庫實(shí)例的表ID(包含主從同步場景),部分按天計(jì)數(shù)的流水號等;
分庫分表場景、全系統(tǒng)唯一性ID場景不適用。
所以,高并發(fā)場景, MySQL的自增主鍵,很少用。
方案3:分布式、高性能的中間件生成ID
Mysql 不行,可以考慮分布式、高性能的中間件完成。
比如 Redis、MongoDB 的自增主鍵,或者其他 分布式存儲的自增主鍵,但是這就會引入額外的中間組件。
假如使用Redis,則通過Redis的INCR/INCRBY自增原子操作命令,能保證生成的ID肯定是唯一有序的,本質(zhì)上實(shí)現(xiàn)方式與數(shù)據(jù)庫一致。
但是,超高并發(fā)場景,分布式自增主鍵的生產(chǎn)性能,沒有本地生產(chǎn)ID的性能高。
總結(jié)一下,分布式、高性能的中間件生成ID的優(yōu)缺點(diǎn)和使用場景:
-
優(yōu)點(diǎn):
整體吞吐量比數(shù)據(jù)庫要高。
-
缺點(diǎn):
Redis實(shí)例或集群宕機(jī)后,找回最新的ID值有點(diǎn)困難。
-
適用場景:
比較適合計(jì)數(shù)場景,如用戶訪問量,訂單流水號(日期+流水號)等。
方案4:UUID、GUID生成ID
UUID:
按照OSF制定的標(biāo)準(zhǔn)計(jì)算,用到了以太網(wǎng)卡地址、納秒級時間、芯片ID碼和許多可能的數(shù)字。由以下幾部分的組合:當(dāng)前日期和時間(UUID的第一個部分與時間有關(guān),如果你在生成一個UUID之后,過幾秒又生成一個UUID,則第一個部分不同,其余相同),時鐘序列,全局唯一的IEEE機(jī)器識別號(如果有網(wǎng)卡,從網(wǎng)卡獲得,沒有網(wǎng)卡以其他方式獲得)
GUID:
微軟對UUID這個標(biāo)準(zhǔn)的實(shí)現(xiàn)。UUID還有其它各種實(shí)現(xiàn),不止GUID一種,不一一列舉了。
這兩種屬于不依賴數(shù)據(jù)源方式,真正的全球唯一性ID
總結(jié)一下,UUID、GUID生成ID的優(yōu)缺點(diǎn)和使用場景:
-
優(yōu)點(diǎn):
不依賴任何數(shù)據(jù)源,自行計(jì)算,沒有網(wǎng)絡(luò)ID,速度超快,并且全球唯一。
-
缺點(diǎn):
沒有順序性,并且比較長(128bit),作為數(shù)據(jù)庫主鍵、索引會導(dǎo)致索引效率下降,空間占用較多。
-
適用場景:
只要對存儲空間沒有苛刻要求的都能夠適用,比如各種鏈路追蹤、日志存儲等。
方式5:snowflake算法(雪花算法)生成ID
snowflake ID 嚴(yán)格來說,屬于 本地生產(chǎn) ID,這點(diǎn)和 Redis ID、MongoDB ID不同, 后者屬于遠(yuǎn)程生產(chǎn)的ID。
本地生產(chǎn)ID性能高,遠(yuǎn)程生產(chǎn)的ID性能低。
snowflake ID原理是使用Long類型(64位),按照一定的規(guī)則進(jìn)行分段填充:時間(毫秒級)+集群ID+機(jī)器ID+序列號,每段占用的位數(shù)可以根據(jù)實(shí)際需要分配,其中集群ID和機(jī)器ID這兩部分,在實(shí)際應(yīng)用場景中要依賴外部參數(shù)配置或數(shù)據(jù)庫記錄。
總結(jié)一下,snowflake ID 的優(yōu)缺點(diǎn)和使用場景:
-
優(yōu)點(diǎn):
高性能、低延遲、去中心化、按時間總體有序
-
缺點(diǎn):
要求機(jī)器時鐘同步(到秒級即可),需要解決 時鐘回?fù)軉栴}
如果某臺機(jī)器的系統(tǒng)時鐘回?fù)?,有可能造?ID 沖突,或者 ID 亂序。
-
適用場景:
分布式應(yīng)用環(huán)境的數(shù)據(jù)主鍵
高并發(fā)ID的技術(shù)選型
這里,不用地址的hash 編碼作為ID
這里,不用數(shù)據(jù)庫的自增長ID
這里,不用redis、mongdb的分布式ID
最終,
這里,從發(fā)號性能、整體有序(B+樹索引結(jié)構(gòu)更加友好)的角度出發(fā),最終選擇的snowflake算法
snowflake算法的吞吐量在 100W ops +
但是 snowflake算法 問題是啥呢?需要解決時鐘回?fù)艿膯栴}。
如何解決時鐘回?fù)艿膯栴},可以參考 推特官方的 代碼、 百度ID的代碼、Shardingjdbc ID的源碼,綜合存儲方案設(shè)計(jì)解決。
5、數(shù)據(jù)存儲的高并發(fā)架構(gòu)
這個數(shù)據(jù),非常的結(jié)構(gòu)化,可以使用結(jié)構(gòu)化數(shù)據(jù)庫MYSQL存儲。
結(jié)構(gòu)非常簡單,我們會有二列:
1. ID,int, ??//?分布式雪花id;
2. SURL,varchar, ?//?原始URL;
接下來,開始高并發(fā)、海量數(shù)據(jù)場景,需要進(jìn)行 MYSQL存儲 的分庫分表架構(gòu)。
陳某提示,這里可以說說自己的分庫分表 操作經(jīng)驗(yàn),操作案例。
然后進(jìn)行 互動式作答。
也就是,首先是進(jìn)行 輸入條件 詢問,并且進(jìn)行確認(rèn)。
然后按照分治模式,進(jìn)行兩大維度的分析架構(gòu):
- 數(shù)據(jù)容量(存儲規(guī)模) 的 分治架構(gòu)、
- 訪問流量 ?(吞吐量規(guī)模)的 分治架構(gòu)。

這塊內(nèi)容涉的方案,不同的項(xiàng)目,基本是相通的。
6、二義性檢查的高并發(fā)架構(gòu)
所謂的地址二義性,就行同一個長址多次請求得到的短址不一樣。
在生產(chǎn)地址的時候,需要進(jìn)行二義性檢查,防止每次都會重新為該長址生成一個短址,一個個長址多次請求得到的短址是不一樣。
通過二義性檢查,實(shí)現(xiàn)長短鏈接真正意義上的一對一。
怎么進(jìn)行 二義性檢查?
最簡單,最為粗暴的方案是:直接去數(shù)據(jù)庫中檢查 。
但是,這就需要付出很大的性能代價。
要知道:
數(shù)據(jù)庫主鍵不是 原始url,而是 短鏈url 。
如果根據(jù) 原始url 去進(jìn)行存在性檢查,還需要額外建立索引。
問題的關(guān)鍵是,數(shù)據(jù)庫性能特低,沒有辦法支撐超高并發(fā) 二義性檢查
所以,這里肯定不能每次用數(shù)據(jù)庫去檢查。
這里很多同學(xué)可能會想到另一種方案,就是 redis 的布隆過濾, 把已經(jīng)生成過了的 原始url,
大致的方案是,可以把已經(jīng)生成過的 原始url ,在 redis 布隆過濾器中進(jìn)行記錄。
每次進(jìn)行二義性檢查,走redis 布隆過濾器。
布隆過濾器就是bitset+多次hash的架構(gòu),宏觀上是空間換時間,不對所有的 surl (原始url)進(jìn)行內(nèi)容存儲,只對surl進(jìn)行存在性存儲,這樣就節(jié)省大家大量的內(nèi)存空間。
在數(shù)據(jù)量比較大的情況下,既滿足時間要求,又滿足空間的要求。

布隆過濾器的巨大用處就是,能夠迅速判斷一個元素是否在一個集合中。
布隆過濾器的常用使用場景如下:
- 黑名單 : 反垃圾郵件,從數(shù)十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱(同理,垃圾短信)
- URL去重 : 網(wǎng)頁爬蟲對 URL 的去重,避免爬取相同的 URL 地址
- 單詞拼寫檢查
- Key-Value 緩存系統(tǒng)的 Key 校驗(yàn) (緩存穿透) : 緩存穿透,將所有可能存在的數(shù)據(jù)緩存放到布隆過濾器中,當(dāng)黑客訪問不存在的緩存時迅速返回避免緩存及 DB 掛掉。
- ID 校驗(yàn),比如訂單系統(tǒng)查詢某個訂單 ID 是否存在,如果不存在就直接返回。
Bloom Filter 專門用來解決我們上面所說的去重問題的,使用 Bloom Filter 不會像使用緩存那么浪費(fèi)空間。
當(dāng)然,他也存在一個小小問題,就是不太精確。
規(guī)則是:存在不一定存在,說不存在一定不存在
Bloom Filter 相當(dāng)于是一個不太精確的 set 集合,我們可以利用它里邊的 contains 方法去判斷某一個對象是否存在,但是需要注意,這個判斷不是特別精確。
一般來說,通過 contains 判斷某個值不存在,那就一定不存在,但是判斷某個值存在的話,則他可能不存在。
那么對于 surl,處理的方案是:
- 如果 redis bloom filter 不存在,直接生成
- 否則,如果 redis bloom filter 判斷為存在,可能是誤判,還需要進(jìn)行db的檢查。
但是, redis bloom filter誤判的概率很低,合理優(yōu)化之后,也就在1%以下。
可能有小伙伴說,如果100Wqps,1%也是10W1ps,DB還是扛不住,怎么辦?
可以使用緩存架構(gòu),甚至多級緩存架構(gòu)
具體來說,可以使用 Redis 緩存進(jìn)行 熱門url的緩存,實(shí)現(xiàn)部分地址的一對一緩存
比如將最近/最熱門的對應(yīng)關(guān)系存儲在K-V數(shù)據(jù)庫中,比如在本地緩存 Caffeine中存儲最近生成 的長對短的對應(yīng)關(guān)系,并采用過期機(jī)制實(shí)現(xiàn) LRU 淘汰,從而保證頻繁使用的 URL 的總是對應(yīng)同一個短址的,但是不保證不頻繁使用的URL的對應(yīng)關(guān)系,從而大大減少了空間上的消耗。
7、映射模塊(/轉(zhuǎn)換模塊)高并發(fā)架構(gòu)
這里,主要是介紹自己對 多級緩存的 掌握和了解。
可以使用了緩存,二級緩存、三級緩存,加快id 到 surl的轉(zhuǎn)換。
簡單的緩存方案
將熱門的長鏈接(需要對長鏈接進(jìn)來的次數(shù)進(jìn)行計(jì)數(shù))、最近的長鏈接(可以使用 Redis 保存最近一個小時的數(shù)據(jù))等等進(jìn)行一個緩存,如果請求的長URL命中了緩存,那么直接獲取對應(yīng)的短URL進(jìn)行返回,不需要再進(jìn)行生成操作
補(bǔ)充服務(wù)間的重定向301 和 302 的不同
301永久重定向和 302 臨時重定向。
- 301永久重定向:第一次請求拿到長鏈接后,下次瀏覽器再去請求短鏈的話,不會向短網(wǎng)址服務(wù)器請求了,而是直接從瀏覽器的緩存里拿,減少對服務(wù)器的壓力。
- 302臨時重定向:每次去請求短鏈都會去請求短網(wǎng)址服務(wù)器(除非響應(yīng)中用 Cache-Control 或 Expired 暗示瀏覽器進(jìn)行緩存)
使用 301 雖然可以減少服務(wù)器的壓力,但是無法在 server 層獲取到短網(wǎng)址的訪問次數(shù)了,如果鏈接剛好是某個活動的鏈接,就無法分析此活動的效果以及用于大數(shù)據(jù)分析了。
而 302 雖然會增加服務(wù)器壓力,但便于在 server 層統(tǒng)計(jì)訪問數(shù),所以如果對這些數(shù)據(jù)有需求,可以采用 302,因?yàn)檫@點(diǎn)代價是值得的,但是具體采用哪種跳轉(zhuǎn)方式,還是要結(jié)合實(shí)際情況進(jìn)行選型。
8、架構(gòu)的魅力
架構(gòu)魅力,在于沒有最好的方案,只有更好的方案,大家如果有疑問,或者更好的方案,可以多多交流。
2.?號稱 Redis Plus,來看看 KeyDB 性能有多炸裂!
3.?使用 IDEA 遠(yuǎn)程 Debug 調(diào)試(一篇懂所有)
最近面試BAT,整理一份面試資料 《Java面試BATJ通關(guān)手冊》 ,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)“ 在看 ”,關(guān)注公眾號并回復(fù)? Java ?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
PS:因公眾號平臺更改了推送規(guī)則,如果不想錯過內(nèi)容,記得讀完點(diǎn)一下 “在看” ,加個 “星標(biāo)” ,這樣每次新文章推送才會第一時間出現(xiàn)在你的訂閱列表里。
點(diǎn)“在看”支持小哈呀,謝謝啦
