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

转载请附上本文地址: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张图片

注意点

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;
}
?>

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

如果你觉得有收获~可以关注我的公众号【咖啡色的羊驼】~第一时间收到我的分享和知识梳理~
在这里插入图片描述

你可能感兴趣的:(php)