
在Java中使用redisTemplate操作缓存
背景
在最近的项目中,有一个需求是对一个很大的数据库进行查询,数据量大概在几千万条。但同时对查询速度的要求也比较高。
这个数据库之前在没有使用Presto的情况下,使用的是Hive,使用Hive进行一个简单的查询,速度可能在几分钟。当然几分钟也并不完全是跑SQL的时间,这里面包含发请求,查询数据并且返回数据的时间的总和。但是即使这样,这样的速度明显不能满足交互式的查询需求。
我们的下一个解决方案就是Presto,在使用了Presto之后,查询速度降到了秒级。但是对于一个前端查询界面的交互式查询来说,十几秒仍然是一个不能接受的时间。
虽然Presto相比Hive已经快了很多(FaceBook官方宣称的是10倍),但是对分页的支持不是很友好。我在使用的时候是自己在后端实现的分页。
在这种情况下应用缓存实属无奈之举。讲道理,优化应从底层开始,自底而上。上层优化的方式和效率感觉都很有局限。
为什么要使用缓存
前端查询中,单次查询的匹配数据量有可能会达到上百甚至上千条,在前端中肯定是需要分页展示的。就算每次查询10条数据,整个查询也要耗时6-8s的时间。想象一下,每翻一页等10s的场景。
所以,此时使用redis缓存。减少请求数据库的次数。将匹配的数据一并存入数据库。这样只有在第一次查询时耗费长一点,一旦查询完成,用户点击下一页就是毫秒级别的操作了。
使用redisTemplate
Spring封装了一个比较强大的模板,也就是redisTemplate,方便在开发的时候操作Redis缓存。在Redis中可以存储String、List、Set、Hash、Zset。下面将针对List和Hash分别介绍。
List
Redis中的List为简单的字符串列表,常见的有下面几种操作。
hasKey
判断一个键是否存在,只需要调用hasKey就可以了。假设这个Key是test,具体用法如下。
range
该函数用于从redis缓存中获取指定区间的数据。具体用法如下。
delete
删除某个键。
size
获取该键的集合长度。
leftPush
我们把存放这个值的地方想象成如图所示的容器。
并且取数据总是从左边取,但是存数据可以从左也可以从右。左就是leftPush,右就是rightPush。leftPush如下图所示。
用法如下。
控制台输出的结果如下。
leftPushAll
基本和leftPush一样,只不过是一次性的将List入栈。
当然你也可以这样
leftPushIfPresent
跟leftPush是同样的操作,唯一的不同是,当且仅当key存在时,才会更新key的值。如果key不存在则不会对数据进行任何操作。
leftPop
该函数用于移除上面我们抽象的容器中的最左边的一个元素。
值得注意的是,当返回为空后,在redis中这个key也不复存在了。如果此时再调用leftPushIfPresent,是无法再添加数据的。有代码有真相。
rightPush
rightPush如下图所示。
用法如下。
控制台输出的结果如下。
rightPushAll
同rightPush,一次性将List存入。
当然你也可以这样。
rightPushIfPresent
跟rightPush是同样的操作,唯一的不同是,当且仅当key存在时,才会更新key的值。如果key不存在则不会对数据进行任何操作。
rightPop
该函数用于移除上面我们抽象的容器中的最右边的一个元素。
与leftPop一样,返回空之后,再调用rightPushIfPresent,是无法再添加数据的。
index
获取list中指定位置的元素。
值得注意的有两点。一个是如果下标是-1的话,则会返回List最后一个元素,另一个如果数组下标越界,则会返回null。
trim
用于截取指定区间的元素,可能你会理解成与range是一样的作用。看了下面的代码之后应该就会立刻理解。
其实作用完全不一样。range是获取指定区间内的数据,而trim是留下指定区间的数据,删除不在区间的所有数据。trim是void,不会返回任何数据。
remove
用于移除键中指定的元素。接受3个参数,分别是缓存的键名,计数事件,要移除的值。计数事件可以传入的有三个值,分别是-1、0、1。
-1代表从存储容器的最右边开始,删除一个与要移除的值匹配的数据;0代表删除所有与传入值匹配的数据;1代表从存储容器的最左边开始,删除一个与要移除的值匹配的数据。
1
rightPopAndLeftPush
该函数用于操作两个键之间的数据,接受两个参数,分别是源key、目标key。该函数会将源key进行rightPop,再将返回的值,作为输入参数,在目标key上进行leftPush。具体代码如下。
Hash
存储类型为hash其实很好理解。在上述的List中,一个redis的Key可以理解为一个List,而在Hash中,一个redis的Key可以理解为一个HashMap。
put
用于写入数据。
putALl
用于一次性向一个Hash键中添加多个key。
putIfAbsent
用于向一个Hash键中写入数据。当key在Hash键中已经存在时,则不会写入任何数据,只有在Hash键中不存在这个key时,才会写入数据。
同时,如果连这个Hash键都不存在,redisTemplate会新建一个Hash键,再写入key。
get
用于获取数据。
值得注意的是,使用get函数获取的数据都是Object类型。
所以需要使用类型与上述例子中的布尔类型的话,则需要强制转换一次。List类型则可以使用fastjson这种工具来进行转换。转换的例子已列举在上述代码中。
delete
用于删除一个Hash键中的key。可以理解为删除一个map中的某个key。
values
用于获取一个Hash类型的键的所有值。
entries
用于以Map的格式获取一个Hash键的所有值。
hasKey
用于获取一个Hash键中是否含有某个键。
keys
用于获取一个Hash键中所有的键。
size
用于获取一个Hash键中包含的键的数量。
increment
用于让一个Hash键中的某个key,根据传入的值进行累加。传入的数值只能是double或者long,不接受浮点型
multiGet
用于批量的获取一个Hash键中多个key的值。
scan
获取所以匹配条件的Hash键中key的值。我查过一些资料,大部分写的是无法模糊匹配,我自己尝试了一下,其实是可以的。如下,使用scan模糊匹配hash键的key中,带SCAN的key。
引入redisTemplate
如果大家看懂了怎么用,就可以将redisTemplate引入项目中了。
引入pom依赖
新建配置文件
然后需要新建一个RedisConfig配置文件。
注入
将redisTemplate注入到需要使用的地方。
本文转载自微信公众号「SH的全栈笔记」
原文链接:https://mp.weixin.qq.com/s/jwXPFJAEm1QIFLg8RLFU8Q.
