1、安装
开发环境使用docker来安装redis。
- 首先,搜索redis的官方镜像
docker search redis
搜索结果的第一行就是了:
[root@fedora ~]# docker search redis
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 7815 [OK]
bitnami/redis Bitnami Redis Docker Image 136 [OK]
sameersbn/redis 79 [OK]
grokzen/redis-cluster Redis cluster 3.0, 3.2, 4.0 & 5.0 63
rediscommander/redis-commander Alpine image for redis-commander - Redis man… 34 [OK]
kubeguide/redis-master redis-master with "Hello World!" 31
redislabs/redis Clustered in-memory database engine compatib… 24
redislabs/redisearch Redis With the RedisSearch module pre-loaded… 20
arm32v7/redis Redis is an open source key-value store that… 20
oliver006/redis_exporter Prometheus Exporter for Redis Metrics. Supp… 18
webhippie/redis Docker images for Redis 10 [OK]
redislabs/redisgraph A graph database module for Redis 9 [OK]
s7anley/redis-sentinel-docker Redis Sentinel 9 [OK]
bitnami/redis-sentinel Bitnami Docker Image for Redis Sentinel 9 [OK]
insready/redis-stat Docker image for the real-time Redis monitor… 9 [OK]
arm64v8/redis Redis is an open source key-value store that… 8
redislabs/redismod An automated build of redismod - latest Redi… 6 [OK]
centos/redis-32-centos7 Redis in-memory data structure store, used a… 4
circleci/redis CircleCI images for Redis 3 [OK]
frodenas/redis A Docker Image for Redis 2 [OK]
tiredofit/redis Redis Server w/ Zabbix monitoring and S6 Ove… 1 [OK]
runnable/redis-stunnel stunnel to redis provided by linking contain… 1 [OK]
wodby/redis Redis container image with orchestration 1 [OK]
cflondonservices/redis Docker image for running redis 0
xetamus/redis-resource forked redis-resource 0 容器 [OK]
- 安装redis容器
redis是一个内存key-value数据库。使用如下命令默认安装,是安装没有启用存储的实例:
$ docker run --name some-redis -d redis
从 redis 镜像创建一个名字叫 “some-redis” 的容器,不启用存储。
如果要安装有存储的实例,则参照如下命令:
docker run -v /docker/redis/conf:/usr/local/etc/redis -v /docker/redis/data:/data -p 6379:6379 --name redis -d redis redis-server --appendonly yes
- --name redis -d redis: 从镜像 redis 创建容器,名字也叫redis
- -v /docker/redis/conf:/usr/local/etc/redis: 这是将本地的 /docker/redis/conf 目录映射到redis容器的 /usr/local/etc/redis 目录;后者是redis配置文件所在目录
- -v /docker/redis/data:/data 将本地的 /docker/redis/data 目录映射到 redis容器的 /data 目录,这是redis容器存储数据的目录
- redis-server
- --appendonly yes: 使用AOF(日志追加模式)存储数据。
关于redis的 RDB存储模式和AOF存储模式的优缺点,就不在这里描述了。大家自行阅读官网文章《Redis Persistence》。
2、Redis与Memcache的简单对比
Redis 和 Memcache 都是key-value高速存储,他们有类似也有差异。简单来说如下:
Redis | Memcache | |
---|---|---|
类型 | key-value 内存数据库,支持存储 | key-value 内存缓存,不能存储 |
灾难恢复 | 支持,有RBD模式和AOF模式存储,节点挂掉可以恢复 | 不支持,节点挂掉数据就丢失了 |
主从复制 | 支持 | 本身不支持,但有第三方的magent来实现 |
数据结构 | string、hash、list、set、sorted set | 字符串、图片、文件、视频 |
内存使用 | 实时申请内存,支持虚拟内存,内存用完后可交换数据到磁盘 | 根据配置申请内存,不能超过内存限制 |
数据一致性 | 支持简单事务,实际上并不可靠 | 通过CAS确保数据一致 |
性能对比 | 使用单核,单条数据100k以下时性能好于memcache | 使用多核,单条数据超过100k时性能好于Redis |
3、Redis支持的数据结构
这里主要介绍 redis 的每种数据结构和其支持的主要操作,并针对每种主要操作,基于spring-data-redis提供的默认 StringRedisTemplate 进行操作举例。
由于 RedisTemplate 默认假设使用的 key 和 value,都是 Object,其提供的所有接口,都自动进行了 redis 中字符串到对象的转换。
但实际上,个人并不推荐直接在存取 redis 时依赖 RedisTemplate 进行转换,而是 key 和 value 都作为字符串(其在 redis 中时的原始的样子)来存取。至于这个字符串在业务上是否是一个对象序列化而来,由业务来处理。RedisTemplate 作为一个操作 Redis 的工具,还是不要涉及这种业务行为为好。
好在 spring-boot + spring-data-redis 的环境中,spring-boot 默认除了提供基于Object 的 RedisTemplate 的实例 redisTemplate 外,还提供了基于 string 的 StringRedisTemplate 的实例 stringRedisTemplate。后面的所有例子,都是基于 StringRedisTemplate 来实现的。
3.1 字符串格式
缓存数据是一个字符串。一个key对应一个value。
在memcached中,所有的值都是字符串。如果要缓存对象,则需要在存储前,先将对象序列化为字符串再缓存;在取出后,先将数据组装成对象后再使用。
在redis中,我们仍然可以继续这么做。只不过,redis有hash类型,可以更好的支持缓存对象。
下面使用 spring-boot 的默认 stringRedisTemplate 来操作字符串数据。
- 读取key对应的字符串 (redis指令get):对应值存在,则返回;对应值不存在,则返回 null
public String get(String key) {
ValueOperations<String, String> opsForValue = this.stringRedisTemplate.opsForValue();
return Optional
.ofNullable(opsForValue.get(key))
.orElse("");
}
- 读取多个key对应的字符串 (redis指令mget):返回List,结果的顺序和key的顺序一样
public List<String> multiGet(List<String> keys) {
ValueOperations<String, String> opsForValue = this.stringRedisTemplate.opsForValue();
return Optional
.ofNullable(opsForValue.multiGet(keys))
.orElse(Collections.emptyList());
}
- 缓存字符串(redis 指令set):对应key存在,则将缓存的值直接更新为给定的新值;对应key不存在,则新增。
public void set(String key, String value) {
this.stringRedisTemplate.opsForValue().set(key, value);
}
- 缓存字符串并指定过期时间 (redis指令set):set 的重载方法,只是多个了指定过期时长的 Duration 参数。
public void set(String key, String value, int expire) {
this.redisTemplate.opsForValue().set(key, value, Duration.of(expire, ChronoUnit.SECONDS));
}
- 如果不存在则缓存 setIfAbsent:顾名思义,只有对应key不存在时,才写入缓存。
public void setIfAbsent(String key, String value) {
this.redisTemplate.opsForValue().setIfAbsent(key, value);
}
- 缓存多个字符串 multiSet:使用 Map 作为参数,同时缓存多个字符串。
public void multiSet(Map<String, String> values) {
ValueOperations<String, String> opsForValue = this.stringRedisTemplate.opsForValue();
opsForValue.multiSet(values);
}
- 返回旧值并更新为新值(redis指令getset):先获取旧值,然后在更新为新值后,返回旧值。
public String getAndSet(String key, String value) {
ValueOperations<String, String> opsForValue = this.stringRedisTemplate.opsForValue();
return Optional
.ofNullable(opsForValue.getAndSet(key, value))
.orElse("");
}
3.2 哈希 hash
缓存的数据结构是一个hash,可以看作Java的一个HashMap。一个key对应的一个HashMap。
由于缓存的是一个HashMap,因此可以操作整个hash,也可以操作hash中某个hash key对应的值。
- HashMap 中的key,仍然是一个字符串,但 RedisTemplate 返回的是一个Object。这是因为这个key,可能也是一个对象的序列化字符串。为了简化操作,我们一般在对redis进行操作前,先将用作hash key的对象序列化为字符串了。
- redis对hash 中的 value只能是字符串,不再继续支持级联嵌套 hash了。我们可以将对象序列化为字符串后,作为 hash 的值处理,但无法再多一层。
下面使用 spring-boot 的默认 stringRedisTemplate 来操作 hash 数据。
3.2.1 涉及整个 hash 的操作如下:
- 获取key对应的整个hash:
public Map<String, String> get(String key) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return Optional
.ofNullable(opsForHash.entries(key))
.orElse(Collections.emptyMap());
}
- 获取 key 对应的 hash 中所有的列:
public List<String> getAll(String key, Collection<String> hashKeys) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return Optional
.ofNullable(opsForHash.values(key))
.orElse(Collections.emptyList());
}
- 获取 key 对应的 hash 中,多个 hash key 对应的列:
public List<String> multiGet(String key, Collection<String> hashKeys) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return Optional
.ofNullable(opsForHash.multiGet(key, hashKeys))
.orElse(Collections.emptyList());
}
- 缓存 key 对应的整个hash:将 entire 中的所有属性缓存到 key 对应的 hash 中去。
public void set(String key, Map<String, String> entire) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
opsForHash.putAll(key, entire);
}
- 获取 key 对应的 hash 中所有的 hash key:
public Set<String> keys(String key) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return Optional
.ofNullable(opsForHash.keys(key))
.orElse(Collections.emptySet());
}
3.2.2 涉及 hash 中某个值的操作如下:
- 获取 key 对应 hash 中某个 hash key 对应的值:key或 hash key 不存在时返回null
public String get(String key, String hashKey) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return Optional
.ofNullable(opsForHash.get(key, hashKey))
.orElse("");
}
- 缓存一个字符串到 key 对应的 hash 中 hash key 对应的列
public void set(String key, String hashKey, String value) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
opsForHash.put(key, hashKey, value);
}
- 仅当hash key对应的列不存在时缓存字符串
public void setIfAbsent(String key, String hashKey, String value) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
opsForHash.putIfAbsent(key, hashKey, value);
}
- 删除 key 对应的 hash 中 hash key 对应的列
public void delete(String key, String hashKey) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
opsForHash.delete(key, hashKey);
}
- 检查 key 对应的 hash 中某个 hash key 是否存在:
public boolean hasHashKey(String key, String hashKey) {
HashOperations<String, String, String> opsForHash = this.stringRedisTemplate.opsForHash();
return opsForHash.hasKey(key, hashKey);
}
网友评论