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++;
}
删除数据
我们使用leftPop
和rightPop
来删除列表内元素
其中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();
获取到了自增量。