Redis是一個基於內存的Key-Value非關係型數據庫,由C語言進行編寫。redis
Redis通常做爲分佈式緩存框架、分佈式下的SESSION分離、分佈式鎖的實現等等。數據庫
Redis速度快的緣由:基於內存、單線程、多路複用。 緩存
Redis中提供了五種數據結構,分別是String、Hash、List、Set、ZSet,每種數據結構底層都是經過字符串來進行實現。安全
Key對應的Value是一個字符串類型。服務器
#設置字符串類型的Key
set key value
#僅當Key不存在時設置字符串類型的Key
setnx key value
#設置字符串類型的Key並添加過時時間
setex key second value
#獲取Key對應的Value
get key
#讓Key對應的Value值遞增1
incr key
#讓Key對應的Value值遞減1
decr key
#讓Key對應的Value遞增指定的數值
incrby key num
#讓Key對應的Value遞減指定的數值
decrby key num
#往Key對應的Value中追加字符串
append key str
*在使用set、append命令時,若是Key不存在則建立,不然替換Key對應的Value值。數據結構
*在使用incr、incrby、decr、decrby命令時,若是Key不存在則初始化爲0後再進行操做,若是Key對應的Value不是數值類型字符串,那麼將會報錯。併發
Key對應的Value是一個哈希類型,每一個哈希類型中都包含若干個鍵值對(屬性名與屬性值)app
#往Hash中添加一個屬性
hset key field value
#僅當Key不存在時往Hash中添加一個屬性
hsetnx key field value
#往Hash中添加多個屬性
hmset key field1 value1 field2 value2
#獲取Hash中指定的一個屬性
hget key field
#獲取Hash中指定的多個屬性
hmget key field1 field2
#獲取Hash中全部的屬性
hgetall key
#讓Hash中指定的屬性遞增指定的數值(屬性值必須是數值類型)
hincrby key field num
#判斷Hash中指定的屬性是否存在
hexists key field
#獲取Hash中屬性的個數
hlen key
#獲取Hash中全部的屬性名
hkeys key
#獲取Hash中全部的屬性值
hvals key
#刪除Hash中指定的多個屬性
hdel key field1 field2
*在使用hset、hmset命令時,若是Key不存在則建立,不然往Key對應的Hash中添加屬性,若是屬性名相同則替換屬性值。框架
*Hash中只有hincryby命令,沒有相似String的incr、decr、decrby命令。dom
*當使用hdel命令刪除了Hash中的全部屬性,那麼此時Key也會被刪除。
Key對應的Value是一個List類型(有序可重複),Redis中的List使用鏈表的方式進行實現,所以其在插入和刪除操做時性能要比順序表的高(頭指針指向鏈表的第一個結點)
#從鏈表的左側添加元素 lpush key value #從鏈表的右側添加元素 rpush key value #僅當Key存在時從鏈表的左側添加元素 lpushx key value #僅當Key存在時從鏈表的右側添加元素 rpushx key value #獲取鏈表中指定索引範圍的元素(從鏈表的左側開始遍歷,包括begin和end的位置,若是end爲-1表示倒數第一個元素) lrange key begin end #從鏈表的左側彈出一個元素 lpop key #從鏈表的右側彈出一個元素 rpop key #獲取鏈表中元素的個數 llen key #刪除鏈表中指定個數個Value(若count爲正數,則從鏈表的左側開始刪除指定個數個Value,若count爲負數,則從鏈表的右側開始刪除指定個數個Value,若count爲0,則刪除鏈表中全部指定的Value) lrem key count value #設置鏈表中指定索引的值 lset key index value #從鏈表的右側彈出元素並將其放入到其餘鏈表的左側(通常用在消息隊列的備份) rpoplpush key otherKey
*當使用lpush、rpush命令時,若是Key不存在則建立,不然往Key對應的鏈表中追加元素。
*經過Redis的List能夠實現隊列和棧結構,當遵循lpush、rpop時,此時爲隊列結構,當遵循lpush、lpop時,此時爲棧結構。
Key對應的Value是一個Set(無序不可重複)
#往Set中添加元素
sadd key value
#刪除Set中指定的元素
srem key value
#查看Set中的元素
smembers key
#判斷Set中是否包含某個元素
sismemeber key value
#返回Set中元素的個數
scard set
#返回兩個Set的交集
sinter set1 set2
#返回兩個Set的並集
sunion set1 set2
#返回Set1中Set2沒有的元素(補集)
sdiff set1 set2
#將Set1和Set2的交集放入到新的Set中
sinterstore destSet set1 set2
#將Set1和Set2的並集放入到新的Set中
sunionstore destSet set1 set2
#將Set1中Set2沒有的元素放入到新的Set中
sdiffstore destSet set1 set2
*在使用sadd命令時,若是Key不存在則建立,不然往Key對應的Set中追加元素。
Key對應的Value是一個帶分數的Set(有序不可重複),其中分數能夠相同但Value不能相同。
#往ZSet中添加元素 zadd key score value #獲取ZSet中指定Value的分數 zscore key value #返回ZSet中元素的個數 zcard key #獲取ZSet中指定索引範圍的元素(包括begin和end的位置,end爲-1時表示倒數第一個元素) zrange key begin end #獲取ZSet中指定索引範圍的元素以及分數,返回的元素按照分數從小到大排序 zrange key begin end withscores #獲取ZSet中指定索引範圍的元素以及分數,返回的元素按照分數從大到小進行排序 zrevrange key begin end withscores #獲取ZSet中指定分數範圍的元素(包括begin和end的位置) zrangebyscore key begin end #獲取ZSet中指定分數範圍的元素並限制返回的個數 zrangebyscore key begin end limit num #返回ZSet中指定分數範圍元素的個數 zcount key begin end #刪除ZSet中指定的元素 zrem key value #刪除ZSet中指定分數範圍的元素(包括begin和end的位置) zremrangebyscore key begin end #讓ZSet中指定元素的分數遞增指定的值 zincrby key score value
*當使用zadd命令時,若是Key不存在則建立,不然往Key對應的ZSet中添加元素。
*Set、ZSet中沒有相似String、Hash、List的,當Key不存在或存在時才進行操做的命令。
#查看Redis中的Key(支持通配符,*表明任意個字符,?表明任意一個字符) keys pattern #刪除Key del key #判斷Key是否存在 exists key #對Key進行重命名 rename oldKey newKey #對Key設置過時時間 expire key seconds #查看Key的有效時間(若Key沒有設置過時時間則返回-1) ttl key #查看Key的類型 type key
Redis中提供了事務的功能,Redis會將事務中的全部命令看成一個原子執行,即一個事務在執行時不會被其餘命令所幹擾。
#開啓事務
multi
#執行事務
exec
#放棄事務
discard
*在Shell中事務與客戶端進行綁定,當一個客戶端開啓了事務後,該客戶端事務以後的命令都將放入一個新的隊列中,使用exec命令執行事務,其餘客戶端的命令並不會進入此事務,在Jedis中經過Transaction實例封裝一個事務,不受影響。
因爲Redis是基於內存的Key-Value非關係型數據庫,所以當Redis服務掛掉後因爲內存被釋放會致使數據丟失,此時可使用Redis的持久化功能。
RDB持久化方式即Redis每隔必定時間,就會將當前內存中的全部Key-Value寫入到磁盤文件中(全量寫入),當Redis服務重啓時,讀取RDB文件自動進行數據的恢復。
RDB持久化方式是默認開啓的,能夠經過redis.conf配置文件中修改相關配置。
#在900秒內若是至少有1個Key發生變化,那麼執行一次寫入操做 save 900 1 #在300秒內若是至少有10個Key發生改變,那麼執行一次寫入操做 save 300 10 #在60秒內若是至少有10000個Key發生改變,那麼執行一次寫入操做 save 60 10000 #rdb文件的保存路徑(相對於redis.conf文件) dir ./ #rdb文件的名稱 dbfilename dump.rdb
*使用RDB持久化方式有很大可能會發生Key的修改將來得及寫入到磁盤中服務器就宕機了(能夠調整默認的同步策略)
*RDB持久化方式由Redis進程執行fork操做建立子進程來完成,所以阻塞只會發生在fork階段,客戶端能夠經過save、bgsave命令手動觸發RDB操做,其中save命令會阻塞redis進程直到RDB持久化完成,而bgsave命令由redis進程fork子進程進行完成。
AOF持久化方式即Redis將全部的Key-Value操做都寫入到日誌文件中(追加,增量寫入),當Redis服務重啓時,讀取AOF文件自動進行數據的恢復。
AOF持久化方式提供了三種同步策略:每修改同步、每秒同步、不一樣步。
#開啓AOF方式 appendonly yes #AOF文件名 appendfilename "appendonly.aof" #AOF同步策略 #每修改同步 appendsync always #每秒同步 appendsync everysec #不一樣步 appendsync no
*AOF持久化方式也是由Redis進程執行fork操做建立子進程來完成,而且當日志文件達到必定大小時Redis會對其壓縮(重寫)
*當同時使用RDB和AOF持久化方式時,數據的恢復將固定使用AOF的。
RDB持久化方式與AOF持久化方式的對比
1.文件大小:AOF持久化方式所產生的日誌文件要比RDB持久化方式所產生的rdb文件要大。
2.安全性:AOF持久化方式數據丟失的可能性要比RDB持久化方式低。
3.效率:AOF持久化方式在進行數據恢復時的效率要比RDB持久化方式的低。
Redis中能夠對Key設置過時時間,當Key已經到達過時時間時,並不會當即被刪除,而是經過Redis的過時策略進行處理。
Redis中使用惰性刪除和主動刪除兩種過時策略:
惰性刪除:當訪問一個已通過期的Key時,將該Key刪除。
主動刪除:Redis定時刪除緩存中部分過時的Key,經過抽樣的方式保證Redis中過時的Key在低於25%如下。
*Redis定時刪除緩存中部分過時的Key是經過Redis常規任務處理的,常規任務還包含其餘一些任務處理,能夠經過修改redis.conf配置文件中的hz參數來調整Redis常規任務的執行頻率,默認值是10,表示每秒執行10次,其取值爲1~500,但Redis不建議該值超過100,不然會影響其餘的業務請求,形成延時。
當Redis中使用的內存已經超過所容許的最大值時(maxmemory),會根據預先設置的淘汰策略主動刪除緩存中部分Key。
Redis中提供了六種淘汰策略:
1.allkeys-lru(推薦):刪除部分最近最少使用的Key。
2.volatile-lru:在設置了過時時間的Key中,刪除部分最近最少使用的Key。
3.allkeys-random:隨機刪除部分Key。
4.volatile-random:隨機刪除部分設置了過時時間的Key。
5.volatile-ttl:刪除部分即將過時的Key。
6.noeviction:不清除,對於寫請求直接返回錯誤。
*能夠經過redis.conf配置文件中的maxmemory-policy參數設置Redis的淘汰策略。
通常經過Redis中的一個Key來實現分佈式鎖,當Key已經存在時,表示當前鎖已經被其它線程鎖持有,當Key不存在時,設置Key,表示獲取鎖資源。
分佈式鎖可能面臨的問題:
1.當系統宕機時如何保證鎖可以被釋放?
*經過對Key設置過時時間,當Key已通過期並再次訪問時,經過惰性刪除過時策略刪除該Key。
2.怎樣保證獲取鎖時的同步問題?
*經過使用Redis提供的set(String key, String value, String nxxx, String expx, long time)命令,將判斷Key是否存在以及設置Key做爲一個原子操做。
3.怎樣保證釋放的鎖是當前線程的?
*給Key設置一個表明當前線程的Value,當刪除該Key時判斷Value是否屬於當前線程的。
/** * 獲取鎖 */
public boolean getLock(String key, String value, int second) { return "OK".equalsIgnoreCase(jedis.set(key, value, "nx", "ex", second)); } /** * 釋放鎖 */
public boolean unLock(String key, String value) { if (value.equals(jedis.get(key))) { jedis.del(key); return true; } return false; }
Redis計數器即便用incr、incrby、decr、decrby命令,將Key對應的Value遞增或遞減指定的數值。
頻次控制(登陸錯誤次數)
1.用戶輸入用戶名以及密碼,首先判斷表明用戶的Key是否存在,僅當Key不存在或Key的值小於所容許的最大登陸錯誤次數時,執行登陸操做,不然不容許登陸。
2.執行登陸操做,若是登陸成功則將用戶實體放入HttpSession,不然當密碼錯誤時,使用incr命令自增1(Key不存在時初始化爲0再操做),並根據命令返回值判斷當前的錯誤次數是否已經達到了所容許的最大值,若達到了則設置Key的過時時間。
數量控制(秒殺、搶購)
1.系統在搶購前初始化該Key對應的Value。
2.在進行搶購時直接使用decr命令,並根據返回值判斷是否搶購成功,當返回值大於等於0,表示搶購成功,不然搶購失敗。
*依賴incr、incrby、decr、decrby命令的返回值,能夠避免併發致使的同步問題(不須要獲取值再判斷是否大於0,由於不是原子操做,會有同步問題)
*通常對於頻次控制、數量統計,使用incr、incrby命令,對於數量控制使用decr、decrby命令。
緩存雪崩即Redis中有大部分的Key集中過時,那麼此時全部的客戶端請求都會去到數據庫中,從而增長了數據庫的壓力。
*解決緩存雪崩的方法是對於被頻繁訪問的Key不設置過時時間,對於不頻繁訪問的Key分散設置過時時間。
緩存穿透即不斷查詢Redis中不存在Key,從而佔用Redis以及數據庫的鏈接,容易被惡意佔用資源。
*解決緩存穿透的方法是當數據庫查詢不到記錄時,也將該Key放入到緩存中,其Value是一個空字符串。
緩存擊穿即Redis中一個被高併發訪問的Key忽然過時,那麼此時將會有大量的請求直接去到數據庫,從而增長了數據庫的壓力。
*解決緩存擊穿的方法是對於被頻繁訪問的Key不設置過時時間。
1.下載Redis源碼並進行解壓
2.因爲Redis是使用C語言編寫的,所以須要安裝gcc編譯器,並使用make命令進行編譯
make
3.進行redis的安裝
make PREFIX=/usr/redis install
*安裝完後生成bin目錄,裏面包含一些Redis的可執行命令。
4.將redis源碼目錄下的redis.conf配置文件複製到安裝目錄中,並將配置文件中的daemonize改成yes,經過後臺的方式啓動Redis
5.啓動Redis Server
*Redis Server是一個進程,包含了若干個線程,但只有一個線程用於處理客戶端的請求(Redis單線程處理任務,不會出現共享變量不一致的問題)
6.啓動Redis Client
*Redis Server中包含16個數據庫,編號從0~15,每一個數據庫之間的數據相互獨立,客戶端默認鏈接的是第一個數據庫,能夠經過select num命令修改鏈接的數據庫。
Jedis是Redis官方推薦的Redis Java客戶端類庫。
1.導入依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.1</version>
</dependency>
2.獲取Redis鏈接
//1.直接建立Jedis實例
Jedis jedis = new Jedis(String host , int port); //2.經過Jedis鏈接池來管理鏈接
JedisPoolConfig poolConfig = new JedisPoolConfig(); //最大鏈接數
poolConfig.setMaxTotal(int num); //最大空閒鏈接數
poolConfig.setMaxIdle(int num); //建立鏈接池對象
JedisPool jedisPool = new JedisPool(poolConfig , String host , int port); //獲取Redis鏈接
Jedis jedis = jedisPool.getResource();
*當Redis鏈接使用完畢後須要手動關閉。
/** * @Auther: ZHUANGHAOTANG * @Date: 2019/4/2 17:11 * @Description: */
public class RedisUtils { private static final String host = "192.168.1.80"; private static final int port = 6379; private static JedisPool jedisPool = null; static { JedisPoolConfig poolConfig = new JedisPoolConfig(); //最大鏈接數
poolConfig.setMaxTotal(10); //最大空閒鏈接數
poolConfig.setMaxIdle(5); //建立鏈接池對象
jedisPool = new JedisPool(poolConfig, host, port); } public static Jedis getConnection() { return jedisPool.getResource(); } }