@wangzhuanyun
2020-09-06T15:03:05.000000Z
字数 10320
阅读 2424
springboot
redis简介
redis是一个key-value。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)与范围查询, bitmaps, hyperloglogs和 地理空间(geospatial)索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)
Redis能做什么
缓存,毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效;
排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用Redis的SortSet数据结构能够非常方便搞定;
计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
注:限速器也是对请求限流的一种实现方式。
好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;
简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;
Session共享,默认Session是保存在服务器的文件中,即当前服务器,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。
redis特点
redis优点
redis安装(Windows系统)
下载包
https://github.com/MicrosoftArchive/redis/releases
解压到D盘目录下
在这个目录下cmd,然后直接redis-server.exe redis.windows.conf,即可启动redis
或新建记事本文件startup.bat,输入redis-server.exe redis.windows.conf
修改密码
解压目录下找到redis.windows.conf
大概在387行左右requirepass 下添加requirepass 密码,保存,重新运行即可
下载安装RedisDesktopManager,并连接redis(关闭防火墙并设置允许远程连接此电脑)
引入相关JAR
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-redis</artifactId><version>1.3.8.RELEASE</version></dependency># Redis数据库索引(默认为0)spring.redis.database=0# Redis服务器地址spring.redis.host=127.0.0.1# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=123456# 连接池最大连接数(使用负值表示没有限制)spring.redis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.pool.max-wait=-1# 连接池中的最大空闲连接spring.redis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=0
上述两个步骤完成后,SpringBoot自动在Spring容器中配置一个redisTemplate的Bean,所以可以直接使用redisTemplate
使用Spring封装的RedisTemplate操作redis
常用方法
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set
使用opsForValue操作字符串
存储字符串使用:redisTemplate.opsForValue().set("name","tom");结果:redisTemplate.opsForValue().get("name") 输出结果为tom------------------------------------------------------------------设置失效时间使用:redisTemplate.opsForValue().set("name","tom",10, TimeUnit.SECONDS);TimeUnit.DAYS //天TimeUnit.HOURS //小时TimeUnit.MINUTES //分钟TimeUnit.SECONDS //秒TimeUnit.MILLISECONDS //毫秒结果:redisTemplate.opsForValue().get("name")由于设置的是10秒失效,十秒之内查询有结果,十秒之后返回为null-------------------------------------------------------------------支持整型与浮点型(increment)使用:template.opsForValue().increment("sex",1);System.out.println(template.opsForValue().get("sex"));结果:1--------------------------------------------------------------------如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET使用:template.opsForValue().append("name"," hello");System.out.println(template.opsForValue().get("name"));结果:tom Hello--------------------------------------------------------------------截取key所对应的value字符串System.out.println("*********"+template.opsForValue().get("name",0,3));结果:tom--------------------------------------------------------------------返回key所对应的value值得长度System.out.println("***************"+template.opsForValue().size("key"));--------------------------------------------------------------------存储一个对象(此类必须先序列化实现接口Serializable)RedisSerializer rs = new StringRedisSerializer();redisTemplate.setStringSerializer(rs);ValueOperations ops = redisTemplate.opsForValue();ops.set("user",user);//放入redis//取出对象User setuser = (User) redisTemplate.opsForValue().get("user");
使用opsForList操作list
将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)redisTemplate.opsForList().leftPush("names","张三");redisTemplate.opsForList().leftPush("names","李四");--------------------------------------------------------------------将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从右边插入)redisTemplate.opsForList().rightPush("names","王五");redisTemplate.opsForList().rightPush("names","马六");--------------------------------------------------------------------获取集合长度redisTemplate.opsForList().size("names");--------------------------------------------------------------------返回存储在键中的列表的指定元素使用:System.out.println(redisTemplate.opsForList().range("list",0,-1));结果:[c#, c++, python, java, c#, c#]--------------------------------------------------------------------在列表中index的位置设置value值(如果index不存在则报错)redisTemplate.opsForList().set("names",1,"岳不群");--------------------------------------------------------------------批量把一个数组插入到列表中String[] stringarrays = new String[]{"1","2","3"};redisTemplate.opsForList().leftPushAll("listarray",stringarrays);--------------------------------------------------------------------从存储在键中的列表中删除等于值的元素的第一个计数事件count> 0:删除等于从头到尾移动的值的元素。count <0:删除等于从尾到头移动的值的元素。count = 0:删除等于value的所有元素redisTemplate.opsForList().remove("names",1,"王五");--------------------------------------------------------------------根据下表获取列表中的值,下标是从0开始的redisTemplate.opsForList().index("names",2);
使用opsForSet操作set
无序集合中添加元素,返回添加个数使用:String[] strarrays = new String[]{"strarr1","sgtarr2"};System.out.println(redisTemplate.opsForSet().add("setTest", strarrays));结果:2--------------------------------------------------------------------返回集合中的所有成员使用:System.out.println(redisTemplate.opsForSet().members("setTest"));结果:[ddd, bbb, aaa, ccc]--------------------------------------------------------------------移除集合中一个或多个成员使用:String[] strarrays = new String[]{"strarr1","sgtarr2"};System.out.println(redisTemplate.opsForSet().remove("setTest",strarrays));结果:2--------------------------------------------------------------------无序集合的大小长度使用:System.out.println(redisTemplate.opsForSet().size("setTest"));结果:1--------------------------------------------------------------------判断 ccc 元素是否是集合 key 的成员使用:System.out.println(redisTemplate.opsForSet().isMember("setTest","ccc"));结果:true--------------------------------------------------------------------随机获取key无序集合中的一个元素redisTemplate.opsForSet().randomMember("setTest");
使用opsForZSet操作有序set
新增一个有序集合,存在的话为false,不存在的话为true使用:System.out.println(template.opsForZSet().add("zset1","zset-1",1.0));结果:true--------------------------------------------------------------------从有序集合中移除一个或者多个元素使用:System.out.println(template.opsForZSet().range("zset1",0,-1));System.out.println(template.opsForZSet().remove("zset1","zset-6"));System.out.println(template.opsForZSet().range("zset1",0,-1));结果:[zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]1[zset-1, zset-2, zset-3, zset-4, zset-5]--------------------------------------------------------------------增加元素的score值,并返回增加后的值使用://原为1.1System.out.println(template.opsForZSet().incrementScore("zset1","zset-1",1.1));结果:2.2--------------------------------------------------------------------通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列使用:System.out.println(redisTemplate.opsForZSet().add("zset1","zset-1",1.0));System.out.println(redisTemplate.opsForZSet().add("zset1","zset-2",6.0));System.out.println(redisTemplate.opsForZSet().add("zset1","zset-3",8.0));System.out.println(redisTemplate.opsForZSet().add("zset1","zset-4",4.0));System.out.println(redisTemplate.opsForZSet().add("zset1","zset-5",10.0));System.out.println(redisTemplate.opsForZSet().add("zset1","zset-6",2.0));System.out.println(redisTemplate.opsForZSet().rangeByScore("zset1",0,5));结果:[zset-1, zset-6, zset-4]--------------------------------------------------------------------通过分数返回有序集合指定区间内的成员个数使用:System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));System.out.println(template.opsForZSet().count("zset1",0,5));结果:[zset-1, zset-6, zset-4]3--------------------------------------------------------------------获取有序集合的成员数(zCard与size一样)使用:System.out.println(template.opsForZSet().size("zset1"));结果:6--------------------------------------------------------------------获取指定成员的score值使用:System.out.println(template.opsForZSet().score("zset1","zset-1"));结果:1.0
- 两者的关系是StringRedisTemplate继承RedisTemplate
- 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
- (序列化类)RedisTemplate使用的是JdkSerializationRedisSerializer 存入数据会将数据先序列化成字节数组然后在存入Redis数据库。 StringRedisTemplate使用的是StringRedisSerializer
使用时注意事项:
当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。
但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
RedisTemplate使用时常见问题
redisTemplate 中存取数据都是字节数组。当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null
//向redis里存入数据和设置缓存时间stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS);//val做-1操作stringRedisTemplate.boundValueOps("test").increment(-1);//根据key获取缓存中的valstringRedisTemplate.opsForValue().get("test")//val +1stringRedisTemplate.boundValueOps("test").increment(1);//根据key获取过期时间stringRedisTemplate.getExpire("test")//根据key获取过期时间并换算成指定单位stringRedisTemplate.getExpire("test",TimeUnit.SECONDS)//根据key删除缓存stringRedisTemplate.delete("test");//检查key是否存在,返回boolean值stringRedisTemplate.hasKey("546545");//向指定key中存放set集合stringRedisTemplate.opsForSet().add("red_123", "1","2","3");//设置过期时间stringRedisTemplate.expire("red_123",1000 , TimeUnit.MILLISECONDS);//根据key查看集合中是否存在指定数据stringRedisTemplate.opsForSet().isMember("red_123", "1")//根据key获取set集合stringRedisTemplate.opsForSet().members("red_123");**使用**@RestController@RequestMapping("/user")public class UserResource {@Autowiredpublic StringRedisTemplate stringRedisTemplate;@RequestMapping("/num")public String countNum() {String userNum = stringRedisTemplate.opsForValue().get("userNum");if(StringUtils.isNull(userNum)){stringRedisTemplate.opsForValue().set("userNum", userService.countNum().toString());}return userNum;}