最爛的回答
實(shí)現(xiàn)一個(gè)算法,將長(zhǎng)地址轉(zhuǎn)成短地址。實(shí)現(xiàn)長(zhǎng)和短一一對(duì)應(yīng)。然后再實(shí)現(xiàn)它的逆運(yùn)算,將短地址還能換算回長(zhǎng)地址。
這個(gè)回答看起來(lái)挺完美的,然后候選人也會(huì)說(shuō)現(xiàn)在時(shí)間比較短,如果給我時(shí)間我去找這個(gè)算法就解決問(wèn)題了。但是稍微有點(diǎn)計(jì)算機(jī)或者信息論常識(shí)的人就能發(fā)現(xiàn),這個(gè)算法就跟永動(dòng)機(jī)一樣,是永遠(yuǎn)不可能找到的。即使我們定義短地址是100位。那么它的變化是62的100次方。62=10數(shù)字+26大寫字母+26小寫字母。無(wú)論這個(gè)數(shù)多么大,他也不可能大過(guò)世界上可能存在的長(zhǎng)地址。所以實(shí)現(xiàn)一一對(duì)應(yīng),本身就是不可能的。
再換一個(gè)說(shuō)法來(lái)反駁,如果真有這么一個(gè)算法和逆運(yùn)算,那么基本上現(xiàn)在的壓縮軟件都可以歇菜了,而世界上所有的信息,都可以壓縮到100個(gè)字符。這~可能嗎。
另一個(gè)很爛的回答
和上面一樣,也找一個(gè)算法,把長(zhǎng)地址轉(zhuǎn)成短地址,但是不存在逆運(yùn)算。我們需要把短對(duì)長(zhǎng)的關(guān)系存到DB中,在通過(guò)短查長(zhǎng)時(shí),需要查DB。
怎么說(shuō)呢,沒(méi)有改變本質(zhì),如果真有這么一個(gè)算法,那必然是會(huì)出現(xiàn)碰撞的,也就是多個(gè)長(zhǎng)地址轉(zhuǎn)成了同一個(gè)短地址。因?yàn)槲覀儫o(wú)法預(yù)知會(huì)輸入什么樣的長(zhǎng)地址到這個(gè)系統(tǒng)中,所以不可能實(shí)現(xiàn)這樣一個(gè)絕對(duì)不碰撞的hash函數(shù)。
比較爛的回答
那我們用一個(gè)hash算法,我承認(rèn)它會(huì)碰撞,碰撞后我再在后面加1,2,3不就行了。
ok,這樣的話,當(dāng)通過(guò)這個(gè)hash算法算出來(lái)之后,可能我們會(huì)需要做btree式的大于小于或者like查找到能知道現(xiàn)在應(yīng)該在后面加1,2,或3,這個(gè)也可能由于輸入的長(zhǎng)地址集的不確定性。導(dǎo)致生成短地址時(shí)間的不確定性。同樣爛的回答還有隨機(jī)生成一個(gè)短地址,去查找是否用過(guò),用過(guò)就再隨機(jī),如此往復(fù),直到隨機(jī)到一個(gè)沒(méi)用過(guò)的短地址。
正確的原理
上面是幾種典型的錯(cuò)誤回答,下面咱們直接說(shuō)正確的原理。
正確的原理就是通過(guò)發(fā)號(hào)策略,給每一個(gè)過(guò)來(lái)的長(zhǎng)地址,發(fā)一個(gè)號(hào)即可,小型系統(tǒng)直接用mysql的自增索引就搞定了。如果是大型應(yīng)用,可以考慮各種分布式key-value系統(tǒng)做發(fā)號(hào)器。不停的自增就行了。第一個(gè)使用這個(gè)服務(wù)的人得到的短地址是 http://xx.xx/0 第二個(gè)是 http://xx.xx/1 第11個(gè)是 http://xx.xx/a 第依次往后,相當(dāng)于實(shí)現(xiàn)了一個(gè)62進(jìn)制的自增字段即可。
幾個(gè)子問(wèn)題
1. 62進(jìn)制如何用數(shù)據(jù)庫(kù)或者KV存儲(chǔ)來(lái)做?
其實(shí)我們并不需要在存儲(chǔ)中用62進(jìn)制,用10進(jìn)制就好了。比如第10000個(gè)長(zhǎng)地址,我們給它的短地址對(duì)應(yīng)的編號(hào)是9999,我們通過(guò)存儲(chǔ)自增拿到9999后,再做一個(gè)10進(jìn)制到62進(jìn)制的轉(zhuǎn)換,轉(zhuǎn)成62進(jìn)制數(shù)即可。這個(gè)10~62進(jìn)制轉(zhuǎn)換,你完全都可以自己實(shí)現(xiàn)。
2. 如何保證同一個(gè)長(zhǎng)地址,每次轉(zhuǎn)出來(lái)都是一樣的短地址
上面的發(fā)號(hào)原理中,是不判斷長(zhǎng)地址是否已經(jīng)轉(zhuǎn)過(guò)的。也就是說(shuō)用拿著百度首頁(yè)地址來(lái)轉(zhuǎn),我給一個(gè)http://xx.xx/abc 過(guò)一段時(shí)間你再來(lái)轉(zhuǎn),我還會(huì)給你一個(gè) http://xx.xx/xyz。這看起來(lái)挺不好的,但是不好在哪里呢?不好在不是一一對(duì)應(yīng),而一長(zhǎng)對(duì)多短。這與我們完美主義的基因不符合,那么除此以外還有什么不對(duì)的地方?
有人說(shuō)它浪費(fèi)空間,這是對(duì)的。同一個(gè)長(zhǎng)地址,產(chǎn)生多條短地址記錄,這明顯是浪費(fèi)空間的。那么我們?nèi)绾伪苊饪臻g浪費(fèi),有人非常迅速的回答我,建立一個(gè)長(zhǎng)對(duì)短的KV存儲(chǔ)即可。嗯,聽(tīng)起來(lái)有理,但是。。。這個(gè)KV存儲(chǔ)本身就是浪費(fèi)大量空間。所以我們是在用空間換空間,而且貌似是在用大空間換小空間。真的劃算嗎?這個(gè)問(wèn)題要考慮一下。當(dāng)然,也不是沒(méi)有辦法解決,我們做不到真正的一一對(duì)應(yīng),那么打個(gè)折扣是不是可以搞定?
這個(gè)問(wèn)題的答案太多種,各有各招。這個(gè)方案最簡(jiǎn)單的是建立一個(gè)長(zhǎng)對(duì)短的hashtable,這樣相當(dāng)于用空間來(lái)?yè)Q空間,同時(shí)換取一個(gè)設(shè)計(jì)上的優(yōu)雅(真正的一對(duì)一)。實(shí)際情況是有很多性價(jià)比高的打折方案可以用,這個(gè)方案設(shè)計(jì)因人而異了。那我就說(shuō)一下我的方案吧。
我的方案是:用key-value存儲(chǔ),保存“最近”生成的長(zhǎng)對(duì)短的一個(gè)對(duì)應(yīng)關(guān)系。注意是“最近”,也就是說(shuō),我并不保存全量的長(zhǎng)對(duì)短的關(guān)系,而只保存最近的。比如采用一小時(shí)過(guò)期的機(jī)制來(lái)實(shí)現(xiàn)LRU淘汰。
這樣的話,長(zhǎng)轉(zhuǎn)短的流程變成這樣:
在這個(gè)“最近”表中查看一下,看長(zhǎng)地址有沒(méi)有對(duì)應(yīng)的短地址
有就直接返回,并且將這個(gè)key-value對(duì)的過(guò)期時(shí)間再延長(zhǎng)成一小時(shí)
如果沒(méi)有,就通過(guò)發(fā)號(hào)器生成一個(gè)短地址,并且將這個(gè)“最近”表中,過(guò)期時(shí)間為1小時(shí)
所以當(dāng)一個(gè)地址被頻繁使用,那么它會(huì)一直在這個(gè)key-value表中,總能返回當(dāng)初生成那個(gè)短地址,不會(huì)出現(xiàn)重復(fù)的問(wèn)題。如果它使用并不頻繁,那么長(zhǎng)對(duì)短的key會(huì)過(guò)期,LRU機(jī)制自動(dòng)就會(huì)淘汰掉它。
當(dāng)然,這不能保證100%的同一個(gè)長(zhǎng)地址一定能轉(zhuǎn)出同一個(gè)短地址,比如你拿一個(gè)生僻的url,每間隔1小時(shí)來(lái)轉(zhuǎn)一次,你會(huì)得到不同的短地址。但是這真的有關(guān)系嗎?
3. 如何保證發(fā)號(hào)器的大并發(fā)高可用
上面設(shè)計(jì)看起來(lái)有一個(gè)單點(diǎn),那就是發(fā)號(hào)器。如果做成分布式的,那么多節(jié)點(diǎn)要保持同步加1,多點(diǎn)同時(shí)寫入,這個(gè)嘛,以CAP理論看,是不可能真正做到的。其實(shí)這個(gè)問(wèn)題的解決非常簡(jiǎn)單,我們可以退一步考慮,我們是否可以實(shí)現(xiàn)兩個(gè)發(fā)號(hào)器,一個(gè)發(fā)單號(hào),一個(gè)發(fā)雙號(hào),這樣就變單點(diǎn)為多點(diǎn)了?依次類推,我們可以實(shí)現(xiàn)1000個(gè)邏輯發(fā)號(hào)器,分別發(fā)尾號(hào)為0到999的號(hào)。每發(fā)一個(gè)號(hào),每個(gè)發(fā)號(hào)器加1000,而不是加1。這些發(fā)號(hào)器獨(dú)立工作,互不干擾即可。而且在實(shí)現(xiàn)上,也可以先是邏輯的,真的壓力變大了,再拆分成獨(dú)立的物理機(jī)器單元。1000個(gè)節(jié)點(diǎn),估計(jì)對(duì)人類來(lái)說(shuō)應(yīng)該夠用了。如果你真的還想更多,理論上也是可以的。
4. 具體存儲(chǔ)如何選擇
這個(gè)問(wèn)題就不展開(kāi)說(shuō)了,各有各道,主要考察一下對(duì)存儲(chǔ)的理解。對(duì)緩存原理的理解,和對(duì)市面上DB、Cache系統(tǒng)可用性,并發(fā)能力,一致性等方面的理解。
5. 跳轉(zhuǎn)用301還是302
這也是一個(gè)有意思的話題。首先當(dāng)然考察一個(gè)候選人對(duì)301和302的理解。瀏覽器緩存機(jī)制的理解。然后是考察他的業(yè)務(wù)經(jīng)驗(yàn)。301是永久重定向,302是臨時(shí)重定向。短地址一經(jīng)生成就不會(huì)變化,所以用301是符合http語(yǔ)義的。同時(shí)對(duì)服務(wù)器壓力也會(huì)有一定減少。
但是如果使用了301,我們就無(wú)法統(tǒng)計(jì)到短地址被點(diǎn)擊的次數(shù)了。而這個(gè)點(diǎn)擊次數(shù)是一個(gè)非常有意思的大數(shù)據(jù)分析數(shù)據(jù)源。能夠分析出的東西非常非常多。所以選擇302雖然會(huì)增加服務(wù)器壓力,但是我想是一個(gè)更好的選擇。
轉(zhuǎn)載請(qǐng)保留原文地址: http://headlineschannel.com/show-405.html