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

扶醉桌前 提交于 2020-04-22 08:03:15

内容

  先介绍单机版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,欢迎关注。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!