Redis数据库

Redis作为最受欢迎的NoSQL数据库之一,包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库

作为NoSQL数据库,Redis采用数值对进行数据存储(K-V),其本身具有高性能、分布式、多数据类型的特点,可以应对众多场景。

如在设计验证码短信的时候,在用户验证验证码之前必然需要将验证码信息暂时储存到数据库内,此时作为拥有高性能的键值对数据库的Redis就是极好的选择

其次,当数据库需要在短时间内进行高并发操作,MySQL数据库的性能很难完成这类操作,此时就需要使用Redis来完成。

使用docker安装Redis

sudo docker pull redis:latest
sudo docker images
sudo docker run  --name redis -p 6379:6379 -d --restart=always redis:latest redis-server --appendonly yes --requirepass "Hello122342"

其中 Hello122342 是密码,而6379表示Redis服务的端口号

SpringBoot集成Redis

Spring支持通过网络来使用和操作Redis,使用客户端(如Lettuce、Jedis、Redisson)来简化操作

依赖引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置Redis

# Redis服务器地址
spring.redis.host=192.168.0.1
# Redis 服务器端口号
spring.redis.port=6379
# Redis 服务器密码 (请换成自己机器密码)
spring.redis.password=

StringRedisTemplate类的使用

StringRedisTemplate 是Redis为字符串而使用的工具类,如我们需要将验证码以(“手机号”-“验证码”)的形式存入数据库,可以使用:

  stringRedisTemplate.opsForValue().set("13902350924", "6209");

由于使用的前缀是string,是专为字符串设计的工具类,因此其中的数据为“6209”,而不是数字类型的6209

同时,除了通过 StringRedisTemplate 来录入新数据,也可以通过这个工具类来获取数据,如:



stringRedisTemplate.opsForValue().get("13902350924");

即为从Redis数据库中获取key值为 13902350924 的value值

RedisTemplate使用

RedisTemplate是Spring Data Redis提供的最高级的抽象客户端,可以通过它来完成多种操作。

缓存存入和获取

再实例化RedisTemplate后,通过.opsForValue()方法来实现缓存的存入和获取。

redisTemplate.opsForValue().set(userName, userDO); //使用userName作为key值,userDO作为value存入缓存(userDO要实现序列化)
(UserDO)redisTemplate.opsForValue().get(userName); //通过userName作为key值,获取对象(要经过类型转换)

缓存的修改和删除

基于键值对的Redis是无法修改Value的某个属性,所谓修改就是重新存入值。(即先获取value,再修改获取的值,重新存入)

redisTemplate.opsForValue().set(userName, userDO);

删除缓存其实是按照Key来删除数据,因为不再是关系到Value而是通过Key删除整条K-V数据,因此,删除语句不需要加上opsForValue()方法,而是直接调用delete()

redisTemplate.delete(userName);

Redis列表操作

Redis列表结构就是我们常说的List,通过列表结构将信息进行传输划分,从而使得Redis存储java对象

当然,对象信息需要经过网络传输才能存入远程的Redis服务器,所以对于Redis存储、读取的对象都必须要能过序列化。

网络不能直接使用Java对象,序列化就是把Java对象变成可以传输的数据

所以在设计存入Redis数据库的数据类时,要实现 java.io.Serializable 保证对象可以序列化。

序列化的过程由RedisTemplate自动完成,但必须实现接口进行标识

插入对象

我们使用 leftPush rightPush 来插入对象

其中 leftPush表示从左向右依次插入数据,rightPush表示从右向左依次插入数据。如:

redisTemplate.opsForList().leftPush("categoryList", firstCategory);

即向 categoryList 列表从左向右插入firstCategory对象

注意的是这里我们使用的是forList

无论是leftPush rightPush都存在返回值,类型是long表示执行完操作后,列表的长度。

查询数据

查询列表长度:

Long size = redisTemplate.opsForList().size("categoryList");

根据索引查询:

Category category = (Category)redisTemplate.opsForList().index("categoryList", index);

index()方法用于根据索引查询数据,第一个参数表示列表Key,第二个参数表示索引。

由于从左或从右插入数据时,元素位置会发生变化,所以用索引时要注意结果可能与预期不符。

范围查询:

//查询列表内所有的类目数据
 List<Category> categoryDatas = redisTemplate.opsForList().range("categoryList", 0, -1);

我们通过 range 来查询列表内的值,其中

range的第一个参数是key,即表示列表名

第二个参数是起始位置

第三个参数是结束位置,其中“-1”表示取得所有

由于Redis不能像MySQL一样进行条件查询,所以当需要按条件查找时,需要先查询所有类目,再查找符合条件的值。

数据修改

Redis类目数据的修改,不像关系型数据库,可以只修改几个字段。它需要把数据对象重新放入列表中。

使用set()方法实现数据的修改:

// 演示代码省略 set 各属性值
Category category = new Category();
redisTemplate.opsForList().set("categoryList", 0, category);

第一个参数为列表的Key,第二个为对象索引,第三个是新的对象。

至于如何知道索引,最直观的方法是根据插入的顺序,推算出索引。但很多情况下由于各类操作不是连续的,很难推算。

因此我们通过把列表数据都查出来,直接查找索引的方式:

// 查询所有的数据
List<Category> categoryDatas = redisTemplate.opsForList().range("categoryList", 0, -1);
long index = 0;
for (Category cat : categoryDatas) {
    if (StringUtils.equals(cat.getId(), "gcl_001")) {
        break;
    }
    index++;
}

删除数据

我们使用leftPoprightPop来删除列表内元素

其中leftPop表示删除头元素,rightPop表示删除尾元素、

Category cat = (Category)redisTemplate.opsForList().rightPop("categoryList");

分布式ID

ID是数据的唯一标识,常见的作法是用数据库的AUTO_INCREMENT(主键自增)机制,自动生成ID.

但随着业务的发展,数据量会越来越大,一个数据库无法容纳太多数据时会施行分表甚至分库。这就导致主键自增机制会带来ID冲突。这时就需要一个单独的机制来生成一个唯一的ID。

如线上购买可能存在的订单号,要生成一个‘202102011’前八个数字表示订单日期为2021.02.01 。最后一位表示第一个订单。

Redis有个特点是 所有的命令操作都是单线程的,不像Java那么复杂有多线程的概念。但它足够快,基于这个特点,Redis提供了自增原子命令,保证生成ID是唯一有序的。过程代码:

//格式化格式为年月日
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
//获取当前时间
String now = LocalDate.now().format(dateTimeFormatter);
//通过redis的自增获取序号
RAtomicLong atomicLong = redissonClient.getAtomicLong(now);
atomicLong.expire(1, TimeUnit.DAYS);
long number = atomicLong.incrementAndGet();
//拼装订单号
String orderId = now + "" + number;

这里在获取到当前时间后,因为Redis是Key-Value储存结构,那么获取有个自增ID也需要一个Key,上诉代码,RAtomicLong atomicLong = redissonClient.getAtomicLong(now);表示以now为Key获取一个自增的实例对象,atomicLong.expire(1, TimeUnit.DAYS);表示对象的有效期,使用 long number = atomicLong.incrementAndGet();获取到了自增量。