第一步、導入坐標
<!-- 緩存 --> <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
# \u63A7\u5236\u4E00\u4E2Apool\u6700\u591A\u6709\u591A\u5C11\u4E2A\u72B6\u6001\u4E3Aidle(\u7A7A\u95F2\u7684)\u7684jedis\u5B9E\u4F8B
redis.maxIdle=300
# \u8868\u793A\u5F53borrow(\u5F15\u5165)\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u6700\u5927\u7684\u7B49\u5F85\u65F6\u95F4\uFF0C\u5982\u679C\u8D85\u8FC7\u7B49\u5F85\u65F6\u95F4(\u6BEB\u79D2)\uFF0C\u5219\u76F4\u63A5\u629B\u51FAJedisConnectionException\uFF1B
redis.maxWait=3000
# \u5728borrow\u4E00\u4E2Ajedis\u5B9E\u4F8B\u65F6\uFF0C\u662F\u5426\u63D0\u524D\u8FDB\u884Cvalidate\u64CD\u4F5C\uFF1B\u5982\u679C\u4E3Atrue\uFF0C\u5219\u5F97\u5230\u7684jedis\u5B9E\u4F8B\u5747\u662F\u53EF\u7528\u7684
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命令,但是需要注意一些問題。