美文网首页
Redis入门

Redis入门

作者: 远方的橄榄树 | 来源:发表于2019-12-03 09:45 被阅读0次

一、工具

二、简介

  • Redis是完全开源免费,遵守BSD协议,一个高性能的key - value数据库。
  • Redis 的优势:
    1、支持数据持久化,可以将内存中的数据保存保存在磁盘中。
    2、不仅支持key - value的数据类型,还提供了list、set、zset、hash等数据结构的存储。
    3、极高的读写速率。
    4、Redis的所有操作都是原子性的,意思是要么成功执行、要么失败完全不执行。多个操作也支持事务。
  • Redis在web开发中主要用于存储缓存的数据。

三、Redis的基本操作

  • 启动和关闭redis
net start redis
net stop redis
  • 命令行访问和退出redis
D:\Redis>redis-cli
127.0.0.1:6379> exit
D:\Redis>
  • 访问redis并能正常显示中文
D:\Redis>redis-cli --raw

我是用 IDEA的terminal打开命令行的,如果用windows的cmd工具好像无法正常显示中文。

  • key 常用操作
127.0.0.1:6379> set name earth
OK
127.0.0.1:6379> type name # 判断value类型
string
127.0.0.1:6379> exists name # 判断key是否存在
(integer) 1
127.0.0.1:6379> del name # 删除key
(integer) 1
127.0.0.1:6379> exists name
(integer) 0

四、Redis的数据类型

Redis支持5种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set:有序集合)

  • string
127.0.0.1:6379> set name jarry
OK
127.0.0.1:6379> get name
jarry
string
  • list
    list内的元素是可重复的。
127.0.0.1:6379> rpush jobs teacher # 右边添加元素
1
127.0.0.1:6379> rpush jobs police
2
127.0.0.1:6379> lpush jobs doctor # 左边添加元素
3
127.0.0.1:6379> lpush jobs worker
4
127.0.0.1:6379> lrange jobs 0 -1 # 遍历所有值
worker
doctor
teacher
police
127.0.0.1:6379> lrange jobs 0 2 # 遍历序号为0~2的值
worker
doctor
teacher
127.0.0.1:6379> lpop jobs # 左边删除一个值
worker
127.0.0.1:6379> rpop jobs # 右边删除一个值
police
127.0.0.1:6379> lrange jobs 0 -1
doctor
teacher
list
  • set
    redis的set是字符串类型的无序集合。集合是通过哈希表实现的。因此添加、删除查找时间复杂度都是O(1)。
127.0.0.1:6379> sadd names mike
1
127.0.0.1:6379> sadd names jack
1
127.0.0.1:6379> sadd names lily
1
127.0.0.1:6379> sadd names mike
0
127.0.0.1:6379> sadd names tom
1
127.0.0.1:6379> smembers names
lily
mike
jack
tom
127.0.0.1:6379> sismember names tom
1
127.0.0.1:6379> sismember names jarry
0
set

zset

zset与set类似,都是字符串类型元素的集合,并且集合内元素不能重复。
不同的是,zset每个元素都会关联一个double类型的分数。redis通过分数来为集合中的成员进行从小到大的排序。
zset的元素是惟一的,但分数可以重复。

127.0.0.1:6379> zadd countries 1 China # 添加元素
(integer) 1
127.0.0.1:6379> zadd countries 3 America
(integer) 1
127.0.0.1:6379> zadd countries 8 Japan
(integer) 1
127.0.0.1:6379> zadd countries 5 England
(integer) 1
127.0.0.1:6379> zrange countries 0 -1 # 遍历
1) "China"
2) "America"
3) "England"
4) "Japan"
127.0.0.1:6379> zrange countries 0 2 withscores # 遍历member和score
1) "China"
2) "1"
3) "America"
4) "3"
5) "England"
6) "5"
127.0.0.1:6379> zadd countries 11 India
(integer) 1
127.0.0.1:6379> zadd countries 11 Canada 
(integer) 1
127.0.0.1:6379> zadd countries 12 Canada # 添加失败
(integer) 0
127.0.0.1:6379> zrange countries 0 -1 
1) "China"
2) "America"
3) "England"
4) "Japan"
5) "India"
6) "Canada"
127.0.0.1:6379> zrem countries America  # 删除
(integer) 1
127.0.0.1:6379> zrem countries Brazil  # 删除失败
(integer) 0
127.0.0.1:6379> zrange countries 0 -1
1) "China"
2) "England"
3) "Japan"
4) "India"
5) "Canada"
zset

hash

redis hash是一个键值对集合。
hash是一个string类型的key和value的映射表,hash特别适合存储对象。

127.0.0.1:6379> hset person name smallbear
(integer) 1
127.0.0.1:6379> hset person age 21
(integer) 1
127.0.0.1:6379> hset person gender man
(integer) 1
127.0.0.1:6379> hset person hobby acg
127.0.0.1:6379> hgetall person # 获取所有键值对
1) "name"
2) "smallbear"
3) "age"
4) "21"
5) "gender"
6) "man"
7) "hobby"
8) "acg"
127.0.0.1:6379> hget person gender 
"man"
127.0.0.1:6379> hdel person gender # 删除一个属性
(integer) 1
127.0.0.1:6379> hgetall person
1) "name"
2) "smallbear"
3) "age"
4) "21"
5) "hobby"
6) "acg"
hash

五、redis发布订阅

Redis发布订阅(subscribe / publish)是消息通信模式:发布者(pub)发布消息,订阅者(sub)接收消息。
redis客户端可以订阅任意数量的频道。


subscribe
publish
  • 创建订阅频道sub-channel
127.0.0.1:6379> subscribe sub-channel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "sub-channel"
3) (integer) 1
  • 开启一个新的redis客户端,并发送消息
127.0.0.1:6379> publish sub-channel hello,sub-channel
(integer) 1
127.0.0.1:6379> publish sub-channel "I publish some message to you."
(integer) 1
  • 订阅者的客户端显示如下
1) "message"
2) "sub-channel"
3) "hello,sub-channel"
1) "message"
2) "sub-channel"
3) "I publish some message to you."

六、redis事务

  • redis事务可以一次执行多个命令,其执行过程分为3个阶段
    1、开始事务
    2、命令入队
    3、执行事务
  • 事务有三个重要的保证
    1、批量操作在EXEC命令执行前会放到队列中缓存。
    2、收到EXEC命令后命令进入事务执行,事务中的任意命令执行失败,其余命令依然执行。
    3、在事务执行的过程中,其他客户端提交的命令不会插入到事务的执行命令中。
> multi # 开始事务
OK
> sadd heroes 亚索
QUEUED
> sadd heroes 瑞雯
QUEUED
> sadd heroes 提莫
QUEUED
> exec # 执行事务内的命令
1
1
1

七、spring boot集成redis

  • spring boot项目引入依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
  • 配置redisTemplate,指定hashValue/value的存储格式为json(默认为序列化)
@Configuration
public class RedisConfig {

    @Bean
    public Jackson2JsonRedisSerializer<Object> redisSerializer() {
        Jackson2JsonRedisSerializer<Object> redisSerializer =
                new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        redisSerializer.setObjectMapper(mapper);
        return redisSerializer;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory,
                                                       Jackson2JsonRedisSerializer<Object> jsonRedisSerializer) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}
  • 配置文件
#Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=3000
  • 添加实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User{
    private Long id;
    private String name;
    private Integer age;
}
  • 测试
@SpringBootTest
class RedisDemo2ApplicationTests {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    public void stringTest() {
        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
        opsForValue.set("hello", "world");
        System.out.println(opsForValue.get("hello")); // world
    }

    @Test
    public void objectTest() {
        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
        // 保存
        User user = new User(1L, "小明", 14);
        opsForValue.set("user:" + user.getId() , user);
        //获取
        User target = (User) opsForValue.get("user:1");
        System.out.println(target); // User(id=1, name=小明, age=14)
    }

    @Test
    public void listTest() {
        ListOperations<String, Object> opsForList = redisTemplate.opsForList();
        // 插入
        String key = "numbers";
        opsForList.rightPush(key, 4);
        opsForList.rightPush(key, 13);
        opsForList.rightPush(key, 31);
        opsForList.leftPush(key, -3);
        opsForList.leftPush(key, 100);
        // 遍历
        List numbers = opsForList.range(key, 0, -1);
        System.out.println(numbers); // [100, -3, 4, 13, 31]

        //删除
        opsForList.rightPop(key); // 删除最右边 31
        opsForList.remove(key, 2, -3); // 删除第二个元素 -3 若object与列表的元素不同,则删除失败
    }

    @Test
    public void hashTest() {
        HashOperations<String, String, Object> opsForHash = redisTemplate.opsForHash();
        String key = "pet";

        // 添加
        opsForHash.put(key, "name", "二哈");
        opsForHash.put(key, "age", 4);
        opsForHash.put(key, "type", "dog");
        opsForHash.put(key, "test", "test");

        //获取
        Map<String, Object> pet = opsForHash.entries("pet");
        System.out.println(pet); // {name=二哈, age=4, type=dog, test=test}

        opsForHash.delete(key, "test"); // 删除key为"test"的元素
    }
}
hello
user:1
numbers
pet

八、redis实现消息队列

  • 添加web依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  • 创建消息接受者
@Slf4j
public class Receiver {

    private CountDownLatch latch = new CountDownLatch(1);

    public void receive(String msg) {
        log.info("接收消息==>" + msg);
        latch.countDown();
    }

    public CountDownLatch latch() {
        return latch;
    }
}
  • 创建消息监听
@Configuration
public class MQConfig {

    @Bean
    public Receiver receiver() {
        return new Receiver();
    }

    @Bean
    public MessageListenerAdapter listenerAdapter() {
        return new MessageListenerAdapter(receiver(), "receive");
    }


    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
        return container;
    }
}
  • 测试
@RestController
@RequestMapping("mqTest")
public class MQController {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Receiver receiver;

    @GetMapping("sendMsg")
    public String sendMsg(@RequestParam("msg") String msg) {
        redisTemplate.convertAndSend("chat", msg);
        try {
            receiver.latch().await(1000, TimeUnit.MICROSECONDS);
        } catch (InterruptedException e) {
            return "消息发送失败!";
        }
        return "消息发送成功";
    }
}
  • 测试结果
    控制台打印接收消息==>"你好,redis"
    测试1

九、redis存储session

  • 添加spring-session依赖
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
  • 修改配置文件application.properties
#session有效期限30秒
#server.servlet.session.timeout=PT30s
#session存储方式
spring.session.store-type=redis
#用于存储会话的秘钥的命名空间
spring.session.redis.namespace=spring:session
#指定更新策略
spring.session.redis.flush-mode=on_save
  • 在启动类上添加@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 60 * 60 * 2)的注解,开启redis缓存session。maxInactiveIntervalInSeconds表示session的存活时间。
  • 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysUser implements Serializable {
    private Long id;
    private String username;
    private String password;
}
  • 模拟数据库操作
@Service
public class UserService {

    private List<SysUser> users = new ArrayList<>();

    @PostConstruct // bean初始化
    public void init() {
        users.add(new SysUser(1L, "tom", "123456"));
        users.add(new SysUser(2L, "jack", "123456"));
        users.add(new SysUser(3L, "smallbear", "123456"));
    }

    public Optional<SysUser> login(String username, String password) {
        Optional<SysUser> sysUserOpt = users.stream()
                .filter(user -> Objects.equals(user.getUsername(), username)
                        && Objects.equals(user.getPassword(), password))
                .findFirst();
        return sysUserOpt;
    }
}
  • 创建登录Controller
@RestController
public class LoginController {

    @Autowired
    private UserService userService;

    @GetMapping("home")
    public String home() {
        return "home";
    }

    @PostMapping("login")
    public String login(HttpServletRequest request, String username, String password) {
        Optional<SysUser> userOptional = userService.login(username, password);
        if (userOptional.isPresent()) {
            request.getSession().setAttribute("user", userOptional.get());
            return "登录成功";
        }
        return "登录失败";
    }

    @GetMapping("user")
    public Object user(HttpServletRequest request) {
        return request.getSession().getAttribute("user");
    }
}
  • 修改web配置
// 拦截器配置
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        SysUser user = (SysUser) request.getSession().getAttribute("user");
        if (user != null) {
            return true;
        }
        response.setStatus(403);
        response.getWriter().write("please login first");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor())
                .excludePathPatterns("/home")
                .excludePathPatterns("/login")
                .excludePathPatterns("/mqTest/**");
    }
}
  • 测试







不足:redis中的数据还是序列化的,不知道为啥

相关文章

网友评论

      本文标题:Redis入门

      本文链接:https://www.haomeiwen.com/subject/mgexgctx.html