基於Redis的短鏈接設計思路


[Markdown閱讀][1] 今天上班的時候收到一個需要短鏈接的需求,之前的做法都是使用了新浪的短鏈接API(https://api.weibo.com/2/short_url/shorten.json)。但一是外網訪問,二可能是新浪有所限制(畢竟是免費的),性能肯定不是太好。於是就想能不能自己實現一個,這樣內網訪問肯定快不少。 下班在班車上想了下,初步有些思路,記錄一下,有什么說錯的,歡迎指正。關於短鏈接的問題,我首先想到是兩個問題: 1. 持久化的問題,是否有必要保證短鏈接永久有效? 2. 碰撞的問題,如何保證所有長鏈接對應的短鏈接是唯一的? 第一個問題我想如果能有一個算法直接對`url`本身進行計算然后得到一個短鏈接,而且是可逆那么這個問題就不存在了,但稍微一想就知道這個不大現實,比如類似md5這樣的摘要算法,一是不可逆,必然還是需要存儲一個映射關系的,二為了“短”,第2個問題肯定會更加突出。 此路不通,我想肯定還是得引入`DB`的,因為平時項目中使用`Redis`比較多,直覺它比較合適。對於存儲這種映射關系,`NoSQL`最合適不過了。而且`Redis`數據結構靈活,API簡單明了,性能也很不錯。開啟`EOF`后,持久化問題也能得到保證,另外`Redis`支持過期,可以滿足一些特殊的需求。 再來看性能問題(初衷就是想提高性能的)。個人覺得要提高性能,如何解決第2個問題是關鍵。我有一個思路相信也是大家都能想到的:“提前計算”和“緩存”,說就是`先計算好一批短鏈接緩存起來,請求過來直接取就可以了`。這里大概還有以下幾個問題: 1. 怎么保證緩存起來的短鏈接都是各不相同的? 2. 怎么保證緩存起來的跟歷史生成的不相同? 3. 怎么保證供大於求? 第1個問題,應該比較簡單,編碼應該是能解決的。第2個問題我考慮兩個方面: 1)短鏈接的基數必須足夠大。 參考新浪短鏈接http://t.cn/RhCtEye,6位字符,取值范圍在[a-zA-Z0-9],那么基數就是`56,800,235,584`,56億多應該說是足夠大的了。那么就要看`隨機算法`,只要足夠隨機,那么兩次重復的概率就很小了。 2)以防萬一,每次都去做一次check,按照上面的想法,生成的短鏈接映射關系都是存儲在`Redis`中的,只要一次`get`就可以了。`get`的時間復雜度為[O(1)][1],只要保證數據都在內存里是非常快的(加上內網訪問)。 第2)點在非常高的並發下還是有問題的,可能存在`get`的時候一個相同的key剛好還沒`set`的進來,但結合第1)點這種可能性應該說是非常非常小了,而且我們實際的並發也不是特別大,所以忽略不計。 第3個問題更多是設計上的考慮。我的想法是用一個阻塞隊列來實現,綜合前面的問題,那應該就是一個去重的阻塞隊列。至於如何保證“供大於求”,那就得根據前端的壓力來調整隊列的大小了。 這里還有一個問題,就是相同的url生成短鏈接的問題,如果要保證相同的url只對應一個短鏈接,那就得每次生成之前要去`Redis`找回一下(批量可以`mget`[O(N)][2]),增加了性能損耗,但是當這種情況很多的時候,比如惡意請求,那么反而會更好。可以作為可選配置。 上面說的都是短鏈接生成的問題,短鏈接重定向也就是還原到原始鏈接相對簡單,一個`get`操作就可以搞定了,如果為了提高性能,減少不必要的`Redis`訪問,可以在應用層增加一層Cache,比如`LRU Cache`。 最后說下存在的瓶頸: 1. 短鏈接的隨機生成算法,`java.util.Random`貌似不是非常靠譜; 2. 去重的阻塞隊列,這個應該是最大的瓶頸,如果一個隊列,並發性能就很容易受限於此,如果搞多個,那么碰撞的問題就比較棘手; 3. Redis的性能。其實這里Redis可以換成任何其他類似的`NoSQL`產品。 好了,把腦子里想的寫完了,感覺實際應該夠用了。有問題歡迎拍磚。 [1]: https://www.zybuluo.com/zhanjindong/note/34734 [2]: http://redis.readthedocs.org/en/latest/string/get.html [3]: http://redis.readthedocs.org/en/latest/string/mget.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM