1.redis数据结构

redis五种数据结构
String
Hash 优点由于Hash结构会在单个Hash元素在不足一定数量时进行压缩存储,所以可以大量节约内存(HashMap)。
List 链表
Set 集合,一堆不重复值的组合,求交集并集(HashSet创建了HashMap,HashMap中keySet来遍历set集合)。
Sorted Set 有序集合,增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列。
2.key太多,使用keys可能会导致线程阻塞(通过slowlog get [num] 查询慢查询操作)
当redis中key的数量过多时,由于redis是单线程的。keys指令会导致线程阻塞,线上涉及redis的服务会停止,直到指令执行完服务才正常。故在生产环境慎用keys!!!!!
keys activity*
1) "activity-10"
2) "activity-6"
3) "activity-7"
4) "activity-8"
5) "activity-4"
6) "activity-1"
7) "activity-11"
8) "activity-9"
9) "activity-5"
10) "activity-2"
11) "activity-13"
12) "activity-3"
13) "activity-14"
14) "activity-12"
如果有这样的场景需要模糊查询keys的数量可以使用scan。scan指令可以无阻塞的提取出指定模式的key列表(对应的其他数据结构有对应的SSCAN 命令、 HSCAN 命令和 ZSCAN 命令,第一个参数总是一个数据库键)。
SCAN 命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。
scan 0 match activity* count 100
1) "0"
2) 1) "activity-10"
2) "activity-6"
3) "activity-7"
4) "activity-8"
5) "activity-13"
6) "activity-14"
7) "activity-12"
8) "activity-4"
9) "activity-1"
10) "activity-5"
11) "activity-2"
12) "activity-3"
13) "activity-11"
14) "activity-9"
可以根据返回的新游标进行遍历,当下标为0时,说明迭代结束。
scan 0 match activity* count 10
1) "14"
2) 1) "activity-10"
2) "activity-6"
3) "activity-7"
4) "activity-8"
5) "activity-13"
6) "activity-14"
7) "activity-12"
8) "activity-4"
9) "activity-1"
10) "activity-5"
11) "activity-2"
scan 14 match activity* count 10
1) "0"
2) 1) "activity-3"
2) "activity-11"
3) "activity-9"
3.setnx 注意事项
setnx是redis设置锁常用的方式,但有几个事项需要注意以下:
1.SetNX 不具备设置过期时间的功能,所以需借助 Expire 来设置。
local:0>setnx "activity_test" 1
"1"
local:0>expire "activity_test" 10
"1"
2.setnx成功,expire失败。可以通过multi/exec实现原子性,但是多个请求若没有加判断,setnx失败,但是expire会成功,那么会一直刷新expire时间。使用set参数的方式实现就可以解决这些问题。
local:0>set "activity_test" "test" nx ex 10
"OK"
local:0>get "activity_test"
"test"
4. multi or pipeline ?
multi 事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行, 。
pipeline 将所有命令打包一次性发送。一次性批量执行所有命令,成功后再一次性返回所有处理结果,无法保证一致性。
local:0>multi
"OK"
local:0>set "activity_test_01" 1
"QUEUED"
local:0>set "activity_test_02" 2
"QUEUED"
local:0>exec
1) "OK"
2) "OK"
可以看到multi情况下,每个步骤客户端都收到服务端“QUEUED”的返回,redis服务端会把事务中命令保存到服务端内存中。
需要注意两点:
1.multi指令在执行时,配合watch能实现对key的监控,如果该key在执行过程中被修改,则该事务整个都会被取消;
2.如果只有multi,事务中有某条/某些命令执行失败了, 事务队列中的其他命令仍然会继续执行,Redis不会停止执行事务中的命令。
$redis->multi(Redis::PIPELINE);
$redis->sAdd($key1 , 1);//1
$redis->sAdd($key1 , 2);//2
$redis->exec();
pipeline客户端将命令写入缓冲,最后再通过exec命令发送给服务端。
需要注意两点:
1.pipeline如果指令数过大可能会造成网络阻塞,并且服务器将被迫回复一个队列答复,占用很多内存。所以,如果在需要发送大量的命令,最好是把他们按照合理数量分批次的处理。至于合适的数量一般在100-1000(搜索出来的,待证实)。
2.pipeline有某条/某些命令执行失败了,其他指令仍然执行。
所以pipeline选择客户端缓冲,multi选择服务端缓冲。multi每个命令都发送给服务端,pipeline一次性发送给服务端。根据业务场景选择合适方式。
5. redis 的hgetall获取的数据是有序的吗?
首先看一个例子:
$key2 = 'activity_h_test_06';
for ($index = 0; $index < 512; $index ++) {
$redis->hSet($key2, 'key' . $index, $index);
}
var_dump($redis->hGetAll($key2));
var_dump('length : ' . $redis->hLen($key2));
部分结果:
["key506"]=>
string(3) "506"
["key507"]=>
string(3) "507"
["key508"]=>
string(3) "508"
["key509"]=>
string(3) "509"
["key510"]=>
string(3) "510"
["key511"]=>
string(3) "511"
}
string(12) "length : 512"
key2插入了512个hashkey,可以看到返回数据是按照插入顺序返回。
当数量超过512个hashkey呢,看下一个例子:
$key2 = 'activity_h_test_07';
for ($index = 0; $index < 513; $index ++) {
$redis->hSet($key2, 'key' . $index, $index);
}
var_dump($redis->hGetAll($key2));
var_dump('length : ' . $redis->hLen($key2));
部分结果:
["key330"]=>
string(3) "330"
["key349"]=>
string(3) "349"
["key202"]=>
string(3) "202"
["key316"]=>
string(3) "316"
["key117"]=>
string(3) "117"
["key499"]=>
string(3) "499"
["key512"]=>
string(3) "512"
}
string(12) "length : 513"
可以看到当数量超过512个,返回的数据会编程无序的。为什么会造成这个现象呢,来大致看一下redis的源码:
int hashTypeSet(robj *o, sds field, sds value, int flags) {
....
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
hashTypeConvert(o, OBJ_ENCODING_HT);
....
}
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
.....
for (i = start; i <= end; i++) {
if (sdsEncodedObject(argv[i]) &&
sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
{
hashTypeConvert(o, OBJ_ENCODING_HT);
break;
}
.....
}
#define OBJ_HASH_MAX_ZIPLIST_ENTRIES 512
#define OBJ_HASH_MAX_ZIPLIST_VALUE 64
server.hash_max_ziplist_value
server.hash_max_ziplist_entries 可以通过server配置文件修改
可以看到当hash键值长度小于64字节或数量小于512个时,还是使用ziplist存储,而ziplist是一个经过特殊编码的双向链表,所以能保证有序。当不满足时会转化成hash存储。(ziplist详解)
所以当业务场景中需要保证有序时,可以使用redis列表或者有序集合。
网友评论