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

今天做了一个小实验,起因如下:

先在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.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

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

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

运行时间对比 方法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 testsRequests per second 等结果,可以看出方法 4 比方法 3 的性能更高一些。

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

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

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

原文地址:https://www.cnblogs.com/sunshineliulu/p/12399213.html

时间: 2024-08-29 18:13:27

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

jmeter 随机取一个值的方法

1.添加用户自定义变量 在要用到随机值的地方写入 ${__RandomFromMultipleVars(1|2|0)} 例子: 效果: 原文地址:https://www.cnblogs.com/kaibindirver/p/11663082.html

从数组中随机抽取一个值,(别人问我,我自己想到的一个方法)

今天本来在群里瞎扯淡的,突然看到有人问一个问题,大概意思是:怎么样从一个数组中随机抽取一个元素.当时我想到了PHP中有一个内置函数是打乱数组的顺序的.我就想用这个函数打乱一下,然后就抽取第一个元素. 代码如下: $array = array( 'das'=>array('id'=>1,'name'=>'于杭'), 'dasss'=>array('id'=>2,'name'=>'张三'), 'dass'=>array('id'=>3,'name'=>'李

js数组中如何随机取出一个值

码农一枚,具体说明就不说了,直接贴代码: var arr = ["太阳光大","成功是优点的发挥","不要小看自己", "口说好话","手心向下是助人" ]; var index = Math.floor((Math.random()*arr.length)); alert(arr[index]); 亲测是没问题的,有问题可以给我留言,或给我发邮件([email protected]). 快年底了,俺最近在

在LoadRunner中从数组类型的参数随机取值的方法

使用web_reg_save_param做关联后,有时候会有多个匹配值. 为了模仿用户行为随机取一个值为后续transcation所用,可以使用lr_paramarr_random函数. 例如: web_reg_save_param("EntryID", "LB=_41\"\>\<nobr\>", "RB=\<", "Ord=All", "NOTFOUND=WARNING"

python 操作redis有序集合(sorted set)

#coding:utf8 import redis r =redis.Redis(host="23.226.74.190",port=63279,password="66666666666") 1.Zadd 命令用于将一个或多个成员元素及其分数值加入到有序集当中.如果某个成员已经是有序集的成员,那么更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上.分数值可以是整数值或双精度浮点数.如果有序集合 key 不存在,则创建一个空的有序集并执行

Redis 有序集合(sorted set)

Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员. 不同的是每个元素都会关联一个double类型的分数.redis正是通过分数来为集合中的成员进行从小到大的排序. 有序集合的成员是唯一的,但分数(score)却可以重复. 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1). 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员). 实例 redis 127.0.0.1:6379> ZADD w3ckey 1 r

辛星浅析Redis中的有序集合

Redis中的有序集合也就是sorted-set,它和set很相似,都是字符串的集合,都不允许重复的成员出现在一个集合张.有序集合与集合的主要差别是有序集合中的每一个元素都有一个序号与其相连,这个序号即score,Redis通过这个序号来为集合中的成员进行从小到大的排列.需要特别说明的是,尽管有序集合的元素值是唯一的,但是该value对应的score却可以是多个.在有序集合中添加.删除.更新一个成员的操作都很快,其时间复杂度是集合中成员的对数. 因为有序集合中的成员在集合中的位置是有序的,即便是

Redis命令学习-SortedSet(有序集合)

?ZADD ZADD key score member[score member ... ]:将一个或多个member元素及其score值加入到有序集key中.如果member已经是有序集合中的值,则更新score.score是整数值,双精度值.如果key不存在,则创建一个有序集合,并加入.如果key不是一个有序集合,则返回一个错误. 返回值:成功添加的数量,不包含已经存在和更新的成员. 127.0.0.1:6379> ZADD page_rank 10 google.com (integer)

Redis有序集合

Redis有序集合类似Redis集合存储在设定值唯一性.不同的是,一个有序集合的每个成员带有分数,用于以便采取有序set命令,从最小的到最大的分数有关. Redis 有序set添加,删除和测试中的O(1)的存在成员(固定时间,无论里面包含的元素集合的数量).列表的最大长度为232- 1元素(4294967295,超过4十亿每个元素的集合). 例子 redis 127.0.0.1:6379> ZADD tutorials 1 redis (integer) 1 redis 127.0.0.1:63