Redis

Redis

SQL:关系型数据库

NoSQL:非关系型数据库

image-20221112160510291

1. 初识redis

Redis诞生于2009年全称是Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库;

Redis特征:

  • 键值(key-value)型,value支持多种不同数据结构,功能丰富;
  • 多线程,每个命令具备原子性;
  • 低延迟,速度快(基于内存IO,多路复用,良好的编码);
  • 支持数据持久化;
  • 支持主从集群,分片集群;
  • 支持多语言客户端;

2. Redis命令

2.1Redis通用命令

通用指令是部分数据类型的,都可以使用的指令,常见的有:

  • KEYS:查看符合模板的所有key,不建议在生产环境设备上指定;

  • DEL:删除一个指定的KEY;

  • EXISTS:判断key是否存在;

  • EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除;

  • TTL:查看一个key的剩余有效期;

通过help[command]可以查看一个命令的具体用法,例如

​ help keys;

2.2 String类型

String类型,也就是字符串类型,是Redis中最简单的存储类型;

其value是字符串,不过根据字符串的格式不同,又可以分为3类:

  • String:普通字符串;
  • int:整数类型,可以做自增,自减操作;
  • float:浮点类型,可以做自增,自减操作;

不管是哪种格式,底层都是字节数组形式存储,只不过编码方式不同,字符串类型的最大空间不能超过512m;

image-20221113223704390

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类型的键值对,并且指定有效期;

2.3 Redis的key的结构

Redis的key允许有多个单词形成层级结构,多个单词之间用':'隔开,格式如下:

​ 项目名:业务名:类型:id

image-20221113232234358

2.4 Hash类型

hash类型,也叫散列,其value是一个无序字典,类似于java中的HashMap结构;

String结构是将对象序列化为JSON字符串后存储,当需要修改某个字段时很不方便;

image-20221113233235810

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD;

image-20221113233411052

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不存在,否则不执行;

2.5 List类型

Redis中的List类型与java中的LinkedList类似,可以看做是一个双向链表,既可以支持正向检索和也可以支持反向检索;

特征也与LinkedList类似:

  • 有序
  • 元素可以重复
  • 插入和删除快
  • 查询速度一般

常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等;

List的常见命令

  • LPUSH key element ...:向列表左侧插入一个或多个元素;
  • LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil;
  • RPUSH key element ...:向列表右侧插入一个或多个元素;
  • RPOP key:移除并返回列表右侧的第一个元素;
  • LRANGE key star end:返回一段角标范围内的所有元素;
  • BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil;

image-20221114004457534

思考:

如何利用List结构模拟一个栈?

  • 入口和出口在同一边

如何利用List结构模拟一个栈?

  • 入口和出口在不同边

如何利用List结构模拟一个阻塞队列?

  • 入口和出口在不同边
  • 出队时采用BLPOP或BRPOP

2.6 Set类型

Redis的Set结构与java中HashSet类似,可以看做是一个value为null的HashMap,因为也是一个hash表,具备与hashSet类似的特征:

  • 无序
  • 元素不可重复
  • 查找快
  • 支持交集,并集,差集等功能

Set类型的常见命令有:

  • SADD key member ... :想set中添加一个或多个元素;

  • SREM key member ... :移除set中的指定元素;

  • SCARD key:返回set中元素的个数;

  • SISMEMBER key member:判断一个元素是否存在于set中;

  • SMEMBERS: 获取set中的所有元素;

  • SINTER key1 key2 ... :求key1与key2的交集;

  • SDIFF key1 key2 ... :求key1与key2的差集;

  • SUNION key1 key2... : 求key1和key2的并集;

2.7 SortedSet类型

Redis的SortedSet是一个可排序的set集合,与java中的TreeSet有些类似,但底层数据结构却差别很大,SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表.

SortedSet具备下列特性:

  • 可排序
  • 元素不重复
  • 查询速度快

因为SortedSet的可排序性,经常被用来实现排行榜这样的功能;

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,ZINTTER,ZUNION:求差集,交集,并集

注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可;

3. Redis的java客户端

image-20221114151625368

3.1 Jedis

Jedis的官网地址: https://github.com/redis/jedis

3.1.1 jedis使用的基本步骤

  1. 引入依赖

    copy
    <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.0</version> </dependency>
  2. 创建Jedis对象,建立连接

copy
private Jedis jedis; @BeforeEach void setUp() { //建立连接 jedis = new Jedis("192.168.17.128", 6379); //设置密码 jedis.auth("123456"); //选择库 jedis.select(0); }
  1. 使用Jedis,方法名与Redis命令一致;
copy
//测试String @Test void testString() { //存入数据 String result = jedis.set("name", "虎哥"); System.out.println("result=" + result); //获取数据 String name = jedis.get("name"); System.out.println("name=" + name); }
  1. 释放资源
copy
@AfterEach void tearDown() { if (jedis != null) { jedis.close(); } }

3.1.2 Jedis连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,推荐使用Jedis连接池代替Jedis的直连方式;

copy
public class JedisConnectionFactory { private static final JedisPool jedisPool; static { //配置连接池 JedisPoolConfig poolConfig = new JedisPoolConfig(); //设置最大连接 poolConfig.setMaxTotal(8); //最大空闲连接 poolConfig.setMaxIdle(8); //最小空闲连接 poolConfig.setMaxIdle(0); //设置最长等待事件,ms poolConfig.setMaxWaitMillis(1000); //创建连接池对象 jedisPool = new JedisPool(poolConfig, "192.168.17.128", 6379, 1000, "123456"); } //获取jedis对象 public static Jedis getJedis(){ return jedisPool.getResource(); } }

3.2 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实现;

3.2.1 SpringDataRedis快速入门

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作,并且将不同数据类型的操作API封装到了不同的类型中:

image-20221114191559325

SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:

  1. 引入依赖
copy
<!--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>
  1. 在application.yml配置redis信息
copy
spring: redis: host: 192.168.17.128 port: 6379 password: 123456 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 100
  1. 注入RedisTemplate
copy
@Autowired private RedisTemplate redisTemplate;
  1. 编写测试
copy
@Test void contextLoads() { //插入一条String类型数据 redisTemplate.opsForValue().set("name", "大哥哥"); //读取一条String类型数据 Object name = redisTemplate.opsForValue().get("name"); System.out.println("name:"+name); }

3.2.2 SpringDataRedis的序列化方式

RedisTemplate可以接收任意Object作为值写入Redis,只不过写入钱会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是:\xAC\xED\x00\x05t\x00 \xE5\xA4\xA7\xE5\x93\xA5\xE5\x93\xA5

缺点:1. 可读性差 2.内存占用较大

我们可以自定义RedisTemplate的序列化方式,代码如下:

copy
@Configuration public class RedisConfig { @Bean 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; } }

尽管JSON的序列方式可以满足我们的需求,但任然存在一些问题,如图:

image-20221115000505484

为了在返序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销;

为了节省内存空间我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value,当需要存储java对象时,手动完成对象的序列化和反序列化;

Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式,省去了自定义RedisTemplate的过程:

copy
@SpringBootTest class RedisStringTests { @Autowired private StringRedisTemplate stringRedisTemplate; @Test void contextLoads() { //插入一条String类型数据 stringRedisTemplate.opsForValue().set("name", "大哥哥"); //读取一条String类型数据 Object name = stringRedisTemplate.opsForValue().get("name"); System.out.println("name:" + name); } private static final ObjectMapper mapper = new ObjectMapper(); @Test void testSaveUser() throws JsonProcessingException { //创建对象 User user = new User("小哥哥", 18); //手动序列化 String json = mapper.writeValueAsString(user); //写入数据 stringRedisTemplate.opsForValue().set("user:200", json); //获取数据 String jaonUser = stringRedisTemplate.opsForValue().get("user:200"); //手动反序列化 User user1 = mapper.readValue(jaonUser, User.class); System.out.println("User:" + user1); } @Test void testHash() { stringRedisTemplate.opsForHash().put("user:400", "name", "wz小哥哥"); stringRedisTemplate.opsForHash().put("user:400", "age", "18"); Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400"); System.out.println("entries=" + entries); } }

3.2.3 RedisTemplate的两种序列化实践方案:

方案一:

  1. 自定义RedisTemplate;
  2. 修改Redis Template的序列化器为GenericJackson2JsonRedisSerializer;

方案二:

  1. 使用StringRedisTemplate;
  2. 写入Redis时,手动把对象序列化为 JSON;
  3. 读取Redis时,手动把读取到的JSON反序列化为对象;

posted @   世间美好皆不及你  阅读(131)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起