从有序集合随机取一个值,应该用什么方案?

今天做了一个小实验,起因如下:
953680-20200303003552762-2125838316.png

先在redis里构造了测试数据,如下:

> 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"

测试方法就是很简单的计算程序运行时间

$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() 从数组中随机取出一个值

方法 1 和方法 4 都是先取出有序集合的所有值,再随机取出一个值;
方法 2 和方法 3 则是随机从有序集合中取出一个值。

下面是各方法的运行时间对比。

方法 2 和方法 3,即 zcountzcard 的运行时间对比:

运行时间对比方法2/zcount方法3/zcard
第1次0.00722408294677730.007314920425415
第2次0.00573110580444340.0071389675140381
第3次0.00653600692749020.0071680545806885
第4次0.00473093986511230.0075440406799316
第5次0.00580406188964840.0068428516387939
第6次0.00680613517761230.0073769092559814
第7次0.00705099105834960.0070638656616211
第8次0.0081129074096680.0076460838317871
第9次0.00702095031738280.0067050457000732
第10次0.00697612762451170.0073142051696777

可以看出 zcountzcard 的波动大,且用时长,所以淘汰方法2,这是因为 zcard 的时间复杂度是 O(1),而 zcount 的时间复杂度是 O(log(N))

方法 1 和方法 3,即 zrangezrangebyscore 的运行时间对比:

运行时间对比方法1/zrange方法3/zrangebyscore
第1次0.00762104988098140.0040271282196045
第2次0.00660705566406250.0056281089782715
第3次0.00628614425659180.0061671733856201
第4次0.00703501701354980.0064809322357178
第5次0.00702190399169920.0068569183349609

可以看出方法 2 比方法 1 要快一些。那如果把方法 1 改成用 zrangebyscore 取出所有值,再随机取元素呢,也就是方法 4,再比较方法 4 和方法 3 的运行时间:

运行时间对比方法4/zrangebyscore取出数组,随机取出1一个值方法3/zrangebyscore根据随机数取出一个值
第1次0.00682616233825680.0075819492340088
第2次0.00727510452270510.0073590278625488
第3次0.00558495521545410.0072290897369385
第4次0.00481104850769040.0075399875640869
第5次0.00738406181335450.0075678825378418
第6次0.00723314285278320.0072460174560547
第7次0.0074110031127930.0074880123138428
第8次0.00623607635498050.007282018661499
第9次0.00772905349731450.0074591636657715
第10次0.00681996345520020.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 testsRequests per second 等结果,可以看出方法 4 比方法 3 的性能更高一些。

也就是先取出所有元素,再随机取出一个值 和 构造一个随机数取出一个元素 这两种方案,前者更好一些。

到这里就结束了吗?并没有~

最终结果就是不采用有序集合这种数据结构了,用列表集合这种数据结构即可。因为有序集合 zset 还要构造 score 值,比如插入元素,要查出最大的score值,再加 1。
既然需求只是从一堆元素中随机取一个值,用列表集合这种数据结构就能满足所需了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值