一. 问题场景
Redis 作为当前最流行的内存型 NoSQL 数据库,被许多公司所使用,作为分布式缓存。我们在实际使用中一般都会为 key 带上指定的前缀或者其他定义的格式。当由于我们程序出现bug,造成 redis 里面的存储的值,与我们预期的不一致时,我们可以通过查询指定格式的 key,来定位到我们具体的出现问题的key,从而方便我们解决问题。
二. 解决办法
1.Keys 命令
Keys 命令用于查找所有符合给定模式 pattern 的 key 。要求 Redis 版本大于 1.0.0。keys在查询大数量key时,会长时间阻塞Redis,由于Redis是单线程的,这就是一个突出的问题,需要注意。
2.Scan 命令
Scan 命令相对于 Keys 命令来说,优点就是不会阻塞服务器。要求 Redis 版本大于 2.8。
三. 代码实现
这里采用的Redis驱动是 StackExchange.Redis。
在 StackExchange.Redis 里封装 Redis 命令时分为了两种,一种是针对于集群的命令,一种是针对于单个Redis服务器的命令,Keys 和 Scan 命令便是后者,我们在使用的时候必须在单独的服务器上执行。
Keys 和 Scan 命令都支持模糊查询,这里介绍三种匹配符:
-
* 表示可以匹配
多个
任意字符
-
? 表示可以匹配
单个
任意字符
-
[] 表示可以匹配
指定范围
内的字符
因为我们的key可能分布在集群内多个Redis服务器上,所以我们需要在每台服务器上都执行命令。我们可以通过 ConnectionMultiplexer.GetEndPoints() 方法来获取所有的终结点信息。
在 StackExchange.Redis 对于 keys 和 scan 命令统一封装为了 IServer.Keys()方法,它会自动根据Redis服务器版本来决定使用keys命令还是scan命令。
为了方便测试,我在 Redis 里面准备了四个以 test 为前缀的key,放在序号为1的db里面:
1.遍历所有前缀为 test 的key 代码如下:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.*")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
执行结果:
2.[]的用法
假设我要遍历 key为 test.1-test.3 的数据,可以这样写:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.[1-3]")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
执行结果:
假设我要遍历 key为 test.1和test.4 的数据,可以这样写:
static async Task Main(string[] args){ //创建连接 var conn = await ConnectionMultiplexer.ConnectAsync("192.168.10.110"); //获取db var db = conn.GetDatabase(1); //遍历集群内服务器 foreach (var endPoint in conn.GetEndPoints()) { //获取指定服务器 var server = conn.GetServer(endPoint); //在指定服务器上使用 keys 或者 scan 命令来遍历key foreach (var key in server.Keys(1,"test.[1,4]")) { //获取key对于的值 var val = db.StringGet(key); Console.WriteLine($"key: {key}, value: {val}"); } }}
执行结果:
好了,关于 Redis 查询指定格式的 key 的方法就介绍到这里了
一. 问题场景Redis 作为当前最流行的内存型 NoSQL 数据库,被许多公司所使用,作为分布式缓存。我们在实际使用中一般都会为 key 带上指定的前缀或者其他定义的格式。当由于我们程序出现bug,造成 redis 里面的存储的值,与我们预期的不一致时,我们可以通过查询指定格式的 key,来定位到我们具体的出现问题的key,从而方便我们解决问题。二. 解决办法1.Keys 命令Keys 命令用于...
.net
core
出来有一时间了,这段时间也一直在做技术准备,目前想做一个单点登录(SSO)系统,在这之前用
.net
时我用习惯了machine
Key
,也顺手在
.net
core
中尝试了一上,结果发现不好使了,也不起作用,于是开始了网上学习。
实现
方法
功夫不负有心人,网上高人还是多,在github.com上面ISSUES中也有人在讨论此问题,于是找到代码尝试,结果
实现
了。
直接上代码,我们需要先封装一个XmlRepository,
Key
的
格式
如下:
<?xml version=1.0 encoding=utf-8?>
<
key
id=cbb8a41a-9c
查找符合某个条件比如
前缀
为cart_的
key
,这里我预先插入了一万条数据,在正式开始之前先说一下
key
s 指令的匹配规则:
key
s pattern里面有3个通配符 分别是 *,?,[]
*:通配多个任意字符
?:通配单个字符
[]:通配数组内的某个字符
这些通配都可以组合使用,比如我插入了四个键
List
key
s = new ArrayList<>();
//初始
key
s
List list = this.
redis
Template.opsForValue().multiGet(
key
s);
2、利用PipeLine
final List
key
s = new ArrayListList list = this.
redis
Template.executePipel
redis
-cli -p 26379 -a 123456 -n 0
key
s g.at.ga.* > a.txt
2. 拼出取value的
命令
sed 's/^/get &/g' a.txt > b.txt
3. 取value
cat b.txt |
redis
-cli -p......
//模糊匹配
redis
key
Set<String>
key
s = string
Redis
Template.
key
s("*" +
Redis
Key
Utils.getAllCacheCircleUser
Key
(userId.toString()) + "*");
2. 根据
key
批量
获取
hash value
*
批量
获取
hash
Key
value
* @param
key
s
* @return
一、
获取
方式
redis
的
命令
key
s(*) 可以
获取
所有的
key
。但是此种方式当数据量大的时候,会产生阻塞的情况。
redis
的
key
还可以通过scan
命令
获取
key
。scan采用渐进式遍历的方式来解决
key
s
命令
可能带来的阻塞问题
与SCAN
命令
相关的
命令
:
与SCAN
命令
相关的
命令
还有 SSCAN
命令
、 HSCAN
命令
和 ZSCAN
命令
,都是用于增量地迭代(incrementally iterate)一集元素(a collection of elements
redis
用scan代替
key
s
众所周知,当
redis
中
key
数量越大,
key
s
命令
执行越慢,而且最重要的会阻塞服务器,对单线程的
redis
来说,简直是灾难,且在生产环境,
key
s
命令
一般是被禁止的。scan可用来替换
key
s请求。
# scan用法
SCAN cursor [MATCH pattern] [COUNT count]
scan是一个增量迭代式的
命令
,这意味着每次调用这个
命令
都会返回一个游标cursor,该游标用于下次
查询
。
查询
开始时,cursor值为0;当
查询
结束时,cursor的值也
redis
作为我们接口开发中的首选缓存,当从
redis
中每次
获取
一个
key
的value时,接口响应时间上一般是没啥问题的,但是当需要
获取
多个
key
的value时,一般需要使用mget等
批量
获取
的方法。但是当value是以压缩
格式
写入到
redis
中的时候,还可以直接套用原来的方法吗?最近在一个接口的开发过程中遇到了这个问题,特此记录下解决方法。
先复习下
redis
、jedis和
redis
Template之间的关系:
Redis
是用ANSI C写的一个基于内存的
Key
-Value数据库,而Jedis是R
redis
队列的
批量
获取
一直以来让我很困惑,今天又遇到了同样的问题,经过几番波折,终于找到了一个方法。分享一下
只需要
redis
自带的两个方法(lrange, ltrim)
lrange可以
获取
队列中某一段的数据,例如
获取
队列中1-10的数据,lrange
key
1 10
ltrim 可以保留所选区间的所有值,也就是可以
删除
所选区间之外的所有值, 例如 ltrim
key
5 10 就会...
```java
import org.springframework.data.
redis
.
core
.
Redis
Template;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MovieRankService {
private final
Redis
Template<String, Integer>
redis
Template;
private static final String
REDIS
_
KEY
= "MOVIE_RANK";
public MovieRankService(
Redis
Template<String, Integer>
redis
Template) {
this.
redis
Template =
redis
Template;
public void addMovie(Integer movieId) {
redis
Template.opsForZSet().incrementS
core
(
REDIS
_
KEY
, movieId, 1);
public List<Integer> getTopMovies(int n) {
return
redis
Template.opsForZSet().reverseRange(
REDIS
_
KEY
, 0, n - 1);
在这个示例中,我们使用了
Redis
的有序集合数据结构来
实现
排行榜功能。排行榜中每个电影对应的分数即为它的热度值,每次添加电影时我们通过incrementS
core
方法自增1分数值。使用reverseRange方法可以
获取
到前n个分数最高的电影id,这里我们直接返回了id列表。
如果需要更加复杂的排行榜或者拓展,可以根据实际场景进行调整和扩展。