轉載請附上本文地址: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;
}
?>
優點:直接基於鏈接進行轉換,不需要依賴於額外的資源
不足:存數據庫的時候反查起來,由於非數字所以有優化空間。