目录
RedisJson突然大火,号称性能吊打ES和mongo。趁机小小研究了一下。
RedisJson官网: RedisJSON - Use Redis as a JSON Store | Redis
RedisJson和RedisSearch都是Redis的一个模块,它提供了Json格式的存储结构。redis本身也可以用string或者hash等类型存储json,但是RedisJson提供了不一样的体验。它可以更方便的处理json,并且也有更高的效率。
可以通过docker,非常方便的安装RedisJson(默认最新版本):
docker pull redislabs/rejson
然后运行docker run就可以了。
docker run -d -p 6379:6379 --name redis-rejson redislabs/rejson:latest
可以通过module list查看当前的module:
[root@bogon friso]# docker exec -it f4fe2ec23644 sh
# redis-cli
127.0.0.1:6379> module list
1) 1) "name"
2) "ReJSON"
3) "ver"
4) (integer) 20004
RedisJson存储Json的时候,会按照树的结构存储,这也很好理解,因为json本身就是一棵树。而对于存储的各个节点的添加和查找,遵循了JsonPath规则,可以在这个链接进行查阅:
命令: JSON.SET <key> <path> <json> [NX | XX]
实例:
JSON.SET myDoc $ '{"user":{"name":"John Smith","tag":"foo,bar","hp":1000, "dmg":150}}'
需要说明的是,此处$指的是在哪个路径下添加Json。上述例子说明是在$下添加的,如果想在user路径下再添加json,可以这么写。
JSON.SET myDoc $.user.job '{"type":"teacher","place":"beijing"}'
效果如下:
"{\"user\":{\"name\":\"John Smith\",\"tag\":\"foo,bar\",\"hp\":1000,\"dmg\":150,\"job\":{\"type\":\"teacher\",\"place\":\"beijing\"}}}"
当然,也可以通过这个命令,进行数据更新。
JSON.SET myDoc $.user.job '{"type":"doctor","place":"tianjin"}'
以下实例,如无特别说明,都会基于myDoc这个可以进行操作。
命令:
JSON.GET <key>
[INDENT indentation-string]
[NEWLINE line-break-string]
[SPACE space-string]
[path ...]
实例:
127.0.0.1:6379> JSON.GET myDoc
"{\"user\":{\"name\":\"John Smith\",\"tag\":\"foo,bar\",\"hp\":1000,\"dmg\":150,\"job\":{\"type\":\"teacher\",\"place\":\"beijing\"}}}"
参数说明:
INDENT:指的是嵌套级别的缩进字符串
NEWLINE :设置在每行末尾打印的字符串
SPACE:设置放在键和值之间的字符串
INDENT实例:
127.0.0.1:6379> JSON.GET myDoc indent "\t"
"{\t\"user\":{\t\t\"name\":\"John Smith\",\t\t\"tag\":\"foo,bar\",\t\t\"hp\":1000,\t\t\"dmg\":150,\t\t\"job\":{\t\t\t\"type\":\"doctor\",\t\t\t\"place\":\"tianjin\"\t\t}\t}}"
可以对比来看,和没有设置indent,每个缩进层次加了不同的“\t”。比如user所在层,加了一个“\t”,name所在层加了两个"\t",等等。格式化看一下:
{
\t"user": {
\t\t"name": "John Smith",
\t\t"tag": "foo,bar",
\t\t"hp": 1000,
\t\t"dmg": 150,
\t\t"job": {
\t\t\t"type": "doctor",
\t\t\t"place": "tianjin"
\t\t}
\t}
}
NEWLINE 实例:
127.0.0.1:6379> JSON.GET myDoc newline "\n"
"{\n\"user\":{\n\"name\":\"John Smith\",\n\"tag\":\"foo,bar\",\n\"hp\":1000,\n\"dmg\":150,\n\"job\":{\n\"type\":\"doctor\",\n\"place\":\"tianjin\"\n}\n}\n}"
这么看的话,可能不太明白,格式化一下就很清晰了:
{\n
"user": {\n
"name": "John Smith",\n
"tag": "foo,bar",\n
"hp": 1000,\n
"dmg": 150,\n
"job": {\n
"type": "doctor",\n
"place": "tianjin"\n
}\n
}\n
}
直观上可以这么理解,INDENT指的是在每行头加字符,NEWLINE 是在行尾加字符。
SPACE实例:
127.0.0.1:6379> JSON.GET myDoc space "\n"
"{\"user\":\n{\"name\":\n\"John Smith\",\"tag\":\n\"foo,bar\",\"hp\":\n1000,\"dmg\":\n150,\"job\":\n{\"type\":\n\"doctor\",\"place\":\n\"tianjin\"}}}"
就是在键与值之间加的字符。同样格式化看一下:
{
"user": \n{
"name": \n"John Smith",
"tag": \n"foo,bar",
"hp": \n1000,
"dmg": \n150,
"job": \n{
"type": \n"doctor",
"place": \n"tianjin"
}
}
}
这三种字符的添加,看起来如丝般顺滑,不过这个是在什么场景下会用到呢?
命令:
JSON.STRAPPEND <key> [path] <json-string>
实例:
JSON.STRAPPEND myDoc $.user.name '" TOM"'
查看结果:
127.0.0.1:6379> JSON.GET myDoc
"{\"user\":{\"name\":\"John Smith TOM\",\"tag\":\"foo,bar\",\"hp\":1000,\"dmg\":150,\"job\":{\"type\":\"doctor\",\"place\":\"tianjin\"}}}"
可以看到,在路径$.user.name的值多了个“ TOM”。官网给的例子是基于路径".."的,可以将key中名字相同的键的值都更改了,可以在官网查看。
命令:
JSON.TOGGLE <key> <path>
实例:
先添加一个bool类型:JSON.SET myDoc $.user.male 'true'
toggle之:JSON.TOGGLE myDoc $.user.male
查看结果:
添加bool类型后的结果:
127.0.0.1:6379> JSON.GET myDoc
"{\"user\":{\"name\":\"John Smith TOM\",\"tag\":\"foo,bar\",\"hp\":1000,\"dmg\":150,\"job\":{\"type\":\"doctor\",\"place\":\"tianjin\"},\"male\":true}}"
toggle后的结果:
127.0.0.1:6379> JSON.GET myDoc
"{\"user\":{\"name\":\"John Smith TOM\",\"tag\":\"foo,bar\",\"hp\":1000,\"dmg\":150,\"job\":{\"type\":\"doctor\",\"place\":\"tianjin\"},\"male\":false}}"
命令:
JSON.NUMINCRBY <key> <path> <number>
实例:
JSON.NUMINCRBY myDoc $.user.hp 235
查看结果:
127.0.0.1:6379> JSON.GET myDoc
"{\"user\":{\"name\":\"John Smith TOM\",\"tag\":\"foo,bar\",\"hp\":1235,\"dmg\":150,\"job\":{\"type\":\"doctor\",\"place\":\"tianjin\"},\"male\":false}}"
命令:
JSON.ARRAPPEND <key> <path> <json> [json ...]
实例:
先创建有json数组的key值:
127.0.0.1:6379> json.get doc6
"{\"a\":[1],\"nested\":{\"a\":[1,2]},\"nested2\":{\"a\":42}}"增加数组元素:
127.0.0.1:6379> JSON.ARRAPPEND doc6 $..a 3 4
查看结果:
127.0.0.1:6379> json.get doc6
"{\"a\":[1,3,4],\"nested\":{\"a\":[1,2,3,4]},\"nested2\":{\"a\":42}}"
可以发现,所有键值为a的数组,都多了3,4 。
命令太多,我没有每个都试一遍,具体可以参见官网“commands”章节。
目前有好几个库,都可以支持RedisJson的操作,我选择的是Jedis。具体client支持,可以直接看git:https://github.com/RediSearch/RediSearch
使用springboot搭建。在dependency中引入jedis就可以了。
配置pom文件,添加
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.0.1</version>
</dependency>
配置application.yml
server: port: 8081 spring: application: name: redisStream version: v1.0 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 redis: database: 1 host: 192.168.46.75 port: 6379 jedis: pool: max-active: 20
配置jedis:
package com.redisStream.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.*; import redis.clients.jedis.providers.PooledConnectionProvider; /** * @author qzh * 创建时间:2022/1/27 10:00 */ @Configuration public class UnifiedJedisPoolAutoConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port:6379}") private Integer port; @Bean public UnifiedJedis unifiedJedis() { HostAndPort config = new HostAndPort(host, 6379); PooledConnectionProvider provider = new PooledConnectionProvider(config); UnifiedJedis unifiedJedis = new UnifiedJedis(provider); return unifiedJedis; } }
package com.redisStream.pojo;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class NewKeyInfo {
private String key;
private String value;
}
package com.redisStream.controller;
import com.alibaba.fastjson.JSON;
import com.redisStream.pojo.NewKeyInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.commands.ProtocolCommand;
import redis.clients.jedis.json.JsonSetParams;
import redis.clients.jedis.search.Document;
import redis.clients.jedis.search.Query;
import redis.clients.jedis.search.SearchResult;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@RestController
public class ChineseController {
private static final Logger log = LoggerFactory.getLogger(ChineseController.class);
@Autowired
private UnifiedJedis jedis;
private String key_prefix = "ch:";
@PostMapping("/add")
public String add(@RequestBody NewKeyInfo newKeyInfo) throws Exception{
log.info("start to set value");
redisJsonUtils.setJson(key_prefix + newKeyInfo.getKey(), JSON.toJSONString(newKeyInfo));
log.info("start to read value");
return JSON.toJSONString(jedis.jsonGet(key_prefix + newKeyInfo.getCountryName()));
}
}
这个实例只显示了简单的set和get操作,在set成功后,会通过get命令将结果返回。
下次讨论RedisSearch。