文章目录
前言
NOSQL
NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型,web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非 常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。
一、认识Redis
1.什么是Redis?
Redis (REmote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库,Resdis是一种基于内存的数据库,对数据的读写操作都在内存中完成,因此读写速度非常快,常用于缓存、消息队列、分布式锁等场景。
2.Redis的特点与用途:
- Redis 是基于内存的数据存储系统,因此读写速度非常快。
- 支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等,使其适用于各种场景。
- 用于缓存、会话管理、排行榜、计数器、实时分析等多个应用场景。
- 提供持久化机制,可以将数据写入磁盘,以防止数据丢失。
3.Redis的数据结构:
Redis为我们提供了丰富的数据结构,常见的有以下五种数据类型:
- 字符串(Strings):
用途:存储文本或二进制数据、缓存对象、分布式锁、共享session等。
特点:最基本的数据类型,可以存储任意数据,如文本、JSON、图片等。 - 哈希表(Hashes):
用途:存储对象或记录的属性及其值。
特点:类似于关联数组,适用于存储对象的各个字段。 - 列表(Lists):
用途:有序集合,用于实现队列和栈等数据结构。
特点:可以添加、删除元素,支持下标访问。 - 集合(Sets):
用途:无序集合,支持对元素的添加、删除和查找。
特点:不允许重复元素,可用于实现标签、好友关系等。 - 有序集合(Sorted Sets):
用途:类似于集合,但每个元素关联了一个分数,可以按分数排序。
特点:可以用于排行榜、优先级队列等。
4.Redis 数据存储格式
- Redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储;
- 数据类型指的是存储的数据的类型,也就是 value 部分的类型,key 部分永远都是字符串;
- key的语法:
在一个项目中,key最好使用统一的命名模式
key区分大小写
key不要太长,尽量不要超过1024字节。不仅消耗内存,也会降低查找的效率
key不要太短,太短可读性会降低
5.Redis 的文件配置:
篇幅太长,放在另一篇中:
Redis.windows.conf文件配置
二、Spring-data-redis
Spring Data Redis 是 Spring 框架下的一个子项目,旨在为开发者提供与 Redis 数据存储的集成和简化操作。它为 Redis 数据库提供了更高层次的抽象和便利性,让开发者可以更轻松地在 Spring 应用中使用 Redis。
1.两个封装模板
Spring-data-redis中封装了两个模板类,帮助我们实现更方便的使用Redis:
- RedisTemplate:keyvalue泛型都是object;
- StringRedisTemplate:keyvalue泛型都是string;
当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可;但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
2.自定义模板
无论是RedisTemplate还是StringRedisTemplate,在封装完一个对象信息后,在可视化工具中,都是没有可读性的,这其中原因在于他们的序列化策略,我们可以自己封装一个模板,来解决这个问题。
/**
* @author 杨树林
* @version 1.0
* @since 14/8/2023
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> jsonRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//1.创建自定义模板类
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
//配置json类型的序列化工具
template.setKeySerializer(new StringRedisSerializer());//这样key会用字符串方式保存
// 设置 key 的序列化工具为 StringRedisSerializer
template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
//将 RedisConnectionFactory 参数传递给 template 实例,建立了与 Redis 数据库的连接
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
3.Redis 的工具类:
篇幅太长,放在另一篇中:
Redis工具类
4.Redis与Springboot整合小案例
通过redis缓存实现:
获取用户策略:先从缓存中获取用户,没有则取数据表中数据,再将数据写入缓存;
删除用户策略:删除数据表中数据,然后删除缓存;
- 项目搭建:Springboot项目整合MybatisPlus(用于从数据库中读取数据)
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--MySql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
<scope>runtime</scope>
</dependency>
<!--MySqlpuls依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--lombok:用于封装实体类的各种方法(get,set,有参,无参)-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- yml配置文件
#连接数据数据库的数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
url: jdbc:mysql://localhost:3306/demo_db?serverTimezone=GMT
password: 12345678
#redis相关配置
redis:
host: localhost
port: 6379
#mybatis-plus相关配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
其他相关配置
#Redis数据库索引(默认为0)
spring.redis.database=0
#Redis服务器地址
spring.redis.host=192.168.0.24
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
#连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=10
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=1000
- 创建实体类
@TableName("stu")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
@TableId(value = "stuid", type = IdType.AUTO)
private Integer stuid;
@TableField(value = "stuname")
private String stuname;
@TableField(value = "stuhobby")
private String stuhobby;
}
- 三层架构
4.1mapper数据访问层:
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
}
4.2service业务层:
@Service
public class StudentService {
@Autowired(required = false)
StudentMapper mapper;//装配Mapper
@Autowired
RedisUtils redisUtils;//装配自定义模板类
/**
* 获取用户策略:先从缓存中获取用户,没有则取数据表中数据,再将数据写入缓存
*/
public Student findById(Integer id){
String key = "student:id:" + id;
//1.1判断key在redis中是否存在
boolean hasKey = redisUtils.hasKey(key);
if (hasKey) {
//1.2存在缓存则直接获取s
Object stu = redisUtils.get(key);
ObjectMapper change = new ObjectMapper();
Student student = change.convertValue(stu,Student.class);
System.out.println("==========从缓存中获得数据=========");
System.out.println(student.getStuname());
System.out.println("==============================");
return student;
} else {
//1.3不存在缓存,先从数据库中获取,在保存至redis,最后返回用户
Student student = mapper.selectById(id);
System.out.println("==========从数据表中获得数据=========");
System.out.println(student.getStuname());
System.out.println("==============================");
if (student != null){
redisUtils.set(key, student);//写入缓存
}
return student;
}
}
/**
* 删除用户策略:删除数据表中数据,然后删除缓存
*
*/
public void deleteStudentById(Integer id){
//1.删除数据库
int result = mapper.deleteById(id);
//2.判断数据库中是否删除成功
String key = "student:id:" + id;
if (result != 0) {
//3.判断redis中是否存在
boolean hasKey = redisUtils.hasKey(key);
//4.存在删除,不存在直接跳转
if (hasKey) {
redisUtils.del(key);
System.out.println("删除了缓存中的key:" + key);
}
}
}
}
4.3controller控制层:
@RestController
public class StudentController {
@Autowired
StudentService studentService;//装配service
//restful方式请求
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") Integer id){
Student stu = studentService.findById(id);
return stu;
}
@GetMapping("/delete/{id}")
public Integer delete(@PathVariable("id") Integer id) {
studentService.deleteStudentById(id);
return id;
}
}
项目文件目录:RedisConfig自定义模板类和RedisUtils上文均有介绍
三、Redis事务
1.什么是Redis事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。Redis事务的主要作用就是串联多个命令防止别的命令插队。
2.Redis事务的特点
1.Redis事务没有没有隔离级别的概念;
2.所有的命令在事务中,并没有直接被执行,只有发起执行命令Exec的时候才会执行!;
3.Redis单条命令式保存原子性的,但是事务不保证原子性!
3.Redis事务的相关命令
命令 | 描述 |
---|---|
multi | 标记一个事务的开始 |
exec | 执行所有事务块内的命令 |
discard | 取消事务,放弃执行事务块内的所有命令 |
watch key1 key2 | 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。类似乐观锁 |
unwatch | 取消watch命令对所有 key 的监视。 |
4.注意事项
编译型异常(代码有问题!命令有错!)事务中所有的命令都不会被执行!
127.0.0.1:6379>multi
OK
127.0.0.1:6379>setk1v1
QUEUED
127.0.0.1:6379>setk2v2
QUEUED
127.0.0.1:6379>setk3v3
QUEUED
127.0.0.1:6379>getsetk3
#错误的命令
(error)ERRwrongnumberofargumentsfor'getset'command
127.0.0.1:6379>setk4v4
QUEUED
127.0.0.1:6379>setk5v5
QUEUED
127.0.0.1:6379>exec#执行事务报错!
(error)EXECABORTTransactiondiscardedbecauseofpreviouserrors.
127.0.0.1:6379>getk5#所有的命令都不会被执行!
(nil)
运行时异常,如果事务队列中存在语法性,那么执行命令的时候其他命令是可以正常执行的,错误命令抛出异常!
127.0.0.1:6379>setk1"v1"
OK
127.0.0.1:6379>multi
OK
127.0.0.1:6379>incrk1#会执行的时候失败!
QUEUED
127.0.0.1:6379>setk2v2
QUEUED
127.0.0.1:6379>setk3v3
QUEUED
127.0.0.1:6379>getk3
QUEUED
127.0.0.1:6379>exec
1)(error)ERRvalueisnotanintegeroroutofrange
#虽然第一条命令报错了,但是依旧正常执行成功了!
2)OK
3)OK
4)"v3"
127.0.0.1:6379>getk2
"v2"
127.0.0.1:6379>getk3
"v3"
总结
- Redis支持多种数据结构;
- Redis 是基于内存的数据存储系统,因此读写速度非常快;
- Spring-data-redis中封装了两个模板类:StringRedisTemplate和RedisTemplate;
- Redis支持事务,Redis单条命令式保存原子性的,但是事务不保证原子性;