Spring Boot 技术探索

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

26、Spring Boot之Redis的使用

平台环境:

名称

版本号

Mac OS X

10.15.1

JDK

1.8.0_201

Apache Maven

3.6.0

IntelliJ IDEA

2019.2.4 (Ultimate Edition)

Spring Boot

2.1.9.RELEASE

Redis

stable 5.0.6 (bottled), HEAD

 

  什么是Redis?

  Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

  Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多。且都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

 

Redis服务端的安装

直接下载安装方式直接参考这个网址就行了。

http://docs.redisdesktop.com/en/latest/install/

 

通过homebrew安装

brew install redis

 

常用命令

启动

brew services start redis

 

停止

brew services stop redis

 

如果不想后台服务模式,就这样启动:

redis-server /usr/local/etc/redis.conf

 

查看运行状态

ps -ef|grep redis

 

强行关闭

强行终止redis进程可能会导致数据丢失,因为redis可能正在将内存数据同步到硬盘中。

ps axu|grep redis ## 查找redis-server的PID
kill -9 PID

 

命令关闭

向redis发送SHUTDOWN命令,即 redis-cli SHUTDOWN 。Redis收到命令后,服务端会断开所有客户端的连接,然后根据配置执行持久化,最后退出。

## 关闭服务器
AT8775:redis shoren$ redis-cli shutdown
##关闭成功
AT8775:redis shoren$ ps axu|grep redis shoren 14952 0.0 0.0 2435864 772 s000 S+ 10:19上午 0:00.01 grep redis

 

启动客户端

默认启动
使用命令redis-cli启动客户端,按照默认配置连接Redis(127.0.0.1:6379)。
指定地址和端口号
使用命令 redis-cli -h 127.0.0.1 -p 6379

 

客户端常用命令

keys * 查看所有键值
set (key) (value) 设置键key的值为value
append (key) (value2) 在键key的值后面加上value2
get (key) 查看键key的值

set a 123;//设置缓存:a=>123
EXPIRE a 3600;//设置缓存时间(秒)
TTL a;//查看缓存剩余时间

flushall //执行该命令后会清空redis服务器的所有缓存,一般用于应急处理,不应该作为常用命令

 

关闭客户端

quit

 

到此,服务端就安装配置好了。


 

通过Spring Boot操作Redis

pom.xml加入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

 

commons-pool2是lettuce-core的可选依赖,而Lettuce是Redis的三个框架Jedis、Redisson、Lettuce之一。由于spring-boot-starter-data-redis默认采用了Lettuce框架,所以这里也就使用Lettuce,其他的留作以后研究。

 

具体依赖关系可以这样看到:

command+鼠标左键,打开spring-boot-starter-data-redis依赖,往下翻可以找到lettuce-core依赖。

<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>5.1.8.RELEASE</version>
  <scope>compile</scope>
</dependency>

 

同样方式打开lettuce-core依赖,可以找到commons-pool2依赖。可以看到它的optional属性是true。

<!-- Start of optional dependencies -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
    <optional>true</optional>
</dependency>

 

application.properties中加入配置信息

# REDIS
# Redis数据库索引(默认为0)
spring.redis.database=0  
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0

 

新建Redis全局配置文件RedisConfig

package com.example.demo.config;


import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import java.lang.reflect.Method;


@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
    @Bean
    public KeyGenerator keyGenerator()
    {
        // 自定义主键的生产策略KeyGenerator,默认使用参数名作为主键。
        return new KeyGenerator()
        {
            @Override
            public Object generate(Object target, Method method, Object... params)
            {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params)
                {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

 

到此,Redis的配置就全部完成了。


 

接下来测试:

新建Model类User

package com.example.demo.model;


import java.io.Serializable;




public class User implements Serializable
{


    private static final long serialVersionUID = 1L;
    private Long id;
    private String userName;
    private String password;
    private String email;
    private String nickname;
    private String regTime;


    public User()
    {
        super();
    }


    public User(String email, String nickname, String password, String userName, String regTime)
    {
        super();
        this.email = email;
        this.nickname = nickname;
        this.password = password;
        this.userName = userName;
        this.regTime = regTime;
    }


    public Long getId()
    {
        return id;
    }


    public void setId(Long id)
    {
        this.id = id;
    }


    public String getUserName()
    {
        return userName;
    }


    public void setUserName(String userName)
    {
        this.userName = userName;
    }


    public String getPassword()
    {
        return password;
    }


    public void setPassword(String password)
    {
        this.password = password;
    }


    public String getEmail()
    {
        return email;
    }


    public void setEmail(String email)
    {
        this.email = email;
    }


    public String getNickname()
    {
        return nickname;
    }


    public void setNickname(String nickname)
    {
        this.nickname = nickname;
    }


    public String getRegTime()
    {
        return regTime;
    }


    public void setRegTime(String regTime)
    {
        this.regTime = regTime;
    }


    @Override
    public String toString()
    {
        return "User{" + "id=" + id + ", userName='" + userName + "', password='" + password + "', email='" + email + "', nickname='" + nickname + "', regTime='" + regTime + "'}";
    }
}

 

新建测试类TestRedisTemplate

package com.example.demo;


import com.example.demo.model.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;


import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRedisTemplate
{
    @Autowired
    private RedisTemplate redisTemplate;


    @Test
    public void testString()
    {
        // 测试保存、获取获取String类型
        // 两次set同样的key则value会覆盖
        redisTemplate.opsForValue().set("key1", "welcome");
        redisTemplate.opsForValue().set("key1", "codelabx");
        System.out.println(redisTemplate.opsForValue().get("key1"));
        Assert.assertEquals("codelabx", redisTemplate.opsForValue().get("key1"));
    }


    @Test
    public void testObj()
    {
        // 测试保存、获取一个对象类型
        User user = new User("admin@codelabx.com", "welcome", "to", "codelabx", "2019-11-06");
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        operations.set("com.codelabx", user);
        User u = operations.get("com.codelabx");
        System.out.println("user: " + u.toString());
    }


    @Test
    public void testExpire() throws InterruptedException
    {
        User user = new User("admin@codelabx.com", "welcome", "to", "codelabx", "2019-11-06");
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        operations.set("expire", user, 100, TimeUnit.MILLISECONDS); // 保存数据时,手工指定过期时间100毫秒
        Thread.sleep(1000); // 线程休息1000毫秒
        boolean exists = redisTemplate.hasKey("expire");
        if (exists)
        {
            System.out.println("exists is true");
        }
        else
        {
            System.out.println("exists is false");
        }
    }


    @Test
    public void testDelete()
    {
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        redisTemplate.opsForValue().set("codelabx", "welcome");
        redisTemplate.delete("codelabx"); // 删除数据
        boolean exists = redisTemplate.hasKey("codelabx");
        if (exists)
        {
            System.out.println("exists is true");
        }
        else
        {
            System.out.println("exists is false");
        }
    }


    @Test
    public void testHash()
    {
        String key = "key";
        redisTemplate.delete(key);


        // Redis存储一个key会有一个最小内存,不管你存的这个key的数据量多小都不会低于这个内存,因此合理的使用Hash可以帮我们节省很多内存。
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, "hashKey1", "hashValue1"); // 参数1:Redis存储的主键。参数2:哈希数据的key。参数3:哈希数据key对应的value
        hash.put(key, "hashKey2", "hashValue2"); // 这样一组哈希数就可以共用同一个Redis主键来存储


        String value1 = (String) hash.get(key, "hashKey1");
        String value2 = (String) hash.get(key, "hashKey2");
        System.out.println("hash value1 :" + value1);
        System.out.println("hash value2 :" + value2);
    }




    @Test
    public void testList()
    {
        String key = "list";
        redisTemplate.delete(key);


        ListOperations<String, String> list = redisTemplate.opsForList();
        list.leftPush(key, "value1");
        list.leftPush(key, "value2");
        list.leftPush(key, "value3");


        // 移除并返回list中传入的参数key对应的第一个元素
        String value = (String) list.leftPop(key);
        System.out.println("list leftPop value :" + value.toString());


        // 返回传入索引位置的元素(第一个是0)
        List<String> values = list.range(key, 0, 1);
        for (String v : values)
        {
            System.out.println("list range :" + v);
        }
    }




    @Test
    public void testSet()
    {
        String key = "set";
        redisTemplate.delete(key);


        // 存入 读取set类型
        SetOperations<String, String> set = redisTemplate.opsForSet();
        set.add(key, "value1");
        set.add(key, "value2");
        set.add(key, "value2");
        set.add(key, "value3");


        Set<String> values = set.members(key);
        for (String v : values)
        {
            System.out.println("set value :" + v);
        }
    }


    @Test
    public void testSetMore()
    {
        String key1 = "setMore1";
        String key2 = "setMore2";
        redisTemplate.delete(key1);
        redisTemplate.delete(key2);


        SetOperations<String, String> set = redisTemplate.opsForSet();
        set.add(key1, "value1");
        set.add(key1, "value2");
        set.add(key1, "value3");
        set.add(key1, "value4");
        set.add(key2, "value1");
        set.add(key2, "valueXXX");
        set.add(key2, "value2");


        // 返回set集合中传入key1对应的值中有哪些不同于key2对应的值
        Set<String> diffs = set.difference(key1, key2);
        for (String v : diffs)
        {
            System.out.println("diffs set value :" + v);
        }


        String key3 = "setMore3";
        String key4 = "setMore4";
        redisTemplate.delete(key3);
        redisTemplate.delete(key4);


        set.add(key3, "value1");
        set.add(key3, "value2");
        set.add(key3, "value3");
        set.add(key4, "value1");
        set.add(key4, "value2");
        set.add(key4, "valueXXX");


        // 返回set集合中传入的key1与key2对应的所有的值(自动去除重复)
        Set<String> unions = set.union(key3, key4);
        for (String v : unions)
        {
            System.out.println("unions value :" + v);
        }
    }




    @Test
    public void testZset()
    {
        String key = "zset";
        redisTemplate.delete(key);
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        zset.add(key, "value1", 1);
        zset.add(key, "value2", 6);
        zset.add(key, "value3", 4);
        zset.add(key, "value4", 3);


        // 返回传入索引位置的元素(第一个是0)
        Set<String> zsets = zset.range(key, 0, 3);
        for (String v : zsets)
        {
            System.out.println("zset range value :" + v);
        }


        // 返回位于传入最大最小参数之间的Score值对应的元素
        Set<String> zsetB = zset.rangeByScore(key, 0, 4);
        for (String v : zsetB)
        {
            System.out.println("zset rangeByScore value :" + v);
        }
    }


}

 

总结一个Redis工具类

package com.example.demo.service;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;


import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@Service
public class RedisService
{
    private Logger logger = LoggerFactory.getLogger(RedisService.class);
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * set value
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value)
    {
        boolean result = false;
        try
        {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e)
        {
            logger.error("set error: key {}, value {}", key, value, e);
        }
        return result;
    }


    /**
     * set value with expireTime
     *
     * @param key
     * @param value
     * @param expireTime
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime)
    {
        boolean result = false;
        try
        {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e)
        {
            logger.error("set error: key {}, value {},expireTime {}", key, value, expireTime, e);
        }
        return result;
    }


    /**
     * @param key
     * @return
     */
    public boolean exists(final String key)
    {
        return redisTemplate.hasKey(key);
    }


    /**
     * @param key
     * @return
     */
    public Object get(final String key)
    {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }


    /**
     * remove single key
     *
     * @param key
     */
    public void remove(final String key)
    {
        if (exists(key))
        {
            redisTemplate.delete(key);
        }
    }


    /**
     * batch delete
     *
     * @param keys
     */
    public void remove(final String... keys)
    {
        for (String key : keys)
        {
            remove(key);
        }
    }


    /**
     * batch delete with pattern
     *
     * @param pattern
     */
    public void removePattern(final String pattern)
    {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0)
        {
            redisTemplate.delete(keys);
        }
    }


    /**
     * hash set
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hashSet(String key, Object hashKey, Object value)
    {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }


    /**
     * hash get
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hashGet(String key, Object hashKey)
    {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }


    /**
     * list push
     *
     * @param k
     * @param v
     */
    public void push(String k, Object v)
    {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }


    /**
     * list range
     *
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> range(String k, long l, long l1)
    {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }


    /**
     * set add
     *
     * @param key
     * @param value
     */
    public void setAdd(String key, Object value)
    {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }


    /**
     * set get
     *
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key)
    {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }


    /**
     * ordered set add
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure)
    {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }


    /**
     * rangeByScore
     *
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1)
    {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }
}

 

参考资料:

https://www.cnblogs.com/liyan492/p/9858548.html

 

Bootstrap Thumbnail Second
MySQL

MySQL is the world's most popular open source database.

GO

Bootstrap Thumbnail Third
算法基础

本书介绍了什么是计算机算法,如何描述它们,以及如何来评估它们。

GO