<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          短鏈接的設計與實現(xiàn)

          共 3527字,需瀏覽 8分鐘

           ·

          2020-10-29 21:11

          前言

          短鏈接的實現(xiàn)在生活中比較常見,比如我們接受到的廣告短信,短信會包含他們的活動鏈接。


          這個鏈接是進行壓縮過的,比較短。這樣既美觀也能滿足字數(shù)的限制,比如短信中某個字段需要在多少字符以內。




          短鏈跳轉的基本原理


          用戶訪問短鏈地址然后重定向到原來的地址。

          在HTTP協(xié)議中,30X狀態(tài)代表的是重定向的狀態(tài)。其中可以是301 也可以是302。


          301 代表永久重定向。對于GET請求, 301跳轉會默認被瀏覽器cache。也就是說,用戶第一次訪問某個短鏈接后,如果服務器返回301狀態(tài)碼,則這個用戶在后續(xù)多次訪問同一短鏈接地址,瀏覽器會直接請求跳轉地址,而不會再去短鏈接系統(tǒng)上取!


          這么做優(yōu)點很明顯,降低了服務器壓力,但是無法統(tǒng)計到短鏈接地址的點擊次數(shù)。


          302代表臨時重定向。對于GET請求, 302跳轉默認不會被瀏覽器緩存,除非在HTTP響應中通過 Cache-Control 或 Expires 暗示瀏覽器緩存。因此,用戶每次訪問同一短鏈接地址,瀏覽器都會去短鏈接系統(tǒng)上取。


          這么做的優(yōu)點是,能夠統(tǒng)計到短地址被點擊的次數(shù)了。但是服務器的壓力變大了。


          1. 生成策略

          如果用 62 個字符 [A-Z, a-z, 0-9][A?Z,a?z,0?9] 來代表一位的話(62進制)。那么我們設計長度為 n 的短鏈接,則可以包含會有 62^n 個鏈接。當然也可以添加別的字符,讓進制數(shù)變得更大,要注意特殊符號。


          我們可以將自增主鍵的值(十進制的ID)來計算得到短鏈字符(62進制的字符)。然后可以用一個全局發(fā)號器來提供自增主鍵,這樣編碼生成的短鏈字符做成key,提供的url做value。


          這樣域名解析系統(tǒng)通過key 在表中找到value。value 和key之間靠主鍵關聯(lián),這樣的方式別人也可以很容易的推導出來你的url(根據相應短鏈進行反推) 是具有規(guī)律性的。

          private static final String BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
          public static String toBase62(long num) { StringBuilder sb = new StringBuilder();do {int i = (int) (num % 62); sb.append(BASE.charAt(i)); num /= 62; } while (num > 0);
          return sb.reverse().toString();}

          那么我們如何打亂呢,打亂的方式可以將短鏈的字母打亂,也可以在固定位置插入隨機值。


          2.全局發(fā)號器

          其中全局發(fā)號器的自增主鍵可以涉及到分布式ID的生成。通過UUID的方式獲取字符型 ID 的話,數(shù)據庫占用空間大,索引效率比整型低。


          分布式ID的生成:

          1. 利用數(shù)據庫的自增ID 64位long類型

            • 數(shù)據庫自增ID的缺點是數(shù)據在插入前,無法獲得ID。數(shù)據在插入后,獲取的ID雖然是唯一的,但一定要等到事務提交后,ID才算是有效的。有些雙向引用的數(shù)據,不得不插入后再做一次更新,比較麻煩

          2. 采用一個集中式ID生成器,它可以是Redis,也可以是ZooKeeper,也可以利用數(shù)據庫的表記錄最后分配的ID。

            • 這種方式最大的缺點是復雜性太高,需要嚴重依賴第三方服務,而 且代碼配置繁瑣。一般來說,越是復雜的方案,越不可靠,并且測試越痛苦。

          3. 類似Twitter的Snowflake算法,它給每臺機器分配一個唯一標識,然后通過時間戳+標識+自增實現(xiàn)全局唯一ID。

            • 這種方式好處在于ID生成算法完全是一個無狀態(tài)機,無網絡調用,高效可靠。缺點是如果唯一標識有重復,會造成ID沖突。

          利用數(shù)據庫生成id一定要在用到的時候去生成 id 嗎,是否可以提前生成這些自增 id ?設計一個專門的發(fā)號表,每插入一條記錄,為短鏈 id 預留 (主鍵 id * 1000 - 999) 到 (主鍵 id * 1000) 的號段

          發(fā)號表:url_sender_num
          id | gmt_create | tmp_start_num | tmp_end_num
          如圖示:tmp_start_num 代表短鏈的起始 id,tmp_end_num 代表短鏈的終止 id。


          當長鏈轉短鏈的請求打到某臺機器時,先看這臺機器是否分配了短鏈號段,未分配就往發(fā)號表插入一條記錄,則這臺機器將為短鏈分配范圍在 tmp_start_num 到 tmp_end_num 之間的 id


          從 tmp_start_num 開始分配,一直分配到 tmp_end_num,如果發(fā)號 id 達到了 tmp_end_num,說明這個區(qū)間段的 id 已經分配完了,則再往發(fā)號表插入一條記錄就又獲取了一個發(fā)號 id 區(qū)間。


          畫外音:思考一下這個自增短鏈 id 在機器上該怎么實現(xiàn)呢, 可以用 redis, 不過更簡單的方案是用 AtomicLong,單機上性能不錯,也保證了并發(fā)的安全性,當然如果并發(fā)量很大,AtomicLong 的表現(xiàn)就不太行了,可以考慮用 LongAdder,在高并發(fā)下表現(xiàn)更加優(yōu)秀。流程如下圖所示

          這里有個需要注意的地方,我們可能需要防止多次相同的長鏈生成不同的短鏈 id 這種情況,這就需要每次先根據長鏈來查找 db 看是否存在相關記錄,一般的做法是給長鏈加索引,但這樣的話索引的空間會很大,所以我們可以對長鏈適當?shù)膲嚎s,比如 md5,再對長鏈的 md5 字段做索引,索引就會小很多。這樣只要根據長鏈的 md5 去表里查是否存在相同的記錄即可。

          如何讓各個機器分配的號段區(qū)間不重?


          小結

          以上做法為給要生成的鏈接分配一個分布式id,然后再生成62進制數(shù)。

          以上為通過自增序列來區(qū)別各個斷鏈的生成。還可以通過hash的方法來生成id。


          3. 哈希算法

          通過hash將原來的長鏈hash成一個序列數(shù),然后再進行62進制轉換。用到hash就要防止hash沖突,可通過數(shù)據庫主鍵避免沖突,或者通過布隆過濾器優(yōu)化判斷是否存在沖突的邏輯。


          數(shù)據庫避免沖突方式可先查找是否有再進行插入,2 次數(shù)據庫操作。對于這塊的優(yōu)化可通過DUPLICATE語句 優(yōu)化成一次。


          推薦 Google 出品的 MurmurHash 算法,MurmurHash 是一種非加密型哈希函數(shù),適用于一般的哈希檢索操作。


          與其它流行的哈希函數(shù)相比,對于規(guī)律性較強的 key,MurmurHash 的隨機分布特征表現(xiàn)更良好。


          非加密意味著著相比 MD5,SHA 這些函數(shù)它的性能肯定更高(實際上性能是 MD5 等加密算法的十倍以上),也正是由于它的這些優(yōu)點,所以雖然它出現(xiàn)于 2008,但目前已經廣泛應用到 Redis、MemCache、Cassandra、HBase、Lucene 等眾多著名的軟件中。


          MurmurHash 提供了兩種長度的哈希值,32 bit,128 bit,為了讓網址盡可通地短,我們選擇 32 bit 的哈希值,32 bit 能表示的最大值近 43 億,對于中小型公司的業(yè)務而言綽綽有余。


          比如對一個長鏈做 MurmurHash 計算,得到的哈希值為 3002604296,此時我們再將哈希值轉換為62進制數(shù)。



          4. 更高性能設計

          在電商公司,經常有很多活動,秒殺,搶紅包等等,在某個時間點的 QPS 會很高。

          考慮到這種情況,我們引入了 openResty,它是一個基于 Nginx 與 Lua 的高性能 Web 平臺。


          由于 Nginx 的非阻塞 IO 模型,使用 openResty 可以輕松支持 100 w + 的并發(fā)數(shù),一般情況下你只要部署一臺即可,不過為了避免單點故障,兩臺為宜。


          同時 openResty 也自帶了緩存機制,集成了 redis 這些緩存模塊,也可以直接連 mysql。不需要再通過業(yè)務層連這些中間件,性能自然會高不少

          如圖示,使用 openResty 省去了業(yè)務層這一步,直達緩存層與數(shù)據庫層,也提升了不少性能。


          最后

          通常我們用分布式id + "62進制"就可以了,哈希的方法可作為拓展思路。


          References

          【原創(chuàng)】這可能是東半球最接地氣的短鏈接系統(tǒng)設計liaoxuefeng.com/article/1280526512029729高性能短鏈設計

          瀏覽 99
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  少妇综合精品导航 | 中文字幕永久永久在线 | 91操操操 | 最新在线一级片 | 日韩性高潮视频 |