Redis的基本使用与进阶知识

Redis的基本使用与进阶知识

Redis是采用键值对key和value来存储数据的一种非关系型数据库,因为其数据是存储在内存中的,所以读写效率高,常用于的场景有:海量数据的读写、数据高并发的读写、数据缓存等。

一、基本数据类型

Redis数据库有五种基本数据类型,现在新版本Redis又新增了三种数据类型 BitmapsHyperLogLogGeospatial ,这里不作说明,只简要说明五种基本数据类型。

1. String
字符串类型,存储的 value 数据为字符串。
2. List
列表类型,存储的 value 数据为一个列表,有序列表,其数据排列的顺序与数据添加的顺序相同。
3. Hash
哈希类型,存储的 value 数据为一个对象,对象内也是采用 key 和 value 进行存储数据的,类似于Java中的 map 数据类型或者Python中的字典 dict 类型。
4. Set
集合类型,存储的 value 数据为一个集合,该集合是 无序 的,集合中不允许有重复值,会自动去重。
5. Zset
有序集合类型,存储的 value 数据也是集合,该集合是 `有序` 的,通过给每个 value 设置 score 值,按照每个 value 的 score 值进行排序,同时该集合也是不包含重复值的,自动去重。

二、基本操作命令

1. String

String字符串类型中的常用命令操作

# 设置key为k1,值为10
set k1 10

# 获取key为k1的值
get k1

# 对key为k1的value值进行+1
incr k1

# 对key为k1的value值进行+20
incrby k1 20 

# 对key为k1的value值进行-1
decr k1

# 对key为k1的value值进行-20
decrby k1 20 

# 设置k1的过期时间为20s
expire k1 20

# 设置k2的值为v2,过期时间为30s
setex k2 30 v2

# 查看k2的过期时间(-1表示永久有效,-2表示已失效,其他数字表示剩余过期时间)
ttl k2

# 设置k2的值为v2,若k2已存在,则不设置,否则设置。
setnx k2 v2

# 同时设置多个key和value
mset k1 v1 k2 v2

# 同时获取多个key的value
mget k1 k2
2. List

List列表类型中的常用命令操作

# 从左边依次添加数据至t1列表中,结果为 3 2 1
lpush t1 1 2 3

# 从右边依次添加数据至t2列表中,结果为 1 2 3
rpush t2 1 2 3

# 从t1列表中获取索引从0到-1的所有value值,索引0代表第一个,索引-1代表最后一个
lrange t1 0 -1

# 从左边依次删除t1列表中的值
lpop t1

# 从右边依次删除t1列表中的值
rpop t1

# 获取t1列表的长度
llen t1
3. Hash

Hash类型中的常用命令操作

# 设置key为k1中的id值等于1001,一次只能设置一个
hset k1 id 1001

# 设置key为k1中的name=lucy,age=20,一次可以设置多个值
hmset k1 name lucy age 20

# 获取k1中id的值
hget k1 id

# 一次性获取k1中的id、name、age值
hmget k1 id name age

# 查看key为k1中的所有属性key 结果-> id name age
hkeys k1

# 查看key为k1中的所有属性key所对应的值 结果-> 1001 lucy 20
hvals k1

# 对key为k1中的age值+2
hincrby k1 age 2
4. Set

Set集合类型中的常用命令操作

# 添加 1 2 3 元素到t1集合中
sadd t1 1 2 3

# 删除t1集合中的3元素
srem t1 3

# 查看t1集合中是否包含4这个元素
sismember t1 4

# 查看t1集合中的所有元素
smembers t1
5. Zset

Zset有序集合类型中的常用命令操作

# 添加 java c++ php 到t1集合中,并指定相应的score值为 100 200 300
zadd t1 100 java 200 c++ 300 php

# 删除t1集合中的java元素
zrem t1 java

# 查看t1集合中索引从0到-1的所有元素(索引0代表第一个元素,索引-1代表最后一个元素)
# 结果 "java" "c++" "php" (按照score值进行从小到大排序)
zrange t1 0 -1

# 查看t1集合中索引从0到-1的所有元素,并显示相应的score值。
# 结果 "java" 100 "c++" 200 "php" 300
zrange t1 0 -1 withscores

# 查看t1集合中索引从0到-1的所有元素,并按倒序(从大到小)排序
# 结果 "php" "c++" "java"
zrevrange t1 0 -1

# 查看t1集合中索引从0到-1的所有元素,按倒序(从大到小)排序,并显示相应的score值。
# 结果 "php" 300 "c++" 200 "java" 100
zrevrange t1 0 -1 withscores

补充其他通用命令

# 查看符合正则表达式的所有key -> (keys pattern)
# 查看所有的key
keys *

# 删除对应的key
del key

# 设置key的过期时间为20s
expire key 20

# 查看key的过期时间(-1表示永久有效,-2表示已失效,其他数字表示剩余过期时间)
ttl key

三、进阶知识

1. 数据持久化

由于Redis的数据是写入到内存中,没有对数据进行持久化操作,当服务器重启时,数据就会丢失,所以需要对数据进行持久化操作,写入到磁盘中。

  • RDB模式:根据自定义设置的时间间隔,将满足条件的数据写入到磁盘中进行备份。

    ​ 优缺点:效率高,但最后一次时间间隔内的数据可能会丢失。

  • AOF模式:将所有的写操作记录到日志文件中,服务器重启时,根据日志文件来恢复数据。

    ​ 优缺点:能够完全保证数据的可靠性,但是数据恢复比较慢。

2. 主从复制

一台服务器作为主服务器,只负责写操作,其他服务器作为主服务器的从服务器,只负责读数据。

优点:读写分离、容灾快速恢复

缺点:复制延时(从机同步数据有一定的延迟)

  • 一仆二主 : 一台主机连接着多台从机

  • 薪火相传 : 一台主机连接着一台从机,从机下面又连接着一台从机

  • 反客为主 : 当主机宕机之后,其中的一台从机输入命令 slaveof no one 升级为主机(可以使用哨兵模式自

    动完成)

  • 哨兵模式 : 监测主机的运行状态,当主机宕机之后,将其中的一台从机升级为主机。

3. 集群

集群是为了保证服务的高可用性,当某些服务器宕机之后,还可以继续提供服务。

优点:

​ 1. 能够解决容量不够的问题 。

  	  2. 能够解决高并发读写的问题,分摊服务器压力。
  	  2.  去中心化配置(任何一台服务器都可以作为入口服务器来进行读写)
4. 缓存穿透

缓存穿透是指查询一个不存在的数据时,由于Redis未命中,则会去数据库中查询该数据,当数据库查询不到该数据时,不会写入缓存,导致每次查询不存在的数据时,都会去访问数据库,造成缓存穿透。

解决办法:

  1. 将查询到的空值设置到缓存中(设置过期时间)
  2. 设置可访问的白名单
  3. 采用布隆过滤器
5. 缓存击穿

缓存击穿是指,当访问一些比较热门的key时,而此时的key又过期了,导致大量请求数据库,数据库宕机。

6. 缓存雪崩

缓存雪崩是指,在极少的时间段内,查询的大量key都过期了,导致大量请求数据库,数据库宕机。

7. 分布式锁

分布式锁可以保证同步操作,比如抢购、秒杀,保证不会产生超卖等问题。JAVA代码如下:

public class SellServiceImpl implements SellService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 库存总量
     */
    private static final Integer TOTAL = 1000;

    /**
     * 例如国庆大甩卖 图书大甩卖 库存 1000 件
     * 存放商品信息
     */
    static Map<String, Integer> products;

    /**
     * 抢购成功者信息
     */
    static Map<String, String> orders;

    static {
        products = new HashMap<>();
        orders = new HashMap<>();
        // 设置商品名称及库存
        products.put("book", TOTAL);
    }

    /**
     * 第一种方法 synchronized 锁机制,解决高并发产生的超卖问题 但效率大大降低 不推荐使用
     * 第二种方法 使用 Redis 分布式锁,解决高并发产生的超卖问题 并且效率相对高很多
     */
    @Override
    public String orderGoods(String productId) {
        String lockValue = UUID.randomUUID().toString();
        // 设置锁,并设置3s过期时间,防止手动释放锁之前程序出错,导致锁一直未释放。
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", lockValue, 3, TimeUnit.SECONDS);
        // 加锁失败 说明有人正在使用
        if (!lock) {
            return "抢购失败,请再试试吧...";
        }
        //先获取商品余量
        int number = products.get(productId);
        if(number <= 0){
            return "商品已抢购完,请您下次再来,谢谢您的理解...";
        }else {
            //模拟下单(不同用户拥有不同ID)
            orders.put(UUID.randomUUID().toString(), productId);
            //减库存
            number = number - 1;
            //模拟延迟
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            products.put(productId, number);
        }
        log.info("共抢购 {} 件,抢购详情:{}", orders.size(), orders);
        // 手动解锁
        String value = (String) redisTemplate.opsForValue().get("lock");
        // 确保释放的锁是同一次设置的锁
        if (lockValue.equals(value)){
            redisTemplate.opsForValue().getOperations().delete("lock");
        }
        return "抢购成功!";
    }

    @Override
    public String queryGoods(String productId) {
        return "国庆图书大甩卖,库存 " +
                TOTAL + " 件,现余 " +
                products.get(productId) + " 件,已被抢购 " +
                orders.size() + " 件";
    }
}

四、Redis整合SpringBoot使用

1. 引入依赖
<!-- Redis依赖 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置YML文件
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    database: 0
3. 配置RedisTemplate

RedisTemplate无需配置也可以使用,只不过没有对key和value进行序列化操作,会导致存储到Redis数据库中的数据为16进制编码格式。

package cn.quicklyweb.flea.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 自定义RedisTemplate
 */
@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建RedisTemplate实例
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        
		// 创建Redis的String字符串序列化对象
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        // 创建Redis的Json字符串序列化对象
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        
        // key采用String的序列化方式
        redisTemplate.setKeySerializer(stringSerializer);
        // hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringSerializer);
        // value采用Jackson的Json序列化方式
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        // hash的value也采用Jackson的Json序列化方式
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        
		// 设置默认的序列化器为Jackson的Json序列化方式
        redisTemplate.setDefaultSerializer(genericJackson2JsonRedisSerializer);
        
		// 设置连接工厂
        redisTemplate.setConnectionFactory(factory);

        return redisTemplate;
    }
}
4. 使用
// 直接注入后便可操作Redis数据库了
@Autowired
private RedisTemplate redisTemplate

redisTemplate.opsForValue.set("key", "value")
redisTemplate.opsForValue.get("key")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总是提示已注册

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值