redis介绍与快速入门
一、NoSQL 概述及与 SQL 的区别
- 什么是 NoSQL?
NoSQL(Not Only SQL)是一类非关系型数据库管理系统,设计用于处理大规模数据存储和高并发访问的场景。与传统的关系型数据库(SQL)不同,NoSQL数据库通常具有更灵活的数据模型,能够更好地适应现代应用程序的需求,特别是在大数据和实时Web应用方面。
- SQL 与 NoSQL 的主要区别
特性 |
SQL (关系型数据库) |
NoSQL (非关系型数据库) |
数据模型 |
基于表格的固定结构 |
灵活的数据模型(文档、键值、列族、图等) |
模式(Schema) |
预定义的模式,结构严格 |
无模式或动态模式 |
可扩展性 |
垂直扩展(增加服务器性能) |
水平扩展(增加服务器数量) |
事务支持 |
ACID 事务支持完善 |
通常只支持基本事务或最终一致性 |
查询语言 |
使用标准化的 SQL 语言 |
无统一查询语言,各数据库不同 |
性能 |
适合复杂查询 |
适合高吞吐量和简单查询 |
数据关系 |
通过外键和JOIN操作维护关系 |
通常不强调关系,或通过嵌入文档处理 |
典型用例 |
需要复杂事务的应用(如银行系统) |
大数据、实时分析、内容管理系统等 |
代表产品 |
MySQL, PostgreSQL, Oracle, SQL Server |
MongoDB, Cassandra, Redis, Neo4j |
垂直扩展与水平扩展详解:
垂直扩展 (Vertical Scaling)
垂直扩展又称为"向上扩展"(Scale Up),是指通过增加单个服务器的资源(CPU、内存、存储等)来提高系统性能。
水平扩展 (Horizontal Scaling)
水平扩展又称为"向外扩展"(Scale Out),是指通过增加更多服务器数量来分散负载,提高系统整体性能。
- NoSQL 的主要类型
-
- 文档数据库 (如 MongoDB):存储类似JSON的文档
- 键值存储 (如 Redis):简单的键值对存储
- 宽列存储 (如 Cassandra):按列而不是按行存储数据
- 图数据库 (如 Neo4j):专门存储和查询图结构数据
- 选择建议
- 选择SQL:当需要复杂查询、严格的数据完整性和事务支持时
- 选择NoSQL:当处理大量非结构化数据、需要高可扩展性或灵活的数据模型时
现代开发中,许多系统会结合使用SQL和NoSQL数据库,以利用各自的优势。
二、Redis介绍
Redis(Remote Dictionary Server)是一个开源的、内存中的键值存储系统,常用作数据库、缓存和消息代理。它支持多种数据结构,并提供持久化功能,是当前最流行的 NoSQL 数据库之一。
核心特性
- 内存存储:数据主要存储在内存中,提供极高的读写性能(10万+/秒 OPS)
- 数据结构丰富:不仅支持简单字符串,还支持:
- 字符串(Strings)
- 哈希(Hashes)
- 列表(Lists)
- 集合(Sets)
- 有序集合(Sorted Sets)
- 位图(Bitmaps)
- HyperLogLogs
- 地理空间索引(Geospatial indexes)
- 流(Streams)
- 持久化选项:
- RDB(快照):定期将内存数据转储到磁盘
- AOF(追加文件):记录所有写操作命令
- 高可用与分布式:
- Redis Sentinel(哨兵):提供自动故障转移
- Redis Cluster:分布式解决方案,数据分片存储
- 多功能:
- 缓存(最常用场景)
- 会话存储
- 消息队列(Pub/Sub)
- 实时分析
- 排行榜/计数器
常见使用场景
- 缓存系统:
- 减轻数据库负载
- 存储会话数据
- 页面缓存
- 实时应用:
- 实时排行榜
- 计数器
- 实时分析
- 消息系统:
- 发布/订阅模式
- 简单的消息队列
- 其他:
- 分布式锁
- 限流系统
- 地理位置应用
三、redis的安装
在这里我们选择用虚拟机(Linux环境 centos 7)来实现部署redis
在官网中下载redis压缩包Redis下载
注意:浏览器可能识别文件不安全,选择保留文件
利用终端工具将压缩包上传到虚拟机并解压压缩包
tar -zvxf redis-5.0.4.tar.gz
结果如图:
注意的是redis是用C写的所以需要C的环境:
yum install -y gcc tcl
cd redis-5.0.4
编译redis
make && make install
如此编译成功:
四、redis的启动
1、默认启动
进入redis’的根目录
redis-server
如此启动成功
2、指定配置启动
打开redis.conf文件(在redis的根目录下)
监听的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,全产环境不要设置为0.0.0.0
守护进程,修改为yes后即可后台运行
设置密码,设置后访问redis 需要密码
设置完成后,保存并退出,
启动:
redis-server redis.conf
查看是否启动成功:
ps -ef | grep redis
五、redis客户端
1、命令行客户端
Redis 自带一个功能强大的命令行客户端工具 redis-cli,是与 Redis 服务器交互的主要方式之一。
连接本地 Redis 服务器(默认端口 6379):
redis-cli
连接远程 Redis 服务器:
redis-cli -h hostname -p port -a password
例如:
redis-cli -h redis.example.com -p 6380 -a mypassword
常用命令行选项
选项 |
说明 |
-h <host> |
指定服务器主机名 (默认: 127.0.0.1) |
-p <port> |
指定服务器端口 (默认: 6379) |
-a <password> |
使用密码认证 |
-n <db> |
选择数据库编号 (默认: 0) |
--raw |
显示原始格式的输出 |
--no-raw |
强制格式化输出 |
--csv |
输出 CSV 格式 |
--stat |
实时显示服务器统计信息 |
--scan |
使用 SCAN 命令代替 KEYS |
--pattern <pat> |
与 --scan 配合使用指定模式 |
--intrinsic-latency <sec> |
测试 Redis 内在延迟 |
--eval <file> |
执行 Lua 脚本文件 |
这里 我们使用本地连接:
redis-cli -a 123456
输入ping 命令,查看是否链接成功,
显示pong则为成功
做一些简单的命令:
2、图形化界面客户端
官方下载
Releases · lework/RedisDesktopManager-Windows
下载成功后解压,点击exe文件下载到本地,
填写数据,连接redis
注意:一定要关闭虚拟机的防火墙,否则将连接失败
sudo systemctl stop firewalld
最后,结果为
六、redis常见value 的数据结构/类型
Redis 支持多种数据结构类型,每种类型都有特定的使用场景和操作命令。以下是 Redis 中主要的 value 数据结构类型:
1、基本数据结构类型
类型 |
存储结构 |
特性 |
常用场景 |
String (字符串) |
二进制安全字符串 |
最基本类型,最大512MB |
缓存、计数器、分布式锁 |
Hash (哈希) |
键值对集合 |
适合存储对象 |
用户信息、商品信息 |
List (列表) |
双向链表 |
按插入顺序排序,元素可重复 |
消息队列、最新列表 |
Set (集合) |
无序集合 |
元素唯一,支持集合运算 |
标签、好友关系 |
Sorted Set (有序集合) |
带分数的集合 |
元素唯一,按分数排序 |
排行榜、优先级队列 |
2、高级数据结构类型
类型 |
存储结构 |
特性 |
常用场景 |
Bitmaps (位图) |
字符串的位操作 |
极省空间 |
用户签到、布隆过滤器 |
HyperLogLog |
概率数据结构 |
估算基数(去重计数) |
UV统计 |
Geospatial (地理空间) |
有序集合实现 |
存储地理位置 |
附近的人、地点搜索 |
Stream (流) |
消息链表 |
持久化消息队列 |
消息系统、事件溯源 |
七、redis通用指令
KEYS:查看符合模板的所有key,不建议在生产环境设备上使用
DEL:删除一个指定的key
EXISTS:判断key是否存在
EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
TTL:查看一个KEY的剩余有效期
例子:
# 1. 设置一个带过期时间的键值
127.0.0.1:6379> SET session:user123 "auth_token_xyz" EX 300
OK # 设置值并设置300秒过期时间
# 2. 检查key是否存在
127.0.0.1:6379> EXISTS session:user123
(integer) 1
# 3. 查看剩余时间
127.0.0.1:6379> TTL session:user123
(integer) 287
# 剩余287秒 # 4. 修改过期时间
127.0.0.1:6379> EXPIRE session:user123 600
(integer) 1
# 5. 再次查看剩余时间
127.0.0.1:6379> TTL session:user123
(integer) 598
# 已更新为约600秒 # 6. 删除key
127.0.0.1:6379> DEL session:user123
(integer) 1
# 7. 确认删除
127.0.0.1:6379> EXISTS session:user123
(integer) 0
八、redis key的类型以及常用指令
String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
string:普通字符串
int:整数类型,可以做自增、自减操作
float:浮点类型,可以做自增、自减操作
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m.
String类型的常见命令
SET:添加或者修改已经存在的一个String类型的键值对
GET:根据key获取String类型的value
MSET:批量添加多个String类型的键值对
MGET:根据多个key获取多个String类型的value
INCR:让一个整型的key自增1
INCRBY:让一个整型的key自增并指定步长,例如:incrbynum2让num值自增2
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
SETEX:添加一个String类型的键值对,并且指定有效期
九、redis的层级结构
Redis 虽然没有内置的层级结构支持,但通过合理的 key 命名规范可以模拟出层级结构,
其中分隔符分层法是最常用的,一般以:作为风格符
例子:
user:1001:profile # 用户1001的基本资料
user:1001:settings # 用户1001的个人设置
user:1001:orders # 用户1001的订单集合
user:1001:cart # 用户1001的购物车
十、哈希类型
Redis 的哈希类型是一种键值对集合,非常适合存储对象类型的数据。
+---------------------+
| Redis Hash Key |
+----------+----------+
| Field1 | Value1 |
+----------+----------+
| Field2 | Value2 |
+----------+----------+
| ... | ... |
+----------+----------+
| FieldN | ValueN |
+----------+----------+
Hash类型的常见命令
HSETkeyfieldvalue:添加或者修改hash类型key的field的值
HGETkeyfield:获取一个hash类型key的field的值
HMSET:批量添加多个hash类型key的field的值
HMGET:批量获取多个hash类型key的field的值
HGETALL:获取一个hash类型的key中的所有的field和value
HKEYS:获取一个hash类型的key中的所有的field
HVALS:获取一个hash类型的key中的所有的value
HINCRBY:让一个hash类型key的字段值自增并指定步长
HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
十一、List类型
Redis 的列表(List)类型是一个双向链表结构,支持从两端插入和弹出元素,是实现队列、栈等数据结构的理想选择。
List类型的常见命令
LPUSHkeyelement...:向列表左侧插入一个或多个元素
LPOPkey:移除并返回列表左侧的第一个元素,没有则返回nil
RPUSHkeyelement...:向列表右侧插入一个或多个元素
RPOPkey:移除并返回列表右侧的第一个元素
LRANGEkeystarend:返回一段角标范围内的所有元素
BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
示例:
十二、set类型
Redis 的集合(Set)是一个无序的、元素唯一的集合数据类型,它提供了高效的成员检查、交集、并集等操作。
特点
无序
元素不可重复
查找快
支持交集、并集、差集等功能
常用命令
SADDkeymember...:向set中添加一个或多个元素
SREMkeymember..:移除set中的指定元素
SCARDkey:返回set中元素的个数
SISMEMBERkeymember:判断一个元素是否存在于set中
SMEMBERS:获取set中的所有元素
SINTERkey1key2..:求key1与key2的交集
SDIFFkey1key2...:求key1与key2的差集
SUNiONkey1key2..:求key1和key2的并集
示例:
十三、SortedSet类型
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。
特点
可排序
元素不重复
查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
常见命令
ZADDkeyscoremember:添加一个或多个元素到sortedset,如果已经存在则更新其score值
ZREMkeymember:删除sortedset中的一个指定元素
ZScOREkeymember:获取sortedset中的指定元素的score值
ZRANKkeymember:获取sortedset中的指定元素的排名
ZCARDkey:获取sortedset中的元素个数
ZCOUNTkeyminmax:统计score值在给定范围内的所有元素的个数
ZINcRBYkeyincrementmember:让sortedset中的指定元素自增,步长为指定的increment值
ZRANGEkeyminmax:按照score排序后,获取指定排名范围内的元素
ZRANGEBYSCOREkeyminmax:按照score排序后,获取指定score范围内的元素
ZDIFF、ZINTER、ZUNION:求差集、交集、并集
示例:
十四、redis 的java客户端
Redis 提供了多种 Java 客户端选择,以下是主流的java客户端:
客户端 |
维护者 |
特点 |
适用场景 |
Jedis |
Redis 官方 |
同步阻塞IO、API 直接映射 Redis 命令 |
简单应用、快速开发 |
Lettuce |
Redis 官方 |
基于 Netty 的异步非阻塞、支持响应式编程 |
高并发、异步应用 |
Redisson |
社区 |
分布式和可扩展的 Java 数据结构 |
分布式系统、复杂场景 |
Spring Data Redis |
Spring |
对 Jedis/Lettuce 的封装、与 Spring 生态集成 |
Spring 应用 |
1、Jedis
Jedis 是 Redis 官方推荐的 Java 客户端,是一个轻量级、高效且功能完整的 Redis Java 驱动库。但是Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们一般使用Jedis连接池代替Jedis的直连方式
实战:
导入依赖:
<!-- jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
</dependency>
创建配置类(连接池)
private static final JedisPool jedisPool;
static {
//配置Redis连接池
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(8);//最大连接数
jedisPoolConfig.setMaxIdle(8);//最大空闲连接数
jedisPoolConfig.setMinIdle(0);//最小空闲连接数
jedisPoolConfig.setMaxWaitMillis(1000);//最大等待时间
//创建连接池对象
jedisPool = new JedisPool(jedisPoolConfig, "192.168.81.131", 6379, 1000);
}
public static Jedis getJedisPool() {
return jedisPool.getResource() ;
}
在测试类中测试:
private Jedis jedis;
@BeforeEach
public void setUp() {
// 建立连接
jedis = JedisConnectionFactory.getJedisPool();
// 设置密码
jedis.auth("123456");
// 选择数据库
jedis.select(1);
}
@Test
public void testString() {
//存入数据
String result = jedis.set("name", "张三");
System.out.println("result存入" + result);
//获取数据
String value = jedis.get("name");
System.out.println("name = " + value);
}
@Test
public void testHash() {
//存入数据
Long name = jedis.hset("user:1", "name", "李四");
Long name1 = jedis.hset("user:1", "age", "21");
System.out.println("result存入" + name + "年龄"+ name1);
//获取数据
Map<String, String> stringStringMap = jedis.hgetAll("user:1");
System.out.println("name = " + stringStringMap);
}
@AfterEach
public void tearDown() {
if (jedis != null) {
jedis.close();
}
}
运行的结果:
2、springdataredis
Spring Data Redis 是 Spring 生态系统中用于访问 Redis 的模块,它提供了对 Redis 的高级抽象和便捷的集成方式。
特点:
提供了对不同Redis客户端的整合(Lettuce和Jedis)
提供了RedisTemplate统一APl来操作Redis
支持Redis的发布订阅模型
支持Redis哨兵和Redis集群
支持基于Lettuce的响应式编程
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
支持基于Redis的JDKCollection实现
实战:
创建spring boot项目,引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
在application.properties中写入配置:
spring.data.redis.host=192.168.81.131
spring.data.redis.port=6379
spring.data.redis.password=123456
spring.data.redis.database=3
spring.data.redis.timeout=10000
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=100ms
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
编写测试:
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","一只游鱼");
//获取
Object object = redisTemplate.opsForValue().get("name");
System.out.println(object);
}
需要注意 的是这样插入中文,会出现这样的问题:
出现这样问题的原因是默认序列化器RedisTemplate 默认使用 JdkSerializationRedisSerializer
该序列化器会将对象转为字节数组存储,导致可读性差
所以我们可以通过配置类来修改其序列化配置:
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置Key的序列化
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置value的序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 返回
return template;
}
当然需要添加依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
修改测试类:
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","一只游鱼");
//获取
Object object = redisTemplate.opsForValue().get("name");
System.out.println(object);
}
如此,解决问题:
当然了,springdataredis提供了专门的stringRedisTemplate来针对String类型(默认为String),这样可以不用通过配置类来实现功能
如下:
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void contextLoads() {
stringRedisTemplate.opsForValue().set("name","一只游鱼");
//获取
Object object = stringRedisTemplate.opsForValue().get("name");
System.out.println(object);
}
当然也可以存入JOSN,
创建User实体类:
private static final long serialVersionUID = 1L;
private String name;
private int age;
// 无参构造函数
public User() {
}
// 带参构造函数
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 必须提供公共的getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
编写测试:
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper objectMapper = new ObjectMapper();
@Test
void testObject() throws JsonProcessingException {
//创建
User user = new User("一只游鱼", 21);
//手动序列化
String json = objectMapper.writeValueAsString(user);
//写入数据
stringRedisTemplate.opsForValue().set("user:1",json);
//获取数据
String userJson = stringRedisTemplate.opsForValue().get("user:1");
//反序列化
User user1 = objectMapper.readValue(userJson, User.class);
System.out.println(user1);
}
如下得到结果:
哈希类型
@Test
void testHash() {
stringRedisTemplate.opsForHash().put("user:2","name","一只游鱼");
stringRedisTemplate.opsForHash().put("user:2","age","21");
//获取
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:2");
System.out.println(entries);
}
结果如下:
最后双手奉上作者的学习源码: YoyuDev/redisStudent: redis学习笔记
如果有帮助,记得点个赞~~