一看就懂系列之 高併發的短鏈接替換實現方案

轉載請附上本文地址:http://blog.csdn.net/u011957758/article/details/75810039

前言

是的,相信只要是社交類的app,或多或少會涉及到描述。那麼當存在鏈接的時候,我們不知道鏈接有多長,所以描述字段就沒法準確的用varchar(140)了。
難道用text?好像不行,兩方面原因:1.描述肯定要有限制的。2.dba不推薦text,效率你懂的。
所以對於高併發下的短鏈如何實現,做一個總結,用於日後留戀。

正文

簡述

需求:
1.描述需要限制140字,鏈接按照固定的字數算比如10字。
2.收集鏈接,用於額外的分析。
思路:
1.需要拆分出鏈接,便於計算字數,同時描述裏頭必須以短鍊形式替換不知長度的鏈接,這樣描述落地到數據庫時候,設置比140字稍長一點的字段即可。
2.需要將鏈接進行收集併入庫存儲,方便取出分析。
3.考慮線上數據量超級大,需要進行分析將儘可能非必要的操作異步化,讓短鏈實現時候基本處在內存中操作。

流程圖

這裏寫圖片描述

注意點

1.短鏈替換完後,先會在redis緩存中設定對應的鏈接,目的是防止數據積壓導致異步任務延時進行,導致描述輸出的時候,短鏈沒地方進行替換。

2.先是[U]{短鏈id}[/U],後面爲[L]{短鏈id}[/L],目的是爲了區分出鏈接是否有真的落到數據庫了,並且這樣可以更直觀的在數據庫的描述中識別出來。

3.如果量級不大,也可以用此套方案,將t2的實現直接在t1中完成,異步增加了複雜度與更多不確定因素。

4.存儲鏈接的數據庫字段需要設置爲“CHARACTER SET utf8mb4 COLLATE utf8mb4_bin”,兼容emoji的表情。

重點實現

1.去除特定的字符標記

public function stripUrls($des) {
    $des = preg_replace('/\[(U|L)\].+?\[\/(U|L)\]/i', '', $des);
    $des = str_ireplace('[U]', '', $des);
    $des = str_ireplace('[/U]', '', $des);
    $des = str_ireplace('[L]', '', $des);
    $des = str_ireplace('[/L]', '', $des);
    return $des;
}

2.匹配出鏈接

public function pregURL($content) {
    $pattern = '/(http|https):\/\/[a-zA-Z0-9\-]+(\.[a-zA-Z0-9]+)+([-A-Z0-9a-z_\$\.\+\!\*\(\)\/\,\:;@&=\?~#%]*)*/i';
    preg_match_all($pattern, $content, $result);

    return $result;
}

3.按照長度對匹配出的鏈接重新排序

public function reorderUrlsByLen($urls) {
    $shorted_urls = $short_by = array();
    foreach ($urls as $url){
        $shorted_urls[] = $url;
        $short_by[] = strlen($url);
    }
    array_multisort($short_by , SORT_DESC ,$shorted_urls);
    return $shorted_urls;
}

短鏈接的生成

方法1

算法原理
1.通過維護redis的一個key,進行自增id的維護。(64或32位的id生成器更佳)
2.將自增的id,轉成62進制輸出成短鏈識別。
3.解析的時候進行62進行反解,得到id後進行數據庫查找出準確的鏈接(需要多一層mc緩存)

public function getShortUrl($url) {
    return '[U]'.$this->getIncrID($url).'[/U]'
}

public function getIncrID() {
    $key = $this->getIncrIDKey();
    $id  = $this->redis->get($key);

    //從999開始,防止太短了
    if(!$id || !is_numeric($id)) {
        $id = 999;
    }

    $next_id = $id + 1;
    $this->redis->set($key, $next_id);

    return dec2s2($id);
}

//十進制轉62進制
function dec2s2($dec) {
    $base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $result = '';

    do {
        $result = $base[$dec % 62] . $result;
        $dec = intval($dec / 62);
    } while ($dec != 0);

    return $result;
}

//62進制轉十進制
function  s22dec($sixty_two) {
    $base_map = array (
        '0' => 0,
        '1' => 1,
        '2' => 2,
        '3' => 3,
        '4' => 4,
        '5' => 5,
        '6' => 6,
        '7' => 7,
        '8' => 8,
        '9' => 9,
        'a' => 10,
        'b' => 11,
        'c' => 12,
        'd' => 13,
        'e' => 14,
        'f' => 15,
        'g' => 16,
        'h' => 17,
        'i' => 18,
        'j' => 19,
        'k' => 20,
        'l' => 21,
        'm' => 22,
        'n' => 23,
        'o' => 24,
        'p' => 25,
        'q' => 26,
        'r' => 27,
        's' => 28,
        't' => 29,
        'u' => 30,
        'v' => 31,
        'w' => 32,
        'x' => 33,
        'y' => 34,
        'z' => 35,
        'A' => 36,
        'B' => 37,
        'C' => 38,
        'D' => 39,
        'E' => 40,
        'F' => 41,
        'G' => 42,
        'H' => 43,
        'I' => 44,
        'J' => 45,
        'K' => 46,
        'L' => 47,
        'M' => 48,
        'N' => 49,
        'O' => 50,
        'P' => 51,
        'Q' => 52,
        'R' => 53,
        'S' => 54,
        'T' => 55,
        'U' => 56,
        'V' => 57,
        'W' => 58,
        'X' => 59,
        'Y' => 60,
        'Z' => 61,
    );
    $result = 0;
    $len = strlen($sixty_two);

    for ($n = 0; $n < $len; $n++) {
        $result *= 62;
        $result += $base_map[$sixty_two{$n}];
    }

    return $result;
}

優點:試用redis或id生成器產生自增id,直接解決了高併發下id的產出問題,並且進行數據存儲的讀取的時候,由於是數字的id所以sql查詢效率更高。
不足:依賴資源。

方法2

來自博客:URL短網址生成算法原理和php實現案例
算法原理
1)將長網址md5生成32位簽名串,分爲4段, 每段8個字節;
2)對這四段循環處理, 取8個字節, 將他看成16進制串與0x3fffffff(30位1)與操作, 即超過30位的忽略處理;
3)這30位分成6段, 每5位的數字作爲字母表的索引取得特定字符, 依次進行獲得6位字符串;
4)總的md5串可以獲得4個6位串; 取裏面的任意一個就可作爲這個長url的短url地址;

public function getShortUrl($url) {
    return '[U]'.$this->getShortUrlKey($url).'[/U]'
}

function getShortUrlKey($input) {
    $base32 = array (
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
        'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
        'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
        'y', 'z', '0', '1', '2', '3', '4', '5'
    );

    $hex = md5($input);
    $hexLen = strlen($hex);
    $subHexLen = $hexLen / 8;
    $output = array();

    for ($i = 0; $i < $subHexLen; $i++) {
        //把加密字符按照8位一組16進制與0x3FFFFFFF(30位1)進行位與運算
        $subHex = substr ($hex, $i * 8, 8);
        $int = 0x3FFFFFFF & (1 * ('0x'.$subHex));
        $out = '';

        for ($j = 0; $j < 6; $j++) {
            //把得到的值與0x0000001F進行位與運算,取得字符數組chars索引
            $val = 0x0000001F & $int;
            $out .= $base32[$val];
            $int = $int >> 5;
        }
        $output[] = $out;
    }
    return $output;
}
?>

優點:直接基於鏈接進行轉換,不需要依賴於額外的資源
不足:存數據庫的時候反查起來,由於非數字所以有優化空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章