Spring Boot教程(28) – 简单读写Redis

Redis是最流行的key-value数据库,它最常用的功能就是缓存。就算你不是搞后端的,也会不断看到Redis相关的新闻和技术文章等,因为它性能强劲、功能丰富、生态完备,各种“吊打面试”的系列文章肯定少不了它。

如果仅仅是CRUD工程师来使用Redis的话,那需要掌握的东西寥寥几句话就可以概括,简单说就是80%的情况下你只需要用到20%的知识。我想更复杂的地方就是如何针对不同的业务进行数据结构的设计,这个就需要实践和经验了。如果你是在大厂,数据足够多,访问量足够大,可能会需要搭建Redis集群,这也是有现成工具和方案的。

可惜作为一个toB项目的CRUD的工程师,还没有机会将Redis的作用充分发挥出来,只能在这里啰嗦下它的基本使用。轮子丰富的Java生态和Spring全家桶,老早已经为你准备好了工具,Spring Data Redis可以帮你高效使用Redis,而且Spring Boot也提供了starter可以一键接入Redis,“0配置”就可以写代码。在介绍它之前,先复习下Redis的基本概念和命令,然后再来说说它的API的使用。

Redis的基本知识

Redis的官网是https://redis.io/,在3月宕机之前,它一直运行在DigitalOcean的一台5刀每月的机器上。官网说Redis是个内存数据库,也就是说数据库都是放在内存里的,这样就可以得到很高的读写速度。但并不意味着重启之后,数据就没了,Redis提供了持久化机制,你可以设置同步到磁盘的方式和频率。

仅仅从缓存的角度来讲,Redis就相当于一个Map,如果你有个单机系统而且缓存量比较小,可以直接创建个HashMap作为缓存,Caffeine Cache其实就是类似的实现。当你多台服务器都需要缓存的时候,就需要用Redis来共享缓存。而在多台服务器同时访问Redis的时候,请求是一个一个处理的,请求太多的话会阻塞,因为Redis只有一个线程处理请求,为啥呢?因为实现简单性能高,而且CPU基本不是Redis的瓶颈。

不同平台的包管理工具都可以方便的安装Redis,Ubuntu可以使用apt install redis-server,macOS可以使用brew install redis。安装完成之后就可以使用redis-cli命令连接Redis服务器,Redis的host可以用-h参数指定,默认是127.0.0.1,端口可以用-p参数指定,默认是是6379。默认情况下Redis是没有密码的,你可以在配置文件redis.conf中找到包含“#requirepass foobared”的一行,取消注释,修改密码,注意Redis的速度很快,密码的强度记得要大一点。而且要禁止公网访问,提高安全性。

key-value中的key一般都是字符串,而value的类型则多种多样,Redis中提供了多种类型:字符串、列表、哈希、集合和有序结合等。每一种数据类型都有其独特的命令来进行增删查改,命令我就不一一列举了,你需要找个简单的教程来逐个操作试试,建立感性认识。

Spring Boot如何操作Redis

Java有两个常用的Redis库,分别是LettuceJedis,如果你是通过spring-boot-starter-data-redis依赖引入Spring Data Redis的,那么它默认会带上Lettuce,当然你也可以换,Spring Data Redis提供了更高层次的抽象。

Spring Boot有几个基本信息你可能需要配置:

  • spring.redis.host 服务器地址,默认localhost
  • spring.redis.port Redis端口,默认6379
  • spring.redis.password 密码

开发环境下你可能都不需要配置,直接引入Spring Boot自动生成的RedisTemplate就能干活了。我们知道key-value中的value有很多类型,不同类型的操作都不太相同,比如操作列表集合哈希什么的。RedisTemplate为每种类型都准备了 *Operations类来进行对应的操作,可以通过RedisTemplate.opsFor*方法来获取。

其中最常用的是操作字符串类型的,通过opsForValue获取ValueOperations对象。后者有很多方法可以进行增删查改,每个方法基本都对应了一条Redis命令

下面就对Redis中的值进行最简单的修改:

图中的方法执行完毕之后,Redis中的”site”字段就被设置为了”fookwood.com”,但是如果在终端用redis-cli查看下结果,发现是乱码。这是为啥呢?因为默认情况下RedisTemplate会使用Java自带的序列化机制,将一个对象转化为byte数组。也就是上图中的set方法,会将两个字符串序列化称byte数组发送给Redis,所以会出现乱码的情况。可能从编程的角度来说这样没问题,代码也能正常读写对象,但是这样可读性很差,不方便在redis-cli或者其他Redis客户端中查看和修改。

对于key和value类型都是字符串的情况,Spring Boot还自动配置了一个StringRedisTemplate对象,它虽然也是给Redis发送的byte数组,但是这个byte数组是由String.getBytes(Charset)方法生成的,默认编码是UTF-8。这样就通用多了,如下图:

使用Jackson序列化

除了简单的字符串类型的key-value,在实际业务中,key基本都是字符串,比如说缓存商品信息,可能使用诸如”product:32345″这种形式的key,用来表示id为32345的商品信息。而value一般都会比较复杂,而且比较常用的实践就是将商品信息保存称JSON格式的。

你在代码中可能会使用一个Product类,为了让操作更方便,可以直接将Product对象传递给RedisTemplate,然后让它自己搞定对象到JSON的转化过程。幸运的是,可以借力Jackson来完成,我们之前介绍过Jackson如何使用

上图中自己定义了一个新的RedisTemplate对象,放到了容器里,这样Spring Boot就不会生成默认的RedisTemplate了(具体代码可以查看RedisAutoConfiguration)。新的RedisTemplate的key是String类型,value是Product类型,并设置了key serializer和value serializer,前者效果和StringRedisTemplate的效果是一样的。后者是Jackson专用的Jackson2JsonRedisSerializer,将对象序列化成JSON字符串。ObjectMapper是容器中已经存在的,如果你引入了web starter,Spring Boot已经为你配置好了ObjectMapper

接下来对于Redis读写操作则变得非常符合直觉:

可以看到在用redis-cli查看读取的时候,可以方便地看出value部分的内容,如果你用的是其他客户端(比如我用的Another Redis Desktop Manager),还可以对JSON进行格式化,更易读,更容易看出其结构。

在缓存的使用场景下,你可以先查询下Redis是否有数据,然后再去数据库查询,再将数据库的查询结果放到Redis中以备下次使用。如果你对数据库中的数据库有修改,记得及时更新Redis中的副本。之前我们介绍过Spring中缓存注解的使用,你可以在方法上添加注解以完成缓存的设置,再或者,使用其底层的RedisCacheManagerRedisCache。我还是更喜欢使用RedisTemplate,因为使用起来更灵活。

发表评论

电子邮件地址不会被公开。