内容
先介绍单机版Redis镜像在Docker下的安装,然后在容器的Redis Shell中进行常用类型String、List、Set、Hash、SortedSet的增删改查操作测试,最后再结合SpringBoot项目进行简单的测试。后续会推出哨兵模式(Sentinel,一主二从三哨兵)和集群模式(Redis Cluster)的安装和部署,敬请关注。
版本
操作系统: CentOS 7.2 64位
Docker:17.12.1.ce
Redis: 3.2.12
适合人群
linux运维人员,docker运维人员,java人员
说明
转载请说明出处:Docker入门实践笔记(三)一篇文章搞懂Docker下安装Redis,以及Redis与SpringBoot整合
Demo源码托管:https://github.com/leo-zz/SpringBootDemo
参考
Docker官方文档:https://hub.docker.com/r/library/redis/
Redis 命令参考:http://redisdoc.com/
前提
服务器需要安装Docker CE,未安装的童鞋请参考 Docker入门实践笔记(一)——安装Docker CE ;
此示例将redis的缓存持久化到~/redis/data
路径下,如果需要分区挂载数据盘的童鞋,请参考Linux入门实践笔记(三)——数据盘格式化和和多分区挂载。
步骤
下载Redis镜像
使用docker pull
,从Docker仓库中下载Redis镜像,本示例下载的版本为3.2.12。使用docker images
查看已经下载的镜像信息。
#从Docker仓库中下载Redis镜像
[user1@iz8vb62snc6e5cage5yvz9z /]$ sudo docker pull redis:3.2.12
3.2.12: Pulling from library/redis
f17d81b4b692: Pull complete
b32474098757: Pull complete
8980cabe8bc2: Pull complete
58af19693e78: Pull complete
a977782cf22d: Pull complete
9c1e268980b7: Pull complete
Digest: sha256:7b0a40301bc1567205e6461c5bf94c38e1e1ad0169709e49132cafc47f6b51f3
Status: Downloaded newer image for redis:3.2.12
#查看已经下载的镜像信息
[user1@iz8vb62snc6e5cage5yvz9z home]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 3.2.12 87856cc39862 8 days ago 76MB
配置redis.conf
将redis.conf拷贝到/home/user1/redis/config
路径下,可以修改此文件以配置Redis;本示例使用默认配置。
[user1@iz8vb62snc6e5cage5yvz9z config]$ ls
redis.conf
[user1@iz8vb62snc6e5cage5yvz9z config]$ pwd
/home/user1/redis/config
创建Redis容器
使用docker run
创建Redis容器,然后使用docker ps
查看容器的运行情况。
[user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker run -p 6379:6379
-v /home/user1/redis/config:/usr/local/etc/redis/redis.conf -v /home/user1/redis/data:/data --name jmsRedis
-d redis:3.2.12 redis-server /usr/local/etc/redis/redis.conf 14d9c846b6586953c9528a0d6cbfe3257f4a936892e8d8778260a7aaf62b79c7 [user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14d9c846b658 redis:3.2.12 "docker-entrypoint.s…" 15 seconds ago Up 15 seconds 0.0.0.0:6379->6379/tcp jmsRedis
创建容器的参数释义:
-p是指定容器到宿主机的端口映射,这里使用的是Redis的默认端口号6379,映射为宿主机的6379。
-v是指定容器到宿主机的文件映射,这里将Redis容器的持久化数据存放路径/data映射到宿主机的/home/user1/redis/data;将Redis的配置文件/usr/local/etc/redis/redis.conf映射到宿主机的/home/user1/redis/config。
--name是指定容器的名称为jmsRedis。
-d表示在后台启动Redis。
redis-server /usr/local/etc/redis/redis.conf 表示启动指定配置文件的Redis。
容器内测试
执行docker exec进入容器,由于容器中安装了Redis,故可以执行redis-cli命令进入Redis shell。
[user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker exec -it 14d9c846b658 /bin/bash
root@14d9c846b658:/data# redis-cli -h localhost -p 6379
localhost:6379>
测试String类型数据的增删改查
演示Redis中String类型数据的的设置set、查询get、删除del操作。
#将字符串值 value 关联到 key 。如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 #对于带有生存时间(TTL)的键来,当SET命令成功在这个键上执行时,这个键原有的TTL将被清除。 #时间复杂度:O(1);返回值:设置操作成功完成时,返回 OK. localhost:6379> set hello leo OK #返回 key 所关联的字符串值。当 key 不存在时,返回 nil ,否则,返回 key 的值。 #如果 key 不是字符串类型,那么返回一个错误。时间复杂度:O(1) localhost:6379> get hello "leo" #删除给定的一个或多个 key 。不存在的 key 会被忽略。返回值:被删除 key 的数量。 #时间复杂度:O(N), N 为被删除的 key 的数量。删除单个字符串类型的 key ,时间复杂度为O(1)。删除单个列表、集合、有序集合或哈希表类型的 key ,
时间复杂度为O(M), M 为以上数据结构内的元素数量。 localhost:6379> del hello (integer) 1
llocalhost:6379> get hello (nil)
测试List类型数据的增删改查
演示Redis中List类型数据的的插入rpush、范围查询lrange、按下标查询lindex、移除头元素lpop操作。
#将一个或多个值 value 插入到列表 key 的表尾(最右边),索引依次递增。
#时间复杂度:O(1),返回值:rpush后list的长度
localhost:6379> rpush fruit apple
(integer) 1
localhost:6379> rpush fruit banana pear orange
(integer) 4
#返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。下标(index)参数 start 和 stop 都以 0 为底,
也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素;也可以使用负数下标,以 -1 表示列表的最后一个元素,
-2 表示列表的倒数第二个元素,以此类推。#时间复杂度:O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。遍历1次链表拿取所有范围内的数据。
localhost:6379> lrange fruit 0 -1
1) "apple"
2) "banana"
3) "pear"
4) "orange"
#返回列表 key 中,下标为 index 的元素。 时间复杂度O(N),需要遍历链表。
localhost:6379> lindex fruit 2
"pear"
#移除并返回列表 key 的头元素。当 key 不存在时,返回 nil 。
#时间复杂度:O(1),头元素是指index最小的元素。所有元素的index会随着pop操作更新
localhost:6379> lpop fruit
"apple"
localhost:6379> lrange fruit 0 -1
1) "banana"
2) "pear"
3) "orange"
测试SET类型数据的增删改查
演示Redis中Set类型数据的的插入sadd、集合查询smembers、判断成员是否存在sismember、移除成员srem操作。
#将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
#假如 key 不存在,则创建一个只包含 member 元素作成员的集合。当 key 不是集合类型时,返回一个错误。
#时间复杂度:O(N), N 是被添加的元素的数量。返回值:被添加到集合中的新元素的数量,不包括被忽略的元素。
localhost:6379> sadd animal tiger
(integer) 1
localhost:6379> sadd animal panda
(integer) 1
localhost:6379> sadd animal lion
(integer) 1
localhost:6379> sadd animal fish
(integer) 1
localhost:6379> sadd animal lion
(integer) 0
#返回集合 key 中的所有成员。不存在的 key 被视为空集合。
#时间复杂度:O(N), N 为集合的基数。
localhost:6379> smembers animal
1) "panda"
2) "tiger"
3) "fish"
4) "lion"
#判断 member 元素是否集合 key 的成员。时间复杂度:O(1)
#如果 member 元素是集合的成员,返回 1 。如果 member 元素不是集合的成员,或 key 不存在,返回 0 。
localhost:6379> sismember animal panda
(integer) 1
#移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
#时间复杂度:O(N), N 为给定 member 元素的数量。
#返回值:被成功移除的元素的数量,不包括被忽略的元素。当 key 不是集合类型,返回一个错误。
localhost:6379> srem animal panda
(integer) 1
localhost:6379> sismember animal panda
(integer) 0
测试HASH类型数据的增删改查
演示Redis中Hash类型数据的的插入hset、集合查询hgetall、根据Key查询hget、移除hdel操作。
#将哈希表 key 中的域 field 的值设为 value 。如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
#如果域 field 已经存在于哈希表中,旧值将被覆盖。
#返回值:如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。时间复杂度:O(1)
localhost:6379> hset phones mi8 xiaomi
(integer) 1
localhost:6379> hset phones v10 honor
(integer) 1
localhost:6379> hset phones pro2 smartisan
(integer) 1
localhost:6379> hset phones nex vivo
(integer) 1
#返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
#时间复杂度:O(N), N 为哈希表的大小。
localhost:6379> hgetall phones
1) "mi8"
2) "xiaomi"
3) "v10"
4) "honor"
5) "pro2"
6) "smartisan"
7) "nex"
8) "vivo"
#删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。返回值:被成功移除的域的数量.
#时间复杂度:O(N), N 为要删除的域的数量。
localhost:6379> hdel phones pro2
(integer) 1
localhost:6379> hdel phones pro2
(integer) 0
#返回哈希表 key 中给定域 field 的值。时间复杂度:O(1);返回值:给定域的值,若不存在则返回nil。
localhost:6379> hget phones pro2
(nil)
localhost:6379> hget phones mi8
"xiaomi"
localhost:6379> hgetall phones
1) "mi8"
2) "xiaomi"
3) "v10"
4) "honor"
5) "nex"
6) "vivo"
测试Sorted set类型数据的增删改查
演示Redis中Hash类型数据的的插入zadd、范围查询zrange、限定score的范围查询zrangebyscore、移除zrem操作。
#Sorted Set新增/删除一个元素的复杂度为log(N),实现的数据结构感觉像是二叉树
#将一个或多个 member 元素及其 score 值加入到有序集 key 当中。score 值可以是整数值或双精度浮点数。
#如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
#时间复杂度:O(M*log(N)), N 是有序集的基数, M 为成功添加的新成员的数量。
#返回值:被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。
localhost:6379> zadd zphones 1598 vivoZ3
(integer) 1
localhost:6379> zadd zphones 1599 oppoK1
(integer) 1
localhost:6379> zadd zphones 2499 XiaoMi8
(integer) 1
localhost:6379> zadd zphones 1299 SmartisanPro2
(integer) 1
#返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递增(从小到大)来排序。具有相同 score 值的成员按字典序(lexicographical order )来排列。
#以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,支持负数下标,-1 表示最后一个成员,-2 表示倒数第二个成员
#时间复杂度:O(log(N)+M), N 为有序集的基数,而 M 为结果集的基数。
localhost:6379> zrange zphone 0 -1
(empty list or set)
localhost:6379> zrange zphones 0 -1
1) "SmartisanPro2"
2) "vivoZ3"
3) "oppoK1"
4) "XiaoMi8"
localhost:6379> zrange zphones 0 -1 withscores
1) "SmartisanPro2"
2) "1299"
3) "vivoZ3"
4) "1598"
5) "oppoK1"
6) "1599"
7) "XiaoMi8"
8) "2499"
#返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
#可选的 LIMIT 参数指定返回结果的数量及区间,注意当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。
#可选的 WITHSCORES 参数决定结果集是单单返回有序集的成员,还是将有序集成员及其 score 值一起返回。
#min 和 max 可以是-inf和+inf,可以在不知道有序集的最低和最高score值的情况下使用
localhost:6379> zrangebyscore zphones 999 1599 withscores
1) "SmartisanPro2"
2) "1299"
3) "vivoZ3"
4) "1598"
5) "oppoK1"
6) "1599"
#移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。当 key 存在但不是有序集类型时,返回一个错误。
#时间复杂度:O(M*log(N)), N 为有序集的基数, M 为被成功移除的成员的数量。
#返回值:被成功移除的成员的数量
localhost:6379> zrem zphones SmartisanPro2
(integer) 1
localhost:6379> zrange zphones 0 -1 withscores
1) "vivoZ3"
2) "1598"
3) "oppoK1"
4) "1599"
5) "XiaoMi8"
6) "2499"
搭建SpringBoot测试项目
创建SpringBoot项目
通过IDEA中的Spring Initializr创建SpringBoot项目。
Group和Artifact配置如下。
选择SpringBoot的版本为1.5.x,并勾选Redis的依赖。
选则项目所在路径。
项目创建完毕,可以删除maven wrapper相关的文件和文件夹。
POM
在创建项目中已经勾选了Redis相关的依赖,不需要再在POM文件中添加其他依赖
<!-- 可以看到Spring Initailizer已经帮我们引入了redis的依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yaml
spring:
redis:
host: 192.168.1.25 #redis服务器的IP地址
port: 6379 #redis监听的端口号
Redis配置类
@Configuration
public class RedisConfig {
/**
* 注入 RedisConnectionFactory
*/
@Autowired
RedisConnectionFactory redisConnectionFactory;
/**
* 实例化 RedisTemplate 对象
*
*/
@Bean
public RedisTemplate<String, Object> createRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
initializeRedisTemplate(redisTemplate, redisConnectionFactory);
return redisTemplate;
}
/**
* 设置数据存入 redis 的序列化方式
*
*/
private void initializeRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory redisConnectionFactory) {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
}
/**
* 实例化 HashOperations 对象,可以使用 Hash 类型操作
*
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* 实例化 ValueOperations 对象,可以使用 String 操作
*
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* 实例化 ListOperations 对象,可以使用 List 操作
*
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}
/**
* 实例化 SetOperations 对象,可以使用 Set 操作
*
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* 实例化 ZSetOperations 对象,可以使用 ZSet 操作
*
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}
Redis工具类
本示例主要进行Redis中Hash类型数据的操作。
@Component
public class RedisHashUtil<T> {
@Autowired
protected RedisTemplate<String, Object> redisTemplate;
@Resource
protected HashOperations<String, String, T> hashOperations;
private String getRedisKey() {
return "REDIS_DEMO";
}
/**
* 添加
*
* @param key key
* @param value 对象
* @param expire 过期时间(单位:秒),传入 -1 时表示不设置过期时间
*/
public void put(String key, T value, long expire) {
hashOperations.put(getRedisKey(), key, value);
if (expire != -1) {
redisTemplate.expire(getRedisKey(), expire, TimeUnit.SECONDS);
}
}
/**
* 删除
*
* @param key 传入key的名称
*/
public void remove(String key) {
hashOperations.delete(getRedisKey(), key);
}
/**
* 查询
*
* @param key 查询的key
* @return
*/
public T get(String key) {
return hashOperations.get(getRedisKey(), key);
}
/**
* 获取当前redis库下所有value
*
* @return
*/
public List<T> getAll() {
return hashOperations.values(getRedisKey());
}
/**
* 查询查询当前redis库下所有key
*
* @return
*/
public Set<String> getKeys() {
return hashOperations.keys(getRedisKey());
}
/**
* 判断key是否存在redis中
*
* @param key 传入key的名称
* @return
*/
public boolean isKeyExists(String key) {
return hashOperations.hasKey(getRedisKey(), key);
}
/**
* 查询当前key下缓存数量
*
* @return
*/
public long count() {
return hashOperations.size(getRedisKey());
}
/**
* 清空redis
*/
public void clear() {
Set<String> set = hashOperations.keys(getRedisKey());
set.stream().forEach(key -> hashOperations.delete(getRedisKey(), key));
}
}
测试类
使用Redis中HASH类型数据结构存储key-value类型的缓存。
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisdemoApplicationTests {
@Autowired
RedisHashUtil<String> redisHashUtil;
@Test
public void firstRedisTest() {
System.out.println("***********************测试向Redis插入数据");
redisHashUtil.put("Mi8","XiaoMi",-1);
redisHashUtil.put("V10","Honor",-1);
redisHashUtil.put("Pro2","Smartisan",-1);
redisHashUtil.put("NEX","VIVO",-1);
System.out.println("**********************测试从Redis读取数据,以及查询数据总数");
long count = redisHashUtil.count();
List<String> all = redisHashUtil.getAll();
System.out.println("遍历hash中的value,共计"+redisHashUtil.count()+"个品牌。分别为:");
for (String s:all) {
System.out.print(s+" ");
}
System.out.println("");
System.out.println("***********************测试Redis中是否存在指定数据");
if(redisHashUtil.isKeyExists("Pro2")){
String pro2 = redisHashUtil.get("Pro2");
System.out.println("型号为Pro2的手机存在,其品牌为:"+pro2);
}
System.out.println("***********************测试从Redis中删除数据");
redisHashUtil.remove("Pro2");
if(!redisHashUtil.isKeyExists("Pro2")){
System.out.println("型号为Pro2的手机被移除了");
}
System.out.print("剩余型号还有:");
redisHashUtil.getKeys().forEach(key-> System.out.print(key+" "));
System.out.println("");
System.out.println("***********************测试清空Redis中的hash数据");
redisHashUtil.clear();
System.out.println("所有手机型号都被清空,剩余:"+redisHashUtil.count()+"个");
}
}
测试结果
***********************测试向Redis插入数据
**********************测试从Redis读取数据,以及查询数据总数
遍历hash中的value,共计4个品牌。分别为:
XiaoMi Honor Smartisan VIVO
***********************测试Redis中是否存在指定数据
型号为Pro2的手机存在,其品牌为:Smartisan
***********************测试从Redis中删除数据
型号为Pro2的手机被移除了
剩余型号还有:Mi8 V10 NEX
***********************测试清空Redis中的hash数据
所有手机型号都被清空,剩余:0个
看到这的老铁都是真爱,源码已托管到GitHub,欢迎关注。