1. 程式人生 > 其它 >Redis--狂神說Redis基礎彙總(完結)

Redis--狂神說Redis基礎彙總(完結)

Redis--狂神說Redis基礎彙總(完結)

2021.6.12-2021.6.14:端午學學玩玩弄完了Redis基礎的彙總,越學越覺得自己知識量的匱乏。

參考連結:狂神說Java--Redis彙總:https://www.bilibili.com/video/BV1S54y1R7SB?p=1

目錄



Redis五種基本資料型別:

  • 1.String字串

    OK
    127.0.0.1:6380> set k1 1
    OK
    127.0.0.1:6380> INCR k1
    2
    127.0.0.1:6380> DECR k1
    1
    127.0.0.1:6380> keys *
    k1
    127.0.0.1:6380> set k2 huyuqiao
    OK
    127.0.0.1:6380> GETRANGE K2 0 3
    
    127.0.0.1:6380> GETRANGE k2 0 3
    huyu
    127.0.0.1:6380> GETRANGE k2 0 -1
    huyuqiao
    127.0.0.1:6380> SETRANGE k2 1 XX
    8
    127.0.0.1:6380> get k2
    hXXuqiao
    127.0.0.1:6380> SETEX k3 30 "hello, world"				#set exist:存在或不存在都替換
    OK
    127.0.0.1:6380> ttl k3
    28
    127.0.0.1:6380> ttl k2
    -1
    127.0.0.1:6380> keys *
    k1
    k2
    127.0.0.1:6380> ttl k3
    -2	
    127.0.0.1:6380> setnx mykey "redis"						#set if not exist:不存在就set,存在就set失敗,還是原來值
    1
    127.0.0.1:6380> ttl mykey
    -1
    127.0.0.1:6380> setnx mykey "mongodb"
    0
    127.0.0.1:6380> get mykey
    redis
    127.0.0.1:6380> ttl k2									#永久返回-1
    -1
    127.0.0.1:6380> ttl k3									#過期返回-2
    -2
    127.0.0.1:6380> 
    127.0.0.1:6380> mset user:1:name huyuqiao user:1:age 22
    OK
    127.0.0.1:6380> mget user:1:name user:1:age
    huyuqiao
    22
    127.0.0.1:6380> 
    
    
    
  • List

    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> LPUSH list 1
    1
    127.0.0.1:6380> LPUSH list 2
    2
    127.0.0.1:6380> LPUSH list 3
    3
    127.0.0.1:6380> LRANGE list 0 -1
    3
    2
    1
    127.0.0.1:6380> RPUSH list a
    4
    127.0.0.1:6380> LPOP list
    3
    127.0.0.1:6380> LRANGE 0 -1
    ERR wrong number of arguments for 'lrange' command
    
    127.0.0.1:6380> LRANGE list 0 -1
    2
    1
    a
    127.0.0.1:6380> LINDEX list 0
    2
    127.0.0.1:6380> LLEN list
    3
    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> LPUSH list one 
    1
    127.0.0.1:6380> LPUSH list two
    2
    127.0.0.1:6380> LPUSH list two
    3
    127.0.0.1:6380> LREM list 1 one					#移除list中等於one的一個元素(最後加入的移除掉)
    1
    127.0.0.1:6380> LRANGE list 0 -1
    two
    two
    127.0.0.1:6380> LREM list 2 one
    0
    127.0.0.1:6380> LREM list 2 two
    2
    127.0.0.1:6380> LRANGE list 0 -1
    
    127.0.0.1:6380> FLUSHALL
    
    127.0.0.1:6380> LPUSH list one 
    1
    127.0.0.1:6380> LPUSH list two
    2
    127.0.0.1:6380> LPUSH list three
    3
    127.0.0.1:6380> LPUSH list four
    4
    127.0.0.1:6380> LTRIM list 1 2
    OK
    127.0.0.1:6380> LRANGE list 0 -1
    three
    two
    127.0.0.1:6380> 
    
    
    
  • Set

    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> SADD myset "hello"
    1
    127.0.0.1:6380> sadd myset "world"
    1
    127.0.0.1:6380> sadd myset "huyuqiao"
    1
    127.0.0.1:6380> smembers myset 
    hello
    huyuqiao
    world
    127.0.0.1:6380> SISMEMBER myset huyuqiao
    1
    127.0.0.1:6380> sadd myset "HYQ"
    1
    127.0.0.1:6380> SMEMBERS myset
    hello
    huyuqiao
    HYQ
    world
    127.0.0.1:6380> SREM myset hello
    1
    127.0.0.1:6380> scard myset 
    3
    127.0.0.1:6380> SMEMBERS myset 
    huyuqiao
    HYQ
    world
    127.0.0.1:6380> SRANDMEMBER myset 
    world
    127.0.0.1:6380> SRANDMEMBER myset 
    huyuqiao
    127.0.0.1:6380> 
    
  • Hash

    huyuqiao
    127.0.0.1:6380> FLUSHALL
    OK
    127.0.0.1:6380> clear
    127.0.0.1:6380> hset myhash field1 huyuqiao
    1
    127.0.0.1:6380> hmset myhash field1 hello field2 world
    OK
    127.0.0.1:6380> hmget myhash field1 field2
    hello
    world
    127.0.0.1:6380> hgetall myhash
    field1
    hello
    field2
    world
    127.0.0.1:6380> hlen myhash
    2
    127.0.0.1:6380> HEXISTS myhash field1
    1
    
    127.0.0.1:6380> HKEYS myhash
    field1
    field2
    127.0.0.1:6380> HVALS myhash
    hello
    world
    127.0.0.1:6380> HSETNX myhash field4 hello			#hash設定
    1
    127.0.0.1:6380> HGETALL myhash
    field1
    hello
    field2
    world
    field4
    hello
    127.0.0.1:6380> 
    
  • Zset

    127.0.0.1:6380> zadd salary 100 huyuqiao
    1
    127.0.0.1:6380> zadd salary 200 HUYUQIAO
    1
    127.0.0.1:6380> zadd salary 300 HYQ
    1
    127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
    huyuqiao
    HUYUQIAO
    HYQ
    127.0.0.1:6380> zrange salary 0 -1
    huyuqiao
    HUYUQIAO
    HYQ
    127.0.0.1:6380> zrem salary huyuqiao
    1
    127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
    HUYUQIAO
    HYQ
    
    



Redis三種特殊資料型別:

1.GeoSpatical

適用場景:獲取附近人,好友距離,網約車定位功能。

# 新增位置經緯度
127.0.0.1:6379> GEOADD china:city 116.41667 39.91667 北京
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.43333 31.23000 上海
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.45000 29.56667 重慶
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.06667 22.61667 深圳
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.20000 30.26667 杭州
(integer) 1
127.0.0.1:6379> GEOADD china:city 108.95000 34.26667 西安
(integer) 1

# 檢視不同位置經緯度
127.0.0.1:6379> GEOPOS china:city 北京 西安

#檢視不同位置之間距離(預設是m)
127.0.0.1:6379> GEODIST china:city 北京 上海
"1066981.1340"
127.0.0.1:6379> GEODIST china:city 北京 上海 km
"1066.9811"
127.0.0.1:6379> GEODIST china:city 北京 重慶 km
"1465.8918"
127.0.0.1:6379> 

# 檢視指定經緯度周邊範圍內的地方(可以以此擴散到周圍指定範圍內的指定人數的好友)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
重慶
西安
深圳
杭州
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
重慶
西安
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist	#檢視某經緯度周圍500km的所有城市和距離/km
重慶
346.0548
西安
484.7511
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #檢視某經緯度周圍500km的所有城市和經緯度
重慶
106.4500012993812561
29.56666939001875249
西安
108.95000249147415161
34.2666710302806834
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1 #檢視某經緯度周圍500km的三個城市
重慶
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 3 #檢視某經緯度周圍500km的三個城市
重慶
西安
127.0.0.1:6379> 


#檢視地點周邊的所有城市(類似於定位)
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km
北京
西安
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海  1000 km
杭州
上海
127.0.0.1:6379> 


#將城市的二維經緯度轉成一位hash字串
127.0.0.1:6379> GEOHASH china:city 北京 重慶
wx4g14s53n0
wm78nq6w2f0
127.0.0.1:6379> 

#GEO底層是zset,所有可以用zset命令來操作
127.0.0.1:6379> zrange china:city 0 -1		#檢視所有城市
重慶
西安
深圳
杭州
上海
北京
127.0.0.1:6379> zrem china:city 北京			#刪除北京這個城市

2.Hyperloglog

適用場景: 網站UV量。傳統用set統計,但若存在大量使用者id,則太消耗內容且麻煩,若只為計數且允許有錯誤率(0.81%),則可行,否則還是用set統計

基數:集合中不重複元素個數。如{1, 3, 5, 5 ,7}則為{1,3,5,7},基數為4

127.0.0.1:6379> clear
127.0.0.1:6379> PFADD mykey a b c d e f g h i j				#設定mykey 集合
(integer) 1
127.0.0.1:6379> PFCOUNT mykey								#統計mykey 集合基數數量
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2					#2個集合取並集
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379>
3.Bitmaps

適用場景:判斷、統計活躍、不活躍,登入、未登入這些非1即0的場景

[root@VM-8-11-centos ~]# redis-cli -a root --raw
127.0.0.1:6379> setbit sign 0 1			#設定sign使用者某天是否打卡
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 0
(integer) 0
127.0.0.1:6379> SETBIT sign 6 0
(integer) 0
127.0.0.1:6379> GETBIT sign 3			#得到sign使用者某天打卡情況
(integer) 1
127.0.0.1:6379> GETBIT sign 6



Redis事務:

Reidsi事務:沒有隔離級別概念,即事務不保證原子性(事務內部有多條命令,不一定,可以實現部分成功、部分失敗),但是單條命令保證原子性

1.Redis事務流程:
  • 開啟事務(multi)
  • 命令入隊
  • 執行事務(exec)
#開啟-結束事務
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
OK
OK
v2
OK
127.0.0.1:6379>

#放棄事務:之前佇列程式碼全部rollback
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 asdfasdf
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> EXEC
ERR EXEC without MULTI

127.0.0.1:6379> get k2
v2

2.Redis事務非原子性

之所以事務非原子性,是因為有兩種異常:

  • 編譯性異常(檢查性異常):程式碼錯了,根本就沒某個語句、函式、配置檔案等

    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> gasdfa k3				#語句錯誤,相當於沒執行,在編譯時候就出錯了
    ERR unknown command 'gasdfa'
    
    127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> EXEC
    EXECABORT Transaction discarded because of previous errors.
    
    127.0.0.1:6379> get k4
    
    127.0.0.1:6379> get k1
    
    
  • 執行時異常(非檢查性異常):沒有某個變數、某個物件沒有new等(不會全部rollback)

    127.0.0.1:6379> set k1 "v1"
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> incr k1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> get k2
    QUEUED
    127.0.0.1:6379> EXEC
    ERR value is not an integer or out of range
    
    OK
    v2									#上面出錯了,但是依然不影響下面語句執行,所以證明了redis事務是不保證原子性的
    127.0.0.1:6379> 
    



Redis樂觀鎖:

樂觀鎖:不會上鎖,更新資料時才會比較version是否被人修改過(redis事務中如果被修改過,則事務不會執行成功)

悲觀鎖:無論做什麼,都會上鎖,效率低下但是安全

​ Redis事務中用<font color=red size=3>Watch</font>實現樂觀鎖,中途如果被修改,導致version變更,則事務全部都不會成功

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money				#Redis樂觀鎖:watch監視money
OK
127.0.0.1:6379> MULTI 					#在另一個視窗將money改成了101
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> EXEC
127.0.0.1:6379> get money
101
127.0.0.1:6379> 



SpringBooot--配置Redis原始碼

Jedis:直連,多執行緒下不安全,類似於BIO模式(Springboot2.X後被淘汰)

Lettuce:採用Netty,多執行緒下共享,類似於NIO模式

autoconfig->srping.factories中找到redis相關配置

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}



SpringBooot--自定義RedisTemplate、RedisUtil

1.RedisTemplate序列化配置
package com.empirefree.springboot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @program: springboot
 * @description: RedisTemplate配置
 * @author: huyuqiao
 * @create: 2021/06/13 16:05
 */

@Configuration
public class RedisConfig {
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key採用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也採用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式採用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式採用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

2.RedisUtil配置(CRUD操作string,map,list,set)
package com.empirefree.springboot.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @program: springboot
 * @description: Redis工具類
 * @author: huyuqiao
 * @create: 2021/06/13 16:14
 */

@Component
public final class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================
    /**
     * 指定快取失效時間
     * @param key  鍵
     * @param time 時間(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據key 獲取過期時間
     * @param key 鍵 不能為null
     * @return 時間(秒) 返回0代表為永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }


    /**
     * 判斷key是否存在
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 刪除快取
     * @param key 可以傳一個值 或多個
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }


    // ============================String=============================

    /**
     * 普通快取獲取
     * @param key 鍵
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通快取放入
     * @param key   鍵
     * @param value 值
     * @return true成功 false失敗
     */

    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 普通快取放入並設定時間
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒) time要大於0 如果time小於等於0 將設定無限期
     * @return true成功 false 失敗
     */

    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 遞增
     * @param key   鍵
     * @param delta 要增加幾(大於0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }


    /**
     * 遞減
     * @param key   鍵
     * @param delta 要減少幾(小於0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }


    // ================================Map=================================

    /**
     * HashGet
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 獲取hashKey對應的所有鍵值
     * @param key 鍵
     * @return 對應的多個鍵值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 鍵
     * @param map 對應多個鍵值
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * HashSet 並設定時間
     * @param key  鍵
     * @param map  對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 向一張hash表中放入資料,如果不存在將建立
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入資料,如果不存在將建立
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @param time  時間(秒) 注意:如果已存在的hash表有時間,這裡將會替換原有的時間
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 刪除hash表中的值
     *
     * @param key  鍵 不能為null
     * @param item 項 可以使多個 不能為null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }


    /**
     * 判斷hash表中是否有該項的值
     *
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }


    /**
     * hash遞增 如果不存在,就會建立一個 並把新增後的值返回
     *
     * @param key  鍵
     * @param item 項
     * @param by   要增加幾(大於0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }


    /**
     * hash遞減
     *
     * @param key  鍵
     * @param item 項
     * @param by   要減少記(小於0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }


    // ============================set=============================

    /**
     * 根據key獲取Set中的所有值
     * @param key 鍵
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 根據value從一個set中查詢,是否存在
     *
     * @param key   鍵
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 將資料放入set快取
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 將set資料放入快取
     *
     * @param key    鍵
     * @param time   時間(秒)
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 獲取set快取的長度
     *
     * @param key 鍵
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 移除值為value的
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 移除的個數
     */

    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 獲取list快取的內容
     *
     * @param key   鍵
     * @param start 開始
     * @param end   結束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 獲取list快取的長度
     *
     * @param key 鍵
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 通過索引 獲取list中的值
     *
     * @param key   鍵
     * @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 將list放入快取
     *
     * @param key   鍵
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 將list放入快取
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 將list放入快取
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 將list放入快取
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 根據索引修改list中的某條資料
     *
     * @param key   鍵
     * @param index 索引
     * @param value 值
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 移除N個值為value
     *
     * @param key   鍵
     * @param count 移除多少個
     * @param value 值
     * @return 移除的個數
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }

}



Redis--持久化

持久化:在指定時間間隔內將記憶體資料存入磁碟中,斷電也能恢復資料,使用快照檔案讀到記憶體中。

1.Redis--RDB(預設推薦)
  • 儲存過程:父程序fork一個子程序,將資料持久化到臨時檔案中,持久化結束,再替換上次的RDB正式檔案。

  • 觸發條件:

    • save滿足:命令save 900 1 即是在15分鐘內修改了1次 即會觸發RDB。
    • 執行FlushAll命令
    • 退出Redis,會產生RDB檔案
  • 適用場景:適合大規模資料恢復且資料完整性不敏感的情況。

2.Redis--AOF(重啟後是預設先載入AOF,因為資料更完整)
  • 儲存過程:父程序fork一個子程序,以日誌形式將所有指令記錄下來(讀操作不記錄),然後將資料只追加不改寫到AOF檔案,然後替換上次的AOF檔案

  • 觸發條件:appendfsync always/everysec/no 命令

  • 適用場景:對恢復資料完整性要求嚴格

  • 重寫場景:不斷追加檔案到一個閾值,則會重寫aof檔案



Redis--釋出訂閱

釋出訂閱:可以做訊息推送、聊天室等等

#釋出者:往Redis某渠道中發訊息,所有訂閱者都可以接收到
127.0.0.1:6379> PUBLISH huyuqiao "hello,world"
1


#訂閱者:訂閱Redis中某個渠道channel
127.0.0.1:6379> SUBSCRIBE huyuqiao
subscribe
huyuqiao
1
message
huyuqiao
hello,world



Redis--主從複製(主寫從讀)

1.Linux配置檔案
  • daemonize的no改成yes
  • port、pidfile、logfile、dbfilename改成對於新redis名
  • 如果redis有密碼,需要在slave中配置或者在master中去掉密碼
#master斷了,slaver依然是原來master的slaver。但是slaver斷了,master就沒有slaver了
>>slaveof no one #關閉slave狀態,變成master
>>shutdown       #停止redis,其slave狀態停止,下次啟動變成master,且原來master下的該redis就沒有了


redis-server redis80.conf #啟動80redis視窗。
kill -s 9 pid  #關閉某個程序
2. 複製原理
  • 全量複製:slave啟動後,傳送sync同步命令給master,然後同步master中所有資料
  • 增量複製:master寫資料後,slave實時得到。



Redis--哨兵模式

流程:如下,在啟動哨兵後,關閉79主服務,就從80,81中投票選舉了80為主服務,然後把79,80都當成了slave,所以下次79啟動的時候會預設是slave。另外,由於資料是增量複製,所以資料在80中儲存完好,且79啟動後就全量複製到79中了

#哨兵模式下的日誌

8044:X 14 Jun 16:35:06.885 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8044:X 14 Jun 16:35:06.885 # Sentinel ID is c0ce22fc8365ff48663b7db710ce8c359529c3d9
8044:X 14 Jun 16:35:06.885 # +monitor master mymaster 127.0.0.1 6379 quorum 1
8044:X 14 Jun 16:35:51.556 # +sdown master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.556 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
8044:X 14 Jun 16:35:51.556 # +new-epoch 1
8044:X 14 Jun 16:35:51.556 # +try-failover master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +vote-for-leader c0ce22fc8365ff48663b7db710ce8c359529c3d9 1
8044:X 14 Jun 16:35:51.572 # +elected-leader master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +failover-state-select-slave master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.684 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.914 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +failover-end master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380



Redis--快取穿透、擊穿、雪崩

  • 快取穿透:快取和資料庫中都沒有資料
    • 解決方案:1.布隆過濾器 2.儲存空物件:資料庫沒找到後,redis中臨時存一個空物件
  • 快取擊穿:某個key值過期,來了大量訪問
    • 解決方案:1.永不過期 2.分散式鎖:一個執行緒獲取,其他執行緒等待
  • 快取雪崩:海量key值過期,來了大量訪問
    • 解決方案:1.redis高可用:多設定幾臺redis 2.限流降級:快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的數量 3.資料預熱:大量資料載入到快取中,根據不同訪問量來設定不同過期時間