Redis基础篇
1. 基础介绍
1.1 什么是Redis
Redis是一种键值型的NoSQL数据库,即数据以键(key)值(value)对的形式存在。多用于对数据的缓存处理,是一种内存数据结构存储器。
1.2 SQL与NOSQL
NoSql可以翻译做Not Only Sql(不仅仅是SQL),或者是No Sql(非Sql的)数据库。是相对于传统关系型数据库而言,有很大差异的一种特殊的数据库,因此也称之为 非关系型数据库。
1.2.1结构化与非结构化
传统关系型数据库是结构化数据,每张表在创建的时候都有严格的约束信息,如字段名、字段数据类型、字段约束等,插入的数据必须遵循这些约束
而NoSQL则对数据库格式没有约束,可以是键值型,也可以是文档型,甚至是图格式。
1.2.2关联与非关联
SQL的数据,表与表之间通常具有关联性,如 外键约束。
而非关系型的的数据库关系维护要么通过代码之间的业务逻辑,要么通过数据之间的耦合。
1 | { |
例如通过上述结构存储张三与他的各个订单,不可避免的会出现订单信息的冗余,推荐使用业务逻辑来维护数据关系。
1.2.4 语法结构的统一性
关系型数据库通常具有统一的语法格式,如:
1 | SELECT id,name,age FROM tb_user WHERE id=1; |
而非关系型数据库语法格式不固定,不同的库语法可能完全不同。
1 | Redis: get user:1 |
1.2.5 事务处理上
关系型通常需要满足 ACID特性,即原子性,一致性,隔离性,持久性。
而非关系型数据库要么没有事务,要么无法满足强一致性,只能做一些基本的满足。
| SQL | NoSQL | |
|---|---|---|
| 数据结构 | 结构化(Structured) | 非结构化 |
| 数据关联 | 关联(Relational) | 无关联的 |
| 查询方式 | SQL | 非SQL |
| 事务处理 | ACID | BASE |
| 存储方式 | 磁盘 | 内存 |
| 扩展性 | 垂直 | 水平 |
| 使用场景 | 1)数据结构稳定 2)相关业务对数据安全性、一致性要求较高 |
1)数据结构不固定 2)对一致性、安全性要求不高 3)对性能需求大 |
的
1.3 Redis
Redis : Remote Dictionary Serer 远程字典服务器,诞生于2009年,是一个基于内存的键值型NoSQL数据库。
作者:antirez
特征:
- 键值型 : value支持多种不同的数据类型.
- 单线程:所有命令串行执行,因此其线程是安全的。每个命令具有原子性。
- 低延迟,速度快(基于内存,IO多路复用,良好的编码)。
- 支持数据持久化
- 支持主从集群,分片集群
- 支持多语言客户端
1.4 配置准备
Redis 并未给出官方的windows版本,可以通过虚拟机技术模拟linux系统环境。
由于 centOS已经停止维护,故采用了 VM虚拟机 + ubuntu+finalshell 的配置完成本技术的学习。配置过程不在这里赘述。
1.5 Redis客户端
Redis提供了三种客户端方式用于访问Redis
- 命令行客户端
- 图形化桌面客户端
- 编程客户端
1.5.1 命令行客户端
redis-cli 自带的命令行客户端,使用方式如下:
1 | redis-cli [optings] [commonds] |
其中常见的options有:
-h 127.0.0.1: 指定要连接到的redis的节点的IP,默认为 127.0.0.1-p 6379: 要连接的redis的端口,默认6379-a passwords: 访问密码
而commonds即为操作命令,如
ping:与服务器做心跳测试,服务端正常会返回pong- set,get等方法
不指定commonds时,会进入 redis-cli的交互控制台,

可以使用AUTH password 键入密码,不推荐 -a 接密码,有安全风险
1.5.2 图形化客户端
Redis默认有16个库,不能改名,但能通过配置文件修改库的数量
在命令行中可以通过 SELECT 0 的命令连接到0号数据库
1 | SELSET 1 |
2 常用命令
2.1 Redis数据结构
已经知道,Redis是Key-Value 型数据库,而key通常为 String类型,value任意,常见的有:
| 数据类型 | 格式 | 前五个为基础类型,后三个为特殊类型 |
|---|---|---|
| String | ahfia | |
| Hash | {name:”david”,age:”21”} | |
| List | [A->B->C->D] | 链表 |
| Set | {A,B,C} | 无序集合,不能重复 |
| SortedSet | {A:1,B:2,C:3} | 有序,不能重复 |
| GEO | {A :(120.3, 35.5)} | |
| BitMap | 001101011 | |
| HyperLog | 001101010 |
redis通用命令
可以通过 help [commond] 查询命令的用法
- KEYS pattern : 查看符合模板的所有key,pattern通常可以是 ,a等格式,不建议在生产环境设备上使用,redis单线程,数据量极大时会阻塞
- DEL : 删除指定的key
- MSET :批量插入键值对
- EXISTS : 判断key是否存在,返回0/1
- EXPIRE: 给键设置一个有效期,到期后key会被自动删除
EXPIREAT name 20: 设置name 有效期为20s
- TTL : 查看一个key的剩余有效期,未设置返回-1 ,已被移除返回-2
String类型
根据不同的字符串格式,可分为String,int,float三种格式,不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同,字符串类型的最大空间不能超过512M
String类型的常见命令有:
| 命令 | 用法 | 3 |
|---|---|---|
| SET | 添加或修改一个已经存在的String键值对 | |
| GET | 根据key获取String类型的value | |
| MSET | 批量添加多个String类型的键值对 | |
| MGET | 批量获取多个key的value | |
| INCR | 令一个整型的key自增1 | |
| INCRBY | 指定步长的自增,INCRBY num 2 令num自增2 | 可以INCRBY key -1 代替DECRBY实现自减 |
| INCRBYFOLOAT | 指定步长的浮点数自增 | |
| SETNX | 添加一个String类型的键值对,key必需未存在,是真正的**新增** |
|
| SETEX | 添加String类型的键值对并指定有效期 |
key的层级结构
Redis的key允许有多个单词形成层级结构,单词间用:隔开,例如格式:项目名:业务名:类型:id
如果value是一个Java对象,则可以将其序列化为JSON字符串后储存
key: yzxmm:user:1 value: {“id”:1,”name”:”jack”,”age”:”21”}
key: yzxmm:produce:1 value: {“id”:1,”name”:”iphone17”,”price”:7000}
Hash类型
Hash类型,也叫散列,其value是一个无序字典
- Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD

- Hash的常用命令有
| 命令 | 描述 |
|---|---|
| HSET key field value | 添加或者修改hash类型key的field的值 |
| HGET key field | 获取一个hash类型key的field的值 |
| HMSET | 批量添加多个hash类型key的field的值 #4.0后已视为弃用 |
| HMGET | 批量获取多个hash类型key的field的值 |
| HGETALL | 获取一个hash类型的key中的所有的field和value |
| HKEYS | 获取一个hash类型的key中的所有的field |
| HINCRBY | 让一个hash类型key的字段值自增并指定步长 |
| HSETNX | 添加一个hash类型的key的field值,前提是这个field不存在,否则不执行 |
List类型
- Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似 :- 有序
- 元素可以重复
- 插入删除快
- 查询速度一般
- 基于其特点,可以用来存储诸如朋友圈点赞,评论列表等有序数列,
- List常见命令有:
| 命令 | 用法 |
|---|---|
| LPUSH key element… | 向列表左侧插入一个或多个元素 |
| LPOP key | 移除并返回列表左侧的第一个元素,没有则返回nil |
| RPUSH key element… | 向列表右侧插入一个或多个元素 |
| RPOP key | 移除并返回列表右侧的第一个元素 |
| LRANGE key start end | 返回一段角标范围的所有元素 |
| BLPOP与BRPOP | 与LPOP和RPOP类似,但在没有元素时会等待指定的时间,不是直接返回nil |
Set类型
依旧与java的HashSet类似,可以当做value为null的HasgMap。是一个Hsah表,因此具备与HashSet类似的特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集,并集,差集功能
- Set的常见命令有:
| 命令 | 描述 |
|---|---|
| SADD key member… | 向set中添加一个或多个元素 |
| SREM key member … | 移除set中的指定元素 |
| SCARD key | 返回set中元素的个数 |
| SISMEMBER key member | 判断一个元素是否存在于set中 |
| SMEMBERS | 获取set中的所有元素 |
| SINTER key1 key2 … | 求key1与key2的交集 |
| SUNION key1 key2 … | 求key1与key2的并集 |
| SDIFF key1 key2 … | 求key1与key2的差集 |
练习题:
1.用set集合存储
1 | 192.168.23.128:6379> sadd zhangsan lisi wangwu zhaoliu |
- 计算张三的好友有几个
1 | 192.168.23.128:6379> scard zhangsan |
- 计算张三和李四有哪些共同好友
1 | 192.168.23.128:6379> sinter zhangsan lisi |
- 查询哪些是张三的好友却不是李四的好友
1 | 192.168.23.128:6379> sdiff zhangsan lisi |
- 查询张三和李四的好友共有那些人
1 | 192.168.23.128:6379> sunion zhangsan lisi |
- 判断李四是否是张三的好友
1 | 192.168.23.128:6379> sismember zhangsan lisi |
- 判断张三是否是李四的好友
1 | 192.168.23.128:6379> sismember lisi zhangsan |
- 将李四从张三的好友列表移除
1 | 192.168.23.128:6379> srem zhangsan lisi |
SortedSet类型
可排序的set集合,与java中的TreeSet有些相似,但底层数据结构差别非常大。
SortedSet中每一个元素都有一个score属性,可以通过这个score属性对元素进行排序,底层实现是一个 跳表 (SkipList) 加一个Hash表。
SortedSet具备以下属性:
- 可排序
- 元素不重复
- 查询速度快
因其可排序的特性,经常用来实现排行榜这样的功能。
SortedSet类型的常见命令有:
| 命令 | 用法 |
|---|---|
| ZADD key score member | 添加一个或多个元素到sorted set,已存在则更新score的值 |
| ZREM key member | 删除一个元素 |
| ZSCORE key member | 获取sorted set 中的指定元素的score值 |
| ZRANK key member | 获取指定元素的排名 |
| ZCRAD key | 获取元素个数 |
| ZCOUNT key min max | 统计score值在给定范围内的所有元素的个数 |
| ZINCRBY key increment member | 令指定元素自增,步长为increment |
| ZRANGE key min max | 按score排序后,获取指定排名范围内的元素 |
| ZRANGEBYSCORE | 按score排序后,获取指定score范围内的元素 |
| ZUNION,ZINTER,ZDIFF | 求并集,交集,差集 |
所有排名默认是 升序,降序为在命令Z后加上 REV
练习题:
- 将以下数据存入SortedSet:
jack 85, Rose 89, Tom 95,Jerry 78,Amy 92, Miles 76
1 | 192.168.23.128:6379> zadd stus 85 jack 89 Lucy 82 Rose 95 Tom 78 Jerry 92 Amy 76 Miles |
实现下列功能:
- 删除Tom同学
1 | 192.168.23.128:6379> zrem stus Tom |
获取Amy的分数
1
2192.168.23.128:6379> zscore stus Amy
"92"获取Rose的排名
1
2
3
4
5
6192.168.23.128:6379> zrank stus Rose
(integer) 2
#默认升序排列且从0开始,故升序返回2
#降序排名
192.168.23.128:6379> zrevrank stus Rose
(integer) 3查询80分以下的有几个学生
1
2192.168.23.128:6379> zcount stus 0 80
(integer) 2给Amy同学加2分
1
2192.168.23.128:6379> zincrby stus 2 Amy
"94"查出成绩前三名的同学
1
2
3
4192.168.23.128:6379> zrevrange stus 0 2
1) "Amy"
2) "Lucy"
3) "jack"查出成绩80分及以下的所有同学
1
2
3192.168.23.128:6379> zrangebyscore stus 0 80
1) "Miles"
2) "Jerry"
3.Redis的java客户端
jedis
redis/jedis: Redis Java client
1.idea导入jedis的meavn坐标,以及单元测试
1 | <dependencies> |
2.建立链接
1 | package com.example.test; |
3.测试
1 |
|
4.关闭
1 | //释放连接 |
连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式。
新建com.example.jedis.uitl包用来存放工具类,新建JedisConnectionFactory类
1 | public class JedisConnectionFactory { |
这样之前的测试类就可以直接从连接池获取连接
1 |
|
SpringDataRedis
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,对Redis的集成就叫做SpringDataRedis
官网 https://spring.io/projects/spring-data-redis
- 提供了对不同Reids客户端的整合(
Lettuce和Jedis) - 提供了RdisTemplate统一API来操作Redis
- 支持Redis的发布订阅模型
- 支持Redis哨兵和Redis集群
- 支持基于Lettuce的响应式编程
- 支持基于JDK,JSON,字符串,Spring对象的数据序列化及反序列化
- 支持基于Redis的JDKCollection实现
- SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
| API | 返回值类型 | 说明 |
|---|---|---|
| redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
| redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
| redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
| redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
| redisTemplate.opsForzSet() | ZSetOperations | 操作SortedSet类型数据 |
| redisTemplate | 通用的命令 |
快速入门
SpringBoot已经提供了对SpringDataRedis的支持
1.引入依赖
1 |
|
2.配置文件
配置redis的地址信息
1 | spring: |
3.注入RedisTemlpate
1 |
|
4.编写测试
1 |
|
运行发现name=”已修改”,但在虚拟机运行仍然是之前set的”777”,这是因为没有序列化。
reidsTemplate 的set方法接收的参数不是字符串,而是 Object ,其会将Object序列化为字节形式,又由于默认采用JDK序列化,比如上例的key是 name,name会被序列化,相当于重新存了一个新的键值对,在命令行查询结果如下:
1 | 192.168.23.128:6379> get name |
缺点
- 可读性差
- 内存占用大
Spring Boot 的测试默认会在测试方法执行完毕后回滚事务,以保证测试之间互不影响,不会产生脏数据。
linux中直接存和用redistemplate存,其实是存了两个,因为这俩序列化方式不一样,所以出现了两个
因此需要改变redisTemplate的序列化方式
自定义RedisTemplate的序列化方式
在
com/example/redis/config/下新建RedisConfig.java类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RedisConfig {
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建RedisTemplate对象
RedisTemplate<String, Object> stringObjectRedisTemplate = new RedisTemplate<>();
//设置连接工厂
stringObjectRedisTemplate.setConnectionFactory(connectionFactory);
//创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置key的序列化
stringObjectRedisTemplate.setKeySerializer(RedisSerializer.string());
stringObjectRedisTemplate.setHashKeySerializer(RedisSerializer.string());
//设置value的序列化
stringObjectRedisTemplate.setValueSerializer(jsonRedisSerializer);
stringObjectRedisTemplate.setHashValueSerializer(jsonRedisSerializer);
return stringObjectRedisTemplate;
}
}在test中修改
1
2
private RedisTemplate<String,Object> redisTemplate;成功修改正确的name
测试value
1
2
3
4
5
6
7
8
void testSaveUser(){
//写入数据
redisTemplate.opsForValue().set("user:7",new User("用户7",21));
//获取数据
User user7 = (User) redisTemplate.opsForValue().get("user:7");
System.out.println("user7 = " + user7);
}idea返回
1
user7 = User(name=用户7, age=21)
RESP中查看
1
2
3
4
5{
"@class": "com.example.redis.test.User",
"name": "用户7",
"age": 21
}
这样能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化,这会带来额外的内存开销。
简化处理:统一使用String序列化器。
string序列化器
要求只能存储String类型的keyh和value。当需要存储java对象时,手动完成对象的序列化和反序列化。
1 | private static final ObjectMapper mapper= new ObjectMapper(); |
没了…
