Docker入门实践笔记(三)一篇文章搞懂Docker下安装Redis,以及Redis与SpringBoot整合

内容

  先介绍单机版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/

  Docker入门实践笔记(一)——安装Docker CE

  Linux入门实践笔记(三)——数据盘格式化和和多分区挂载

  spring boot整合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项目。

1540451706434

  Group和Artifact配置如下。

1540452619089

  选择SpringBoot的版本为1.5.x,并勾选Redis的依赖。

1540451800559

  选则项目所在路径。

1540452660787

  项目创建完毕,可以删除maven wrapper相关的文件和文件夹。

1540453560286

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,欢迎关注。