一看就懂系列之 高并发的短链接替换实现方案

来源:互联网 发布:中兴b700刷成网络盒子 编辑:程序博客网 时间:2024/06/06 10:04

转载请附上本文地址: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;}?>

优点:直接基于链接进行转换,不需要依赖于额外的资源
不足:存数据库的时候反查起来,由于非数字所以有优化空间。

阅读全文
1 0
原创粉丝点击