Redis6大基礎資料結構以及在spring中的常用命令
Redis6大基礎資料結構:
1.字串
2.雜湊
3.連結串列
4.集合
5.有序集合
6.基數
字串:
最基礎的資料結構,以鍵值對形式儲存於Redis內部,相當於Map,通過鍵去找值。
如果是操作字串redisTemplate的keySerializer和valueSerializer引數都必須是stringRedisSerializer,如果是jdk序列化器那麼Redis儲存的將不會是數字而是異常。
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory"></property> <property name="keySerializer" ref="stringRedisSerializer"></property> <property name="valueSerializer" ref="stringRedisSerializer"></property> </bean>
字串分兩種情況:
1.不是數字 2是數字
在Spring中,redisTemplate.opsForValue()返回的物件可以操作簡單鍵值對。
1.不是數字
redisTemplate.opsForValue().set("key1","111"); //儲存鍵為key1,值為111的鍵值對 //redisTemplate.delete("key1"); //刪除鍵為key1的鍵值對 //Long length=redisTemplate.opsForValue().size("key1"); //求長度 //String old_value = (String) redisTemplate.opsForValue().getAndSet("key1","new_111");//設定新值並返回舊值 //String str = (String) redisTemplate.opsForValue().get("key1");//通過key獲取值 //String str = (String) redisTemplate.opsForValue().get("key1",0,1);//求子串 //int number = redisTemplate.opsForValue().append("key1","append");追加字串到末尾,返回新串長度 //System.out.println(old_value); System.out.println(str); //System.out.print(length);
2.是數字
注意的是:Redis支援簡單的運算,如原欄位加上1和整數,或者原子段減去1或者整數,還有一個是原欄位加上浮點數,原欄位可以是浮點數或者整數。注意關於所有的減法,原有值都必須是整數。注意:無法減去一個double型別的數
public class test { @Test public void xx(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class); //redisTemplate.opsForValue().set("i","9"); //redisTemplate.opsForValue().increment("i",1);//增加值,可以long或者double,但是減法不行 //redisTemplate.opsForValue().increment("i",1.2); //redisTemplate.getConnectionFactory().getConnection().decr(redisTemplate.getKeySerializer().serialize("i")); // 減去1這個可以實現 //redisTemplate.getConnectionFactory().getConnection().decrBy(redisTemplate.getKeySerializer().serialize("i") // ,1);//redisTemplate並沒有支援減法。 printValue(redisTemplate,"i"); } public void printValue(RedisTemplate redisTemplate, String key){ String i =(String) redisTemplate.opsForValue().get(key); System.out.print(i); } }
雜湊:
是String型別的field和value的對映表,實際在Redis記憶體中都是一個個字串,hash的鍵值對在記憶體中是無序的狀態,我們可以通過鍵找到對應的值
需要將spring提供的預設序列化器,即defaultSerializer修改為字串序列化器 因為spring對hash結構的操作中會涉及map等其他類操作,需要明確他的規則。
由於spring對redis進行了封裝,所以需要對redistemplate的配置項進行修改:
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="defaultSerializer" ref="stringRedisSerializer"></property>
<property name="keySerializer" ref="stringRedisSerializer"></property>
<property name="valueSerializer" ref="stringRedisSerializer"></property>
</bean>
如果相對hash結構制定序列化器可以使用hashKeySerializer和hashValueSerializer為hash的filed和value制定序列化器。
注意:
hmset前提是map儲存了多個鍵值對
hgetall會返回所有的鍵值對
hincrby和bincrbyFloat都採用increment方法
redisTemplate.opsForHash().values(key)相當於hvals命令,他會返回所有的值儲存到List物件中,而redisTemplate.opsForHash().keys(key)相當於hkeys,獲取所有的鍵,儲存到Set中
redisTemplate.opsForHash().putall()相當於執行了hmset命令,使用了map,是根據預設的序列化器(字串),所以會用字串來進行轉化,這樣才可以執行對應的數值加法。
public class test {
@Test
public void xx() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
String key = "hash";
Map<String,String> map = new HashMap<String,String>();
map.put("f1","val1");
map.put("f2","11");
//redisTemplate.opsForHash().putAll(key,map);//相當於hmset,新增一個map
//redisTemplate.opsForHash().put(key,"f3","val3");相當於hset,新增指定map的一個鍵值對
//boolean exists = redisTemplate.opsForHash().hasKey(key,"f3");相當於hexists key filed 查詢指定map的鍵值對是否存在
//Map<String,String> map1 =redisTemplate.opsForHash().entries(key);相當於hgetall 返回指定map全部鍵值對
//redisTemplate.opsForHash().increment(key,"f2",2);//相當於hincrby,給指定欄位加上一個整數
//redisTemplate.opsForHash().increment(key,"f2",2.2);//相當於hincrbyfloat,給指定欄位新增一個浮點數,
//Object value = redisTemplate.opsForHash().get(key,"f2");//獲取指定map中的鍵值對
//List<String> list = redisTemplate.opsForHash().values(key);相當於hkeys,獲取指定map的values的list集合
//Set keyList = redisTemplate.opsForHash().keys(key);相當於hkeys,獲取指定map的key值
/*List<String> list =new ArrayList<String>();
list.add("f1");
list.add("f2");
List values = redisTemplate.opsForHash().multiGet(key,list);相當於hmget,獲取指定map的指定多個key對應的value值*/
//System.out.print(values);
// boolean success= redisTemplate.opsForHash().putIfAbsent(key,"f4","val4");相當於hsetnx,如果指定map不存在對應的鍵,才設定值
//Long result = redisTemplate.opsForHash().delete(key,"f1","f2");相當於hdel,刪除指定map中的指定鍵值對
System.out.print(result);
}
}
連結串列操作:
一般儲存字串,是有序的,是雙向的,可以儲存2的32次方-1節點,redis的連結串列是雙向的。
連結串列操作有程序安全和不安全的
不安全:
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
redisTemplate.delete("list");//刪除連結串列
redisTemplate.opsForList().leftPush("list","node1");//插入節點,從頭插入
redisTemplate.opsForList().leftPush("list","node2");//插入節點
/*List<String> nodeList = new ArrayList<String>();
for(int i=0;i<2;i++){
nodeList.add("node:"+i);
}*/
//redisTemplate.opsForList().leftPushAll("list",nodeList);//從左插入連結串列
//redisTemplate.opsForList().rightPush("list","node4");//從右邊插入一個節點
//String node1 =(String) redisTemplate.opsForList().index("list",0);獲取下標為0的節點
//String lpop =(String) redisTemplate.opsForList().rightPop("list");從左邊彈出一個節點
//String rpop =(String) redisTemplate.opsForList().leftPop("list");從右邊彈出一個節點
/*redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),
RedisListCommands.Position.BEFORE,
"node1".getBytes("utf-8"),
"before_node".getBytes("utf-8")
);//使用linsert命令在node1前插入一個節點
*/
/* redisTemplate.getConnectionFactory().getConnection().lInsert("list".getBytes("utf-8"),
RedisListCommands.Position.AFTER,
"node1".getBytes("utf-8"),
"after_node".getBytes("utf-8")
);//使用linsert命令在node1後插入一個節點*/
//redisTemplate.opsForList().leftPushIfPresent("list","head");//判斷list是否存在,如果存在則從左邊插入head節點
//redisTemplate.opsForList().rightPushIfPresent("list","end");//判斷list是否存在,如果存在則從右邊插入end節點
//List values = redisTemplate.opsForList().range("list",0,1);//從左到右獲取指定的節點
//redisTemplate.opsForList().remove("list",2,"node1");//從左到右刪除至多3個node節點
//redisTemplate.opsForList().set("list",0,"new_node");//給下標為0的節點設定新值
printList(redisTemplate,"list");
}
public void printList(RedisTemplate redisTemplate,String key){
Long size = redisTemplate.opsForList().size(key);//獲取指定list節點數
List values = redisTemplate.opsForList().range(key,0,size);//獲取指定連結串列的內容
System.out.print(values);
}
}
以上代表的是redisTemplate對redis連結串列的操作,left代表左操作,right代表右操作,但是有些命令使用redisTemplate並不支援,比如linsert命令,這個時候需要更底層的命令執行。
如果是多值操作,一般都是使用list封裝,然後leftPushAll的方式插入redis
對於連結串列的阻塞操作,也可以實現,為什麼要阻塞呢?因為併發的時候防止多個程序對同一塊資料程序讀寫操作,發生資料安全和一致性的問題。
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
redisTemplate.delete("list1");//刪除連結串列 為了重複資料
redisTemplate.delete("list2");//刪除連結串列 為了重複資料
List<String> list1 = new ArrayList<String>();
for(int i=0;i<5;i++){
list1.add("1node:"+i);
}//初始化
redisTemplate.opsForList().leftPushAll("list1",list1);
/* redisTemplate.opsForList().leftPop("list",1, TimeUnit.SECONDS);
redisTemplate.opsForList().rightPop("list",1, TimeUnit.SECONDS);//使用引數超市時間作為阻塞命令區別*/
List<String> list2 = new ArrayList<String>();
for(int i=0;i<5;i++){
list2.add("2node:"+i);
}//初始化
redisTemplate.opsForList().leftPushAll("list2",list2);
//redisTemplate.opsForList().rightPopAndLeftPush("list1","list2");//彈出list1最右邊的節點,插入到list2最左邊
//redisTemplate.opsForList().rightPopAndLeftPush("list1","list2",1,TimeUnit.SECONDS);//同上,但是他是阻塞的,意思就是併發
printList(redisTemplate,"list1");
printList(redisTemplate,"list2");
}
public void printList(RedisTemplate redisTemplate,String key){
Long size = redisTemplate.opsForList().size(key);//獲取指定list節點數
List values = redisTemplate.opsForList().range(key,0,size);//獲取指定連結串列的內容
System.out.println(values);
}
}
阻塞和非阻塞區別:通過設定超時引數進行區分
集合:
不是一個線性結構,而是一個雜湊表結構,所以他的插入刪除和查詢的時間複雜度都是O(1)
1.對於集合而言,他的每一個元素都是不能重複的,插入相同記錄會失敗
2.無序的
3.每一個元素都是String型別的
集合還有額外的操作,如兩個或者以上集合的交集,差集和並集
public class test {
@Test
public void xx() throws UnsupportedEncodingException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Set set = null;
//redisTemplate.boundSetOps("set1").add("v1","v2","v3");將元素加入列表
//redisTemplate.boundSetOps("set2").add("s1","s2","s3");
//Long length = redisTemplate.opsForSet().size("set1");求集合長度
//set = redisTemplate.opsForSet().difference("set2","set1");求set2中有,但是set1中沒有的
//set = redisTemplate.opsForSet().intersect("set1","set2");求並集
//boolean exists = redisTemplate.opsForSet().isMember("set1","v1");判斷是否存在
//set = redisTemplate.opsForSet().members("set1");//獲取集合所有元素
//String name = (String) redisTemplate.opsForSet().pop("set1");//隨機彈出一個元素
//String name = (String) redisTemplate.opsForSet().randomMember("set1");隨機獲取集合中的元素
//List list = redisTemplate.opsForSet().randomMembers("set1",2L);隨機獲取2個集合的元素
//redisTemplate.opsForSet().remove("set1","v1");//刪除一個集合的元素,引數可以有多個
//set = redisTemplate.opsForSet().union("set1","set2");//兩個集合的並集
redisTemplate.opsForSet().differenceAndStore("set1","set2","diff_set");//求差集,儲存到diff_set
redisTemplate.opsForSet().intersectAndStore("set1","set2","inter_set");//求交集,儲存到inter_set
redisTemplate.opsForSet().unionAndStore("set1","set2","union_set");//求並集,儲存到union_set
}
}
還有有序集合:
他有個分數是浮點數根據這個來排序的。
有序集合操作的時候需要封裝,DefaultTypedTuple預設實現類實現TypedTuple介面,將帶有分數的有序集合的值和分數封裝到這個類,通過這個類物件對去對於的值和分數。
對有序集合的封裝操作與通過DefaultTypedTuple還有通過RedisZSetCommands下的內部類Range進行範圍限定
public class test {
@Test
public void xx() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
Set<ZSetOperations.TypedTuple> set1 = new HashSet<ZSetOperations.TypedTuple>();
Set<ZSetOperations.TypedTuple> set2 = new HashSet<ZSetOperations.TypedTuple>();
int j = 9;
for (int i = 0; i < 9; i++) {
j--;
Double score1 = Double.valueOf(i);
String value1 = "x" + i;
Double score2 = Double.valueOf(j);
String value2 = j % 2 == 1 ? "y" + j : "x" + j;
ZSetOperations.TypedTuple typedTuple1 = new DefaultTypedTuple(value1, score1);
set1.add(typedTuple1);
ZSetOperations.TypedTuple typedTuple2 = new DefaultTypedTuple(value2, score2);
set2.add(typedTuple2);
}
redisTemplate.opsForZSet().add("zset1", set1);//插入集合
redisTemplate.opsForZSet().add("zset2", set2);
Long size = null;
//size = redisTemplate.opsForZSet().zCard("zset1");//統計總數
//size =redisTemplate.opsForZSet().count("zset1",3,6);//求3<=score<=6
Set set = null;
//set = redisTemplate.opsForZSet().range("zset1",1,5);//從下標1開始擷取5個元素,但是不返回分數,每一個元素都是string
//set = redisTemplate.opsForZSet().rangeWithScores("zset1",0,-1);
// 擷取集合所有元素,並且對集合按分數排序,並返回分數,每一個元素是TypeTuple,-1代表全部元素
//printTypedTule(set);
size = redisTemplate.opsForZSet().intersectAndStore("zset1","zset2","inter_zset");//將zset1和zset2兩個集合的交集放入集合inter_zset
//區間:
/*RedisZSetCommands.Range range = RedisZSetCommands.Range.range();
range.lt("x8");//小於
range.gt("x1");//大於
range.lte("x8");//小於等於
range.gte("x1");//大於等於
set = redisTemplate.opsForZSet().rangeByLex("zset1",range);
printSet(set);*/
//限制:
RedisZSetCommands.Limit limit = RedisZSetCommands.Limit.limit();
limit.count(4);//限制返回個數為4
limit.offset(5);//限制從第五個開始擷取
//set = redisTemplate.opsForZSet().rangeByLex("zset1",range,limit);//求區間內的元素,並限制返回四條。
//Long rank = redisTemplate.opsForZSet().rank("zset1","x0");//求排名 第一個返回0 第二個返回1
//size = redisTemplate.opsForZSet().remove("zset1","x5","x11");//刪除元素,返回刪除個數
//size =redisTemplate.opsForZSet().removeRange("zset1",1,2);//按照排名刪除從0開始,這裡刪除第二個和第三個
Double dbl = redisTemplate.opsForZSet().incrementScore("zset1","x1",11);//給集合的一個元素的分數加上11 並且返回這個新的分數
System.out.print(dbl);
}
//列印TypedTuple集合
public void printTypedTule(Set<ZSetOperations.TypedTuple> set){
if(set!=null&&set.isEmpty()){
return;
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
ZSetOperations.TypedTuple val = (ZSetOperations.TypedTuple)iterator.next();
System.out.print("{value="+val.getValue()+",score="+val.getScore()+"}\n");
}
}
public void printSet(Set set){
if(set!=null&&set.isEmpty()){
return;
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Object val = iterator.next();
System.out.print(val+"\t");
}
System.out.println();
}
}
還有基數 :
並不是儲存元素,而是給某一個有重複元素的資料集合 評價需要的空間單元數。