今天作了一個小實驗,原由以下:
php
先在redis裏構造了測試數據,以下:nginx
> zadd my_zset_999 1 35570 (integer) 1 > zadd my_zset_999 2 40617 (integer) 1 > zadd my_zset_999 3 40956 (integer) 1 > zadd my_zset_999 4 41151 (integer) 1 > > zrange my_zset_999 0 -1 WITHSCORES 1) "35570" 2) "1" 3) "40617" 4) "2" 5) "40956" 6) "3" 7) "41151" 8) "4" > > zrange my_zset_999 0 -1 1) "35570" 2) "40617" 3) "40956" 4) "41151"
測試方法就是很簡單的計算程序運行時間。redis
$t1 = microtime(true); // 代碼片斷 $t2 = microtime(true); $t = $t2 - $t1;
方法1
zrange
key 0 -1 取出全部的值
array_rand() 從數組中隨機取出一個值數組
方法2
zcount
key -inf +inf 計算該集合有多少個元素(cnt)
rand(1, cnt) 生成一個隨機數(random)
zrangebyscore
key random random數據結構
方法3:對方法2的改造
zcard
key 計算該集合有多少個元素(cnt)
rand(1, cnt) 生成一個隨機數(random)
zrangebyscore
key random random併發
方法4:對方法1的改造
zrangebyscore
key -inf +inf
array_rand() 從數組中隨機取出一個值dom
方法 1 和方法 4 都是先取出有序集合的全部值,再隨機取出一個值;
方法 2 和方法 3 則是隨機從有序集合中取出一個值。工具
下面是各方法的運行時間對比。性能
方法 2 和方法 3,即 zcount
和 zcard
的運行時間對比:測試
運行時間對比 | 方法2/zcount | 方法3/zcard |
---|---|---|
第1次 | 0.0072240829467773 | 0.007314920425415 |
第2次 | 0.0057311058044434 | 0.0071389675140381 |
第3次 | 0.0065360069274902 | 0.0071680545806885 |
第4次 | 0.0047309398651123 | 0.0075440406799316 |
第5次 | 0.0058040618896484 | 0.0068428516387939 |
第6次 | 0.0068061351776123 | 0.0073769092559814 |
第7次 | 0.0070509910583496 | 0.0070638656616211 |
第8次 | 0.008112907409668 | 0.0076460838317871 |
第9次 | 0.0070209503173828 | 0.0067050457000732 |
第10次 | 0.0069761276245117 | 0.0073142051696777 |
能夠看出 zcount
比 zcard
的波動大,且用時長,因此淘汰方法2,這是由於 zcard
的時間複雜度是 O(1)
,而 zcount
的時間複雜度是 O(log(N))
。
方法 1 和方法 3,即 zrange
和 zrangebyscore
的運行時間對比:
運行時間對比 | 方法1/zrange | 方法3/zrangebyscore |
---|---|---|
第1次 | 0.0076210498809814 | 0.0040271282196045 |
第2次 | 0.0066070556640625 | 0.0056281089782715 |
第3次 | 0.0062861442565918 | 0.0061671733856201 |
第4次 | 0.0070350170135498 | 0.0064809322357178 |
第5次 | 0.0070219039916992 | 0.0068569183349609 |
能夠看出方法 2 比方法 1 要快一些。那若是把方法 1 改爲用 zrangebyscore
取出全部值,再隨機取元素呢,也就是方法 4,再比較方法 4 和方法 3 的運行時間:
運行時間對比 | 方法4/zrangebyscore取出數組,隨機取出1一個值 | 方法3/zrangebyscore根據隨機數取出一個值 |
---|---|---|
第1次 | 0.0068261623382568 | 0.0075819492340088 |
第2次 | 0.0072751045227051 | 0.0073590278625488 |
第3次 | 0.0055849552154541 | 0.0072290897369385 |
第4次 | 0.0048110485076904 | 0.0075399875640869 |
第5次 | 0.0073840618133545 | 0.0075678825378418 |
第6次 | 0.0072331428527832 | 0.0072460174560547 |
第7次 | 0.007411003112793 | 0.0074880123138428 |
第8次 | 0.0062360763549805 | 0.007282018661499 |
第9次 | 0.0077290534973145 | 0.0074591636657715 |
第10次 | 0.0068199634552002 | 0.0074419975280762 |
能夠看到方法 4 比方法 3 快一些,再用 ab
測試工具測一下
# 模擬100個併發用戶,對一個資源發送100個請求。 ab -c 100 -n 100 url
方法 4 的測試結果以下:
Server Software: nginx/1.15.11 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: test1.php Document Length: 38 bytes Concurrency Level: 100 Time taken for tests: 0.520 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Total transferred: 23400 bytes HTML transferred: 3800 bytes Requests per second: 192.25 [#/sec] (mean) Time per request: 520.161 [ms] (mean) Time per request: 5.202 [ms] (mean, across all concurrent requests) Transfer rate: 43.93 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 18 25 5.6 26 35 Processing: 41 219 87.1 219 359 Waiting: 41 219 87.4 219 359 Total: 60 245 92.3 246 393 Percentage of the requests served within a certain time (ms) 50% 246 66% 296 75% 326 80% 340 90% 372 95% 392 98% 392 99% 393 100% 393 (longest request)
方法 3 的測試結果以下:
Server Software: nginx/1.15.11 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: /test2.php Document Length: 38 bytes Concurrency Level: 100 Time taken for tests: 0.526 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Total transferred: 23400 bytes HTML transferred: 3800 bytes Requests per second: 189.97 [#/sec] (mean) Time per request: 526.390 [ms] (mean) Time per request: 5.264 [ms] (mean, across all concurrent requests) Transfer rate: 43.41 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 16 23 3.8 25 31 Processing: 36 216 89.5 220 372 Waiting: 36 216 89.2 220 372 Total: 54 239 92.9 245 403 Percentage of the requests served within a certain time (ms) 50% 245 66% 295 75% 316 80% 333 90% 362 95% 374 98% 402 99% 403 100% 403 (longest request)
經過 Time taken for tests
、Requests per second
等結果,能夠看出方法 4 比方法 3 的性能更高一些。
也就是先取出全部元素,再隨機取出一個值 和 構造一個隨機數取出一個元素 這兩種方案,前者更好一些。
到這裏就結束了嗎?並無~
最終結果就是不採用有序集合這種數據結構了,用列表或集合這種數據結構便可。由於有序集合 zset
還要構造 score
值,好比插入元素,要查出最大的score值,再加 1。
既然需求只是從一堆元素中隨機取一個值,用列表或集合這種數據結構就能知足所需了。