特征:
- 键值(key-value)型,value支持多种不同数据结构,功能丰富
- 单线程,每个命令具备原子性
- 低延迟,速度快(基于内存、IO多路复用、良好的编码)。
- 支持数据持久化
- 支持主从集群、分片集群
- 支持多语言客户端
适用场景:
-
对数据高并发的读写
-
海量数据的读写
-
对数据高可扩展性的
不适用:
-
需要事务支持
-
基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
-
(用不着sql的和用了sql也不行的情况,请考虑用NoSql**)**
常见通用命令
-
redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
-
redis-check-aof:修复有问题的AOF文件
-
redis-check-dump:修复有问题的dump.rdb文件
-
redis-sentinel:Redis集群使用
-
redis-server:Redis服务器启动命令
-
redis-cli:客户端,操作入口
在https://redis.io/commands 中可以查看常用的redis命令
- KEYS:查看符合模板的所有key
- DEL:删除一个指定的key
- EXISTS:判断key是否存在
- EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
- TTL:查看一个KEY的剩余有效期
- dbsize查看当前数据库的key的数量
- flushdb清空当前库
- flushall通杀全部库
通过help [command] 可以查看一个命令的具体用法
key的结构设计
Redis的key允许有多个单词形成层级结构,多个单词之间用’:'隔开,格式如下:
这个格式并非固定,也可以根据自己的需求来删除或添加词条。
例如我们的项目名称叫 heima,有user和product两种不同类型的数据,我们可以这样定义key:
-
user相关的key:heima:user:1
-
product相关的key:heima:product:1
五大数据类型
String
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自增并指定步长,例如:incrby num 2 让num值自增2
-
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
-
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
-
SETEX:添加一个String类型的键值对,并且指定有效期
Hash类型
又称为散列,value是一个无需字典,类似于Java中的HashMap结构
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
例如:
KEY | VALUE | |
---|---|---|
field | value | |
heima:user:1 | name | Jack |
age | 21 | |
heima:user:2 | name | Rose |
age | 18 |
hash常见命令:
-
HSET key field value:添加或者修改hash类型key的field的值
-
HGET key field:获取一个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类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
-
有序
-
元素可以重复
-
插入和删除快
-
查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
常见命令
-
LPUSH key element … :向列表左侧插入一个或多个元素
-
LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
-
RPUSH key element … :向列表右侧插入一个或多个元素
-
RPOP key:移除并返回列表右侧的第一个元素
-
LRANGE key star end:返回一段角标范围内的所有元素
-
BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
Set
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
- 无序
- 元素不可重复
- 查找快
- 支持交集、并集、差集等功能
常见命令
- SADD key member … :向set中添加一个或多个元素
- SREM key member … : 移除set中的指定元素
- SCARD key: 返回set中元素的个数
- SISMEMBER key member:判断一个元素是否存在于set中
- SMEMBERS:获取set中的所有元素
- lSADD key member … :向set中添加一个或多个元素
- SREM key member … : 移除set中的指定元素
- SCARD key: 返回set中元素的个数
- SISMEMBER key member:判断一个元素是否存在于set中
- SMEMBERS:获取set中的所有元素
- SINTER key1 key2 … :求key1与key2的交集
SorterSet(Zset)
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
-
hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
-
跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
跳跃表:
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
2、实例
对比有序链表和跳跃表,从链表中查询出51
(1) 有序链表
要查找值为51的元素,需要从第一个元素开始依次查找、比较才能找到。共需要6次比较。
(2) 跳跃表
从第2层开始,1节点比51节点小,向后比较。
21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
在第0层,51节点为要查找的节点,节点被找到,共查找4次。
从此可以看出跳跃表比有序链表效率要高
SortedSet具备下列特性:
-
可排序
-
元素不重复
-
查询速度快
常见命令
-
ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
-
ZREM key member:删除sorted set中的一个指定元素
-
ZSCORE key member : 获取sorted set中的指定元素的score值
-
ZRANK key member:获取sorted set 中的指定元素的排名
-
ZCARD key:获取sorted set中的元素个数
-
ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
-
ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
-
ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
-
ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
-
ZDIFF、ZINTER、ZUNION:求差集、交集、并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可
基于jedis在Java中操作redis
Jedis的官网地址: https://github.com/redis/jedis
使用步骤
Jedis使用的基本步骤:
1.引入依赖
2.创建Jedis对象,建立连接
3.使用Jedis,方法名与Redis命令一致
4.释放资源
-
导入依赖
<**dependency**> <**groupId**>redis.clients</**groupId**> <**artifactId**>jedis</**artifactId**> <**version**>3.7.0</**version**> </**dependency**>
-
建立连接
@BeforeEach void setUp(){ //建立连接 jedis = new Jedis("192.168.150.101", 6379); //设置密码 jedis.auth("123321"); //选择库 jedis.select(0); }
-
测试string
@Test void testString(){ // 插入数据,方法名称就是redis命令名称,非常简单 String result = jedis.set("name", "张三"); System.out.println("result = " + result); // 获取数据 String name = jedis.get("name"); System.out.println("name = " + name); }
-
释放资源
@AfterEach void tearDown(){ //释放资源 if(jedis != null){ jedis.close(); } }
jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐使用Jedis连接池代替Jedis的直连方式
public class JedisConnectionFactory
{
private static final JedisPool jedisPool;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大连接
jedisPoolConfig.setMaxTotal(8);
// 最大空闲连接
jedisPoolConfig.setMaxIdle(8);
// 最小空闲连接
jedisPoolConfig.setMinIdle(0);
// 设置最长等待时间, ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool = new JedisPool(jedisPoolConfig, "192.168.150.101", 6379,
1000, "123321");
}
// 获取Jedis对象
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
SpringDataRedis
简介:
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,
官网地址:https://spring.io/projects/spring-data-redis
-
提供了对不同Redis客户端的整合(Lettuce和Jedis)
-
提供了RedisTemplate统一API来操作Redis
-
支持Redis的发布订阅模型
-
支持Redis哨兵和Redis集群
-
支持基于Lettuce的响应式编程
-
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
-
支持基于Redis的JDKCollection实现
不同数据类型对应的操作api
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用的命令 |
使用步骤
1.引入spring-boot-starter-data-redis依赖
2.在application.yml配置Redis信息
3.注入RedisTemplate
-
引入依赖
<!--Redis依赖--> <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>
-
配置文件
spring: redis: host: 192.168.150.101 port: 6379 password: 123321 lettuce: pool: max-active: 8 # 最大连接 max-idle: 8 # 最大空闲连接 min-idle: 0 # 最小空闲连接 max-wait: 100 # 连接等待时间
-
注入并测试
@SpringBootTest public class RedisTest { @Autowired private RedisTemplate redisTemplate; @Test void testString() { // 插入一条string类型数据 redisTemplate.opsForValue().set("name", "李四"); // 读取一条string类型数据 Object name = redisTemplate.opsForValue().get("name"); System.out.println("name = " + name); } }
序列化方式
RedisTemplate可以接收任意Object作为值写入Redis,但是写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
他的可读性差、内存占用较大
可以通过定义工厂中的RedisTemplate的序列方式
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException
{
// 创建Template
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer =
new GenericJackson2JsonRedisSerializer();
// key和 hashKey采用 string序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和 hashValue采用 JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
使用 @Configuration 注解将 RedisConfig 类定义为配置类,使用 @Bean 注解将 redisTemplate 方法注册为 Bean 对象。
在上面配置类的方法中,创建了一个 JSON 序列化工具叫作 GenericJackson2JsonRedisSerializer(),这个类需要一个 Jackson 依赖,因此还需要在 pom 文件中引入依赖:
<!--Jackson 依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
但是为了在反序列化的过程中,得知对象的类型,但是如图在存储时将class类型写入了redis,造成了额外的内存浪费
StringRedisTemple进行序列化
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
- 为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。
- 但是当需要存储Java对象时,手动完成对象的序列化和反序列化。
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testStringTemplate() throws JsonProcessingException {
// 准备对象
User user = new User("虎哥", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入一条数据到redis
stringRedisTemplate.opsForValue().set("user:200", json);
// 读取数据
String val = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化
User user1 = mapper.readValue(val, User.class); System.out.println("user1 = " + user1);
}