第一步、导入坐标
<!-- 缓存 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.7.2.RELEASE</version> </dependency>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <context:property-placeholder location="classpath*:properties/*.properties" /> <!-- redis 相关配置 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="JedisConnectionFactory" /> </bean> </beans>
上方引入的redis的properties配置文件,一般改动一个ip即可(resources/properties/redis-config.properties)
# Redis settings
# server IP
redis.host=192.168.25.134
# server port
redis.port=6379
# server pass
redis.pass=
# use dbIndex
redis.database=0
# u63A7u5236u4E00u4E2Apoolu6700u591Au6709u591Au5C11u4E2Au72B6u6001u4E3Aidle(u7A7Au95F2u7684)u7684jedisu5B9Eu4F8B
redis.maxIdle=300
# u8868u793Au5F53borrow(u5F15u5165)u4E00u4E2Ajedisu5B9Eu4F8Bu65F6uFF0Cu6700u5927u7684u7B49u5F85u65F6u95F4uFF0Cu5982u679Cu8D85u8FC7u7B49u5F85u65F6u95F4(u6BEBu79D2)uFF0Cu5219u76F4u63A5u629Bu51FAJedisConnectionExceptionuFF1B
redis.maxWait=3000
# u5728borrowu4E00u4E2Ajedisu5B9Eu4F8Bu65F6uFF0Cu662Fu5426u63D0u524Du8FDBu884Cvalidateu64CDu4F5CuFF1Bu5982u679Cu4E3AtrueuFF0Cu5219u5F97u5230u7684jedisu5B9Eu4F8Bu5747u662Fu53EFu7528u7684
redis.testOnBorrow=true
当前SpringDataRedis环境已经搭建完毕,以下代码在RedisTest.java类中通过单元测试进行。
以下测试通过Spring整合junit进行。
准备:测试类SpringDataRedis
@RunWith(SpringJunit4ClassRunner.class) @ContextConfiguration("classpath:spring/applicationContext-redis.xml") public class SpringDataRedis{ @Autowired private RedisTemplate redisTemplate; //测试代码区 }
说明:下方的存取指的是通过一个key对以该key为依据存入redis值和取出redis中的值。
二、SpringDataRedis的使用
1、String类型的数据存取
1.1 set进一个值
@Test public void testSet(){ redisTemplate.boundValueOps("str").set("我是被存储的字符串!"); }
1.2 get到一个值
@Test public void testGet(){ String result=redisTemplate.boundValueOps("str").get(); System.out.println(result); }
1.3 delete一个key及其它所指向的值,所有类型都可以用
@Test public void testDelete(){ //此操作执行之后再获取该key指向的值时Java中的代码得到的时null redisTemplate.delete("str"); }
拓展:redis中设置键值的存活时间
2、Hash类型的存取
2.1 put一个值到HashMap中
@Test public void testPush(){ //Hash类型其实就是HashMap redisTemplate.boundHashOps("group").put("member1","HUAWEI"); redisTemplate.boundHashOps("group").put("member2","Alibaba"); }
2.2 get到一个指定key的值
@Test public void testGet(){ //所有存到redis中的对象全部需要序列化,并且取值时需要强转成自己需要的类型 String member1=(String)redisTemplate.boundHashOps("group").get("member1"); String member2=(String)redisTemplate.boundHashOps("group").get("member2"); System.out.println(member1); System.out.println(member2); }
2.3 获取被绑定key的所有键/大小/全部值
@Test public void testApi(){ redisTemplate.boundHashOps("group").keys();//返回一个Set,因为不允许重复 redisTemplate.boundHashOps("group").values();//返回一个List,因为可以重复 redisTemplate.boundHashOps("group").size();//返回一个Long数值 }
2.4 从Hash中删除键值
@Test public void testDelete(){ redisTemplate.boundHashOps("group").delete("member1");//删除指定成员的键值 redisTemplate.boundHashOps("group").delete();//删除Hash中指定key下的所有值 }
3、List类型的存取
3.1 push一个值到List中
@Test public void testPush(){ redisTemplate.boundListOps("hero").leftPush("乔峰"); redisTemplate.boundListOps("hero").leftPush("扫地僧"); redisTemplate.boundListOps("hero").leftPush("孙悟空"); }
3.2 pop出一个值
@Test public void testPop(){ redisTemplate.boundListOps("hero").rightPop();//此操作执行之后会从list中删除一个 redisTemplate.boundListOps("hero").rightPop(1,TimeUnit枚举变量);//每一个间隔弹一个 redisTemplate.boundListOps("hero").range(0,-1);//返回所有的值,但是不清除 }
3.3 remove删除操作
@Test public void testRemove(){ //redisTemplate.delete("hero");//这是删除全部的值 redisTemplate.boundListOps("hero").remove(删除个数,要被删除的值);//有API可以设置删除的方向 }
4、Set类型的存取
4.1 add实现添加值
@Test public void testAdd(){ //set不准有重复的key redisTemplate.boundSetOps("character").add("孙行者"); redisTemplate.boundSetOps("character").add("孙行者"); redisTemplate.boundSetOps("character").add("猪悟能"); redisTemplate.boundSetOps("character").add("沙悟净"); redisTemplate.boundSetOps("character").add("孙悟空"); redisTemplate.boundSetOps("character").add("唐玄奘"); }
4.2 获取Set里面的值
@Test public void testMember(){ Set<String> characters=redisTemplate.boundSetOps("character").members();//会返回一个Set for(String character : characters){ System.out.println(s); } Boolean member=redisTemplate.boundSetOps("character").isMember("如来"); //还有api如,取出一个随机的值randomMember(),取出两个diff差集。另外set也可以pop()一个值出来。 }
4.3 remove指定删除
@Test public void testRemove(){ redisTemplate.boundSetOps("character").remove("唐玄奘");//删除指定的值 redisTemplate.boundSetOps("character").delete();//清空所有 }
5、ZSet类型的存取
ZSet的有序是依据分值实现的,相当于权值。
5.1 add值到ZSet中
@Test public void testAdd(){ //key相同时为修改更新 redisTemplate.boundZSetOps("role").add("人民",10); redisTemplate.boundZSetOps("role").add("公民",20); redisTemplate.boundZSetOps("role").add("官员",30); redisTemplate.boundZSetOps("role").add("洋人",40); }
5.2 range取出所有的值
返回值仍为Set
@Test public void testRange(){ //不带分值的获取 Set<String> roles= redisTemplate.boundZSetOps("role").range(0,,-1); for(String role:roles){ System.out.println(role); } //不带分的获取全部 Set<TypedTupl> rolesTypeTupl=redisTemplate.boundZSetOps("role").rangeWithScore(0,-1)//指定获取的所有 for(TypedTupl role : rolesTypeTupl){ System.out.println(role.getValue+"@@@@@@"+role.getScore()); } //带分的获取 Set<TypedTupl> rolesTypeTuplWithScore=redisTemplate.boundZSetOps("role").rangeByScoreWithScore(10,40)//指定score范围 for(TypedTupl role : rolesTypeTuplWithScore){ System.out.println(role.getValue+"@@@@@@"+role.getScore()); } }
拓展:
1、redis的增、删、改、查中对于修改操作,在redis中key值相同时增加既是修改。
2、设置key的存活【expire(Long unit,TimeUnit timeUnit)//是枚举变量】
Redis常见数据结构使用场景
1. String
常用命令: set,get,decr,incr,mget 等。
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用; 常规计数:微博数,粉丝数等。
2.Hash
常用命令: hget,hset,hgetall 等。
Hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。
举个例子: 最近做的一个电商网站项目的首页就使用了redis的hash数据结构进行缓存,因为一个网站的首页访问量是最大的,所以通常网站的首页可以通过redis缓存来提高性能和并发量。我用jedis客户端来连接和操作我搭建的redis集群或者单机redis,利用jedis可以很容易的对redis进行相关操作,总的来说从搭一个简单的集群到实现redis作为缓存的整个步骤不难。感兴趣的可以看我昨天写的这篇文章:
《一文轻松搞懂redis集群原理及搭建与使用》: juejin.im/post/5ad54d…
3.List
常用命令: lpush,rpush,lpop,rpop,lrange等
list就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,最新消息排行等功能都可以用Redis的list结构来实现。
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
4.Set
常用命令: sadd,spop,smembers,sunion 等
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的。 当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同喜好、二度好友等功能。
5.Sorted Set
常用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用Redis中的SortedSet结构进行存储。
MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据(redis有哪些数据淘汰策略???)
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
Redis的并发竞争问题如何解决?
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。 2.服务器角度,利用setnx实现锁。
注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。