提出問(wèn)題
網(wǎng)站每天有1億的訪問(wèn)量,產(chǎn)品提出要統(tǒng)計(jì)每個(gè)uid的周活躍,目前是日志分析解決的,每天有20G的日志,公司有dip平臺(tái)會(huì)用日志去計(jì)算,每次要計(jì)算兩小時(shí)才能處理完。
分析問(wèn)題
考慮了一下是否可以用redis的bitmap的方式來(lái)做一個(gè)統(tǒng)計(jì)周活躍的功能
先簡(jiǎn)單說(shuō)下bitmap的原理:
假設(shè)有3個(gè)同學(xué):
小明 1 | 小明 2 | 小明 3 |
---|---|---|
男 | 女 | 女 |
如果有三間房,0是男,女是1,
房1 | 房2 | 房2 |
---|---|---|
0 | 1 | 1 |
如果要統(tǒng)計(jì)現(xiàn)在班上有幾位女生,就可以看到兩個(gè)1就是兩位女生
在計(jì)算機(jī)里,一個(gè)字節(jié)里有8個(gè)二進(jìn)制位,即1byte=8bit
假設(shè)有7個(gè)數(shù)字,我們可以按照編號(hào)放進(jìn)一段連續(xù)內(nèi)存里,對(duì)應(yīng)位置中存在就顯示1,其它默認(rèn)都顯示0
比如 3,5,1,7,11,15,4,1
那對(duì)應(yīng)的位置為:
byte0 0 1 0 1 0 1 0 1
byte1 0 0 0 1 0 0 0 1
很明顯我們已經(jīng)方便的對(duì)這些數(shù)字做了排序了,甚至還做了排重
如果我們要找7是否存在這個(gè)數(shù)組中,
只需要7/8 對(duì)應(yīng)到第一個(gè)字節(jié)byte0中
7%8對(duì)應(yīng)到第7位上,如果是1就存在,0就不存在。
如果兩億的數(shù)字做排序排重,我們大概要占用好幾G的空間,如果用bitmap方式,最少只需要200000000/8/1024/1024 = 24M的空間就夠了
接下來(lái)我們看看bitmap在redis上的應(yīng)用:
假設(shè)這是我們uid的登錄情況
0代表未登錄,1代表登錄
Monday
8987129 0
8298191 1
8892198 1
Tuesday
8987129 0
8298191 0
8892198 1
Wednesday
8987129 0
8298191 1
8892198 1
Thursday
8987129 0
8298191 0
8892198 0
Friday
8987129 0
8298191 1
8892198 1
Saturday
8987129 0
8298191 1
8892198 0
Sunday
8987129 1
8298191 1
8892198 0
用setbit方法,將這些數(shù)據(jù)錄入到redis中:
setbit key offset value
設(shè)置offset對(duì)應(yīng)二進(jìn)制上的值,返回該位上的舊值
注意:如果offset過(guò)大,則會(huì)在中間填充0
offset最大到2^32-1,即可推出最大的字符串為512M
127.0.0.1:6379> setbit Monday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Monday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Monday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Tuesday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Tuesday 8298191 0
(integer) 0
127.0.0.1:6379> setbit Tuesday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Wednesday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Wednesday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Wednesday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Thursday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Thursday 8298191 0
(integer) 0
127.0.0.1:6379> setbit Thursday 8892198 0
(integer) 0
127.0.0.1:6379> setbit Friday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Friday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Friday 8892198 1
(integer) 0
127.0.0.1:6379> setbit Saturday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Saturday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Saturday 8892198 0
(integer) 0
127.0.0.1:6379> setbit Sunday 8987129 0
(integer) 0
127.0.0.1:6379> setbit Sunday 8298191 1
(integer) 0
127.0.0.1:6379> setbit Sunday 8892198 0
(integer) 0
接下來(lái)要計(jì)算7天內(nèi)有登錄行為的用戶(hù),只需要將周一到周五的值做位或運(yùn)算就可以了
補(bǔ)充下位與運(yùn)算符:
按位與運(yùn)算符(&)
參加運(yùn)算的兩個(gè)數(shù)據(jù),按二進(jìn)制位進(jìn)行“與”運(yùn)算。
運(yùn)算規(guī)則:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:兩位同時(shí)為“1”,結(jié)果才為“1”,否則為0
按位或運(yùn)算符(|)
參加運(yùn)算的兩個(gè)對(duì)象,按二進(jìn)制位進(jìn)行“或”運(yùn)算。
運(yùn)算規(guī)則:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :參加運(yùn)算的兩個(gè)對(duì)象只要有一個(gè)為1,其值為1。
異或運(yùn)算符(^)
參加運(yùn)算的兩個(gè)數(shù)據(jù),按二進(jìn)制位進(jìn)行“異或”運(yùn)算。
運(yùn)算規(guī)則:0^0=0; 0^1=1; 1^0=1; 1^1=0;
即:參加運(yùn)算的兩個(gè)對(duì)象,如果兩個(gè)相應(yīng)位為“異”(值不同),則該位結(jié)果為1,否則為0。
最后計(jì)算7天內(nèi)登錄過(guò)的活躍用戶(hù):
127.0.0.1:6379> bitop OR result Monday Tuesday Wednesday Thursday Friday Saturday Sunday
bitop operation rs key1 [key2..]
對(duì)key1 key2做opecation并將結(jié)果保存在rs上
opecation可以是AND(與) OR(或) NOT(非) XOR(異或)
8298191 1|0|1|0|1|1|1 = 1
8892198 1|1|1|0|1|0|0 = 1
8987129 0|0|0|0|0|0|0 = 0
這里計(jì)算的結(jié)果假設(shè)3個(gè)uid都是連續(xù)的話就是 110,其實(shí)真實(shí)的存儲(chǔ)位置是
...1.....1....0......
也就是本周有兩個(gè)活躍用戶(hù)登錄過(guò)。
就這樣,一個(gè)上億訪問(wèn)量的日志統(tǒng)計(jì),在redis的bitmap方式輕松解決。