(1)Redis概述html
(2)Redis安裝和部署(難點)java
(3)Redis數據結構類型(重點)node
(4)Jedis操做Redis(掌握)mysql
(5)Redis持久化機制(瞭解)c++
Redis是一個開源的,使用ANSI C 編寫,高性能的Key-Value的NoSQL數據庫。redis
(1)基於內存sql
(2)可持久化數據mongodb
(3)具備豐富的數據結構類型,適應非關係型數據的存儲需求shell
(4)支持絕大多數主流開發語言,如C、C++、Java、Python、R、JavaScript等。數據庫
(5)支持集羣模式,高效、穩定。
(1)鍵值對形式。
(2)Redis的數據結構類型,指的就是Redis值的結構類型。
(1)本質是數據庫,能存儲數據。
Redis能靈活處理非關係型數據的讀、寫問題,是對MySQL等關係型數據庫的補充。
新浪微博就是使用Redis集羣作數據庫。
(2)緩存數據。
所謂緩存,就是將數據加載到內存中後直接使用,而不是每次都經過IO流從磁盤上讀取。好處:讀寫效率高。
而Redis則是將數據直接存儲在內存中,只有當內存空間不足時,將部分數據持久化到磁盤上。
本課程是在Linux上安裝Redis。
而Redis官方只提供了源碼,並無提供通過編譯以後的安裝包。
所以,安裝Redis,要先編譯、後安裝。(即源碼安裝方式)
1)、下載,上傳到Linux服務器,並解壓
2)、預編譯(其實是檢查編譯環境的過程)
進入目錄: cd /opt/soft/redis-3.2.9/deps/jemalloc
執行預編譯 ./configure
在預編譯的過程當中,會檢測安裝redis所需的相關依賴,依次安裝便可。
(a)缺乏c編譯環境 yum -y install gcc-c++
預編譯不是必須的步驟,它只是在檢查編譯過程當中須要的環境是否知足。
一般源碼包中,都有一個可執行的configure腳本,這個腳本執行預編譯的腳本。可是有一些源碼包中,沒有該文件,能夠省略預編譯步驟。
3)、編譯 進入/opt/soft/redis-3.2.9/src
make
4)、安裝 進入/opt/soft/redis-3.2.9/src
make install
5)、啓動redis服務端(指定配置文件)
拷貝redis.conf文件到/etc 目錄下,方便管理。
cp /opt/soft/redis-3.2.9/redis.conf /etc/
/usr/local/bin/redis-server /etc/redis.conf
6)、啓動redis客戶端,登錄 /usr/local/bin/redis-cli
7)、安裝成功
給redis設置密碼
將#號放開 將foobar修改成本身的密碼
在客戶端使用auth命令,驗證密碼。
Redis默認有16個數據庫,尋址角標從0開始。
默認鏈接db0
客戶端使用select命令,切換數據庫
指定redis的服務端口,默認6379.
Redis默認關閉後臺進程模式,該成yes,redis服務在後臺啓動。
Redis日誌輸出目錄,默認不輸出日誌到文件。
指定數據持久化的文件名及目錄。
修改配置文件,將daemonize改成yes
說明:Linux系統服務,在/etc/init.d目錄下建立redis腳本
########################### # chkconfig: 2345 10 90 # description: Start and Stop redis PATH=/usr/local/bin:/sbin:/usr/bin:/bin
REDISPORT=6379 EXEC=/usr/local/bin/redis-server REDIS_CLI=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_6379.pid CONF="/etc/redis.conf" PASSWORD=$(cat $CONF|grep '^\s*requirepass'|awk '{print $2}'|sed 's/"//g')
case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis server..." $EXEC $CONF fi if [ "$?"="0" ] then echo "Redis is running..." fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else PID=$(cat $PIDFILE) echo "Stopping ..." if [ -z $PASSWORD ] then $REDIS_CLI -p $REDISPORT shutdown else $REDIS_CLI -a $PASSWORD -p $REDISPORT shutdown fi #$REDIS_CLI -p $REDISPORT SHUTDOWN while [ -x ${PIDFILE} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" fi ;; restart|force-reload) ${0} stop ${0} start ;; *) echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2 exit 1 esac ############################## |
[root@node0927 ~]chmod +x /etc/init.d/redis |
[root@node0927 ~]chkconfig redis on |
redis的key 值是二進制安全的,這意味着能夠用任何二進制序列做爲key值,從形如」foo」的簡單字符串到一個JPEG文件的內容均可以。
空字符串也是有效key值。
redis建議使用字符串作爲key的類型
(1)鍵值不須要太長,消耗內存,在數據中查找這類鍵值的計算成本較高
(2)鍵值不宜太短,可讀性較差,一般建議見名知意。
將以下數據庫表中的數據,轉換爲redis的key-value存儲
id |
username |
password |
|
1 |
lisi |
111111 |
lisi@163.com |
127.0.0.1:6379> set user:id:1:username lisi OK 127.0.0.1:6379> set user:id:1:password 111111 OK 127.0.0.1:6379> set user:id:1:email lisi@163.com OK
127.0.0.1:6379> keys user:id:1* #查找有幾個屬性 1) "user:id:1:password" 2) "user:id:1:username" 3) "user:id:1:email" 127.0.0.1:6379> |
exists key 檢查給定key是否存在。 del key 刪除一個key
del key1 key2 key3 刪除多個key
keys pattern (模糊查找) 查找全部符合給定模式 pattern 的 key 。 keys * 匹配數據庫中全部 key 。 keys n?me 匹配 name、neme、nfme 等。 keys n* 匹配 name、neme、naaaaame等。 keys n[ae]me 只能匹配 name、neme。
expire key seconds 指定key的過時時間。 新添加的key,若是沒有指定過時時間,則會一直保存。 能夠對一個已經帶有生存時間的key執行EXPIRE命令,新指定的生存時間會取代舊的生存時間。 ttl key (time to live) 查看某個key的剩餘過時時間,返回值: -2 表示這個key已通過期,刪除掉 -1 表示沒有設置過時時間 其它 表示剩餘的生存時間,單位爲秒。 rename 語法格式:rename key newkey 將 key 更名爲 newkey 。 當 key 和 newkey 相同,或者 key 不存在時,返回一個錯誤。 當 newkey 已經存在時, RENAME 命令將覆蓋舊值。
type key 查看key對應的value的數據結構類型。 其它key命令見redis幫助文檔 http://doc.redisfans.com/ |
Redis的數據結構類型,指的就是redis的值value的類型;
Redis經常使用的數據結構類型:string、list、set、sortedSet、hash
string類型是redis最經常使用的數據結構類型,存儲的值爲字符串。
set key value 設置一個key,值爲value,類型爲String類型;若是這個key已經存在,則更新這個key的值。 返回值 1 表示成功 0 表示失敗
setnx key value 若是這個key不存在,則設置一個key,值爲value;若是key存在,則不作更新。 返回值 1 表示成功 0 表示失敗
get key 獲取key對應的value值;若是key不存在,則返回nil
mget key1 key2 key3 一次獲取多個key的值,若是對應key不存在,則對應返回nil。
incr key 將 key 中儲存的數字值增一,而後返回。 若是這個key不存在,那麼key的值會先被初始化爲0,而後再執行INCR 操做。 若是這個key對應的value值,不能表示數字,則會返回一個錯誤。 incrby key increment 將key增長指定的步長值。 decr key 將 key 中儲存的數字值減一,而後返回。 若是這個key不存在,那麼key的值會先被初始化爲0,而後再執行INCR操做。 若是這個key對應的value值,不能表示數字,則會返回一個錯誤。
Redis的key是單線程模式,這就意味一瞬間只有一個線程可以持有這個key,因此可使用redis解決部分涉及線程安全的業務。好比,在初級時候經過多線程模擬賣票,使用加鎖的方式,保證只有一個線程可以持有鎖,進行買票業務。
decrby key decrement 將key減小對應的步長值。
append key value 若是key已經存在,則將value追加到這個key原先的value值的末尾。 若是這個key不存在,則執行set操做。
127.0.0.1:6379> help @string 查看string類型的幫助文檔 127.0.0.1:6379> help set 查看set命令的幫助文檔 |
(1)、作與統計有關的業務,如新浪微博(微信朋友圈)中的點贊功能
(2)解決多線程的線程安全問題。
Redis的key是單線程模式,這就意味一瞬間只有一個線程可以持有這個key,因此可使用redis解決部分涉及線程安全的業務,好比說搶購、秒殺。
再好比初級的時候學習多線程模擬買票窗口的賣票業務。
(1)、基於Linked List實現
(2)、元素是字符串類型
(3)、列表頭尾增刪快,中間增刪慢,增刪元素是常態
(4)、元素能夠重複出現
(5)、最多包含2^32-1元素
列表的索引
從左至右,從0開始
從右至左,從-1開始
lpush key value [value ...] 將一個或多個值value插入到列表key的表頭(即從左邊插入); 若是有多個value值,那麼各個value值按從左到右的順序依次插入到表頭:好比說,對空列表mylist執行命令LPUSH mylist a b c,列表的值將是 c b a 這等同於原子性地執行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三個命令; 若是 key 不存在,一個空列表會被建立並執行 LPUSH 操做。 當 key 存在但不是列表類型時,返回一個錯誤。
rpush key value [value ...] 尾部添加(從右向左),操做同上。
llen key 返回key對應list的長度,key不存在返回0,若是key對應類型不是list返回錯誤
lindex key index index元素在list列表中的下角標,從0開始; lindex 是從左到右取元素 lrange key start stop 獲取指定區間的全部元素; 下角標從0開始,0表示第一個元素,1表示第二個,依次類推; -1表示最後一個元素,-2表示倒數第二個元素,依次類推; lpop key 移除並返回列表中的第一個元素 rpop key 移除並返回列表中的最後一個元素。 |
(1)處理排名類業務。如新浪微博評論、論壇回帖樓層等。
(2)聊天室
(1)由Field和與之關聯的value組成map鍵值對
(2)field和value是字符串類型;
(3)一個hash中最多包含2^32-1鍵值對。
hset key field value 設置hash field爲指定值,若是key不存在,則先建立 若是field已經存在,那麼將更新這個field的值。
hget key field 獲取指定的hash field
hmget key filed1....fieldN 獲取所有指定的hash filed
hmset key filed1 value1 ... filedN valueN 同時設置hash的多個field
hexists key field 測試指定field是否存在
hdel key field 刪除指定的hash field
hlen key 返回指定hash的field數量
hkeys key 返回hash的全部field
hvals key 返回hash的全部value
hgetall 返回hash的全部filed和value |
節約內存空間:
redis每建立一個鍵,都會爲這個鍵儲存一些附加的管理信息(好比這個鍵的類型,這個鍵最後一次被訪問的時間等等)
redis的key相對於值來講,更珍貴!!!
因此數據庫裏面的鍵越多,redis數據庫服務器在儲存附加管理信息方面耗費的內存就越多,在獲取key對應的value值時cpu的開銷也會更多
Hash結構能夠將具備關聯關係的一組key-value,存儲到同一個hash結構中,從而減小key的數量。
須要設置鍵過時功能的key:
Redis的key的過時功能只能對鍵操做,而Hash結構不能單獨對某一個filed設置過時功能。
說明:在實際開發中,能使用hash的時候,儘可能使用hash!!
(1)無序的、去重的;
(2)元素是字符串類型;
(3)最多包含2^32-1元素。
sadd key member [member ...](無序不重複) 將一個或多個 member 元素加入到集合 key 當中,已經存在於集合的 member 元素將被忽略。 假如 key 不存在,則建立一個只包含 member 元素做成員的集合。 當 key 不是集合類型時,返回一個錯誤。
smembers key 返回集合 key 中的全部成員。 不存在的 key 被視爲空集合。
spop key 移除並返回集合中的一個隨機元素。 被移除的隨機元素。 當key不存在或key是空集時,返回nil。
scard key 返回集合key的基數(集合中元素的數量)。 集合的基數。 當key不存在時,返回0。
交集、並集、差集sinter sunion sdiff sinter key [key ...] 返回一個集合的所有成員,該集合是全部給定集合的交集。 不存在的 key 被視爲空集。
sunion key [key ...] 返回一個集合的所有成員,該集合是全部給定集合的並集。 不存在的 key 被視爲空集。
sdiff key [key ...] 返回一個集合的所有成員,該集合是全部給定集合之間的差集。 不存在的 key 被視爲空集。 |
(1)新浪微博的共同關注
需求:當用戶訪問另外一個用戶的時候,會顯示出兩個用戶共同關注哪些相同的用戶
設計:將每一個用戶關注的用戶放在集合中,求交集便可
實現以下:
peter={'john','jack','may'}
ben={'john','jack','tom'}
那麼peter和ben的共同關注爲:
SINTER peter ben 結果爲{'john','jack'}
(1)相似Set集合;
(2)有序的、去重的;
(3)元素是字符串類型;
(4)每個元素都關聯着一個浮點數分值(Score),並按照分值從小到大的順序排列集合中的元素。分值能夠相同
(5)最多包含2^32-1元素
適用於須要有序且惟一的業務或操做:
(1)網易音樂排行榜
分析:
每首歌的歌名做爲元素(惟1、不重複)
每首歌的播放次數做爲分值
ZREVRANGE來獲取播放次數最多的歌曲(就是最多播放榜了,雲音樂熱歌榜,沒有競價,沒有權重)
import java.util.Set;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class TestJedis {
@Test public void testJedisConnect(){ // 鏈接redis服務器端(使用host) @SuppressWarnings("resource") Jedis jedis = new Jedis("192.168.56.128"); System.out.println("鏈接redis服務端成功!"); //測試redis服務器是否正在運行 System.out.println("redis服務器正在運行嗎?"+ jedis.ping()); System.out.println("redis服務器信息?\n"+ jedis.info()); } }
|
Tips:使用jedis鏈接redis可能會出現的問題及解決方案:
1)ip綁定問題 Connection refused: connect
把Redis的配置文件redis.conf裏的 bind localhost(或者bind 127.0.0.1,代表只有該主機才能訪問)註釋掉。
或者修改成: bind ip 代表,只能經過ip訪問。
2)、保護模式 DENIED Redis is running in protected mode because protected mode is enabled… redis處於保護模式,只能本地連接,咱們須要修改配置文件redis.conf,將protected-mode yes改爲no
|
@Test public void testKey(){ @SuppressWarnings("resource") Jedis jedis = new Jedis("192.168.56.128"); System.out.println("鏈接redis服務端成功!"); //測試redis服務器是否正在運行 System.out.println("redis服務器正在運行嗎?"+ jedis.ping());
jedis.set("jediskey", "apple");
String value = jedis.get("jediskey");
System.out.println(value);
Set<String> keys = jedis.keys("*");
for (String string : keys) { System.out.println(string); }
} |
@Test public void testList(){ @SuppressWarnings("resource") Jedis jedis = new Jedis("192.168.56.128"); System.out.println("鏈接redis服務端成功!"); //測試redis服務器是否正在運行 System.out.println("redis服務器正在運行嗎?"+ jedis.ping());
jedis.lpush("list01", "string1"); jedis.lpush("list01", "string2"); jedis.lpush("list01", "string3"); List<String> listString = jedis.lrange("list01", 0, 2); for (String string : listString) { System.out.println(string); } } |
@Test public void testSet(){ @SuppressWarnings("resource") Jedis jedis = new Jedis("192.168.56.128"); System.out.println("鏈接redis服務端成功!"); //測試redis服務器是否正在運行 System.out.println("redis服務器正在運行嗎?"+ jedis.ping());
jedis.sadd("jedisset", "mysql"); jedis.sadd("jedisset", "oracle","mongodb"); Set<String> set = jedis.sunion("jedisset"); for (String string : set) { System.out.println(string); } } |
@Test public void testHash(){ @SuppressWarnings("resource") Jedis jedis = new Jedis("192.168.56.128"); System.out.println("鏈接redis服務端成功!"); //測試redis服務器是否正在運行 System.out.println("redis服務器正在運行嗎?"+ jedis.ping());
jedis.hset("hash01", "name", "zhangsan"); jedis.hset("hash01", "age", "18"); jedis.hset("hash01", "phone", "18627738874"); List<String> hashString = jedis.hmget("hash01", "name","age","phone"); for (String string : hashString) { System.out.println(string); } } |
Redis持久化,就是將內存中的數據,永久保存到磁盤上。
Redis持久化有兩種方式:RDB(Redis DB)、AOF(AppendOnlyFile)
在默認狀況下,Redis 將數據庫快照保存在名字爲dump.rdb的二進制文件中,能夠在redis.conf配置文件中修改持久化信息。
(1)自動:BGSAVE
按照配置文件中的條件知足就執行BGSAVE;
非阻塞,Redis服務正常接收處理客戶端請求;
Redis會folk()一個新的子進程來建立RDB文件,子進程處理完後會向父進程發送一個信號,通知它處理完畢;
父進程用新的dump.rdb替代舊文件。
|
(2)手動:SAVE
客戶端(redis-cli)發起SAVE命令;
阻塞Redis服務,沒法響應客戶端請求;
建立新的dump.rdb替代舊文件。
(1)執行效率高;
(2)恢復大數據集速度較AOF快。
(1)會丟失最近寫入、修改的而未能持久化的數據;
(2)folk過程很是耗時,會形成毫秒級不能響應客戶端請求。
Append only file,採用追加的方式保存,默認文件appendonly.aof;
記錄全部的寫操做命令,在服務啓動的時候使用這些命令就能夠還原數據庫;
AOF默認關閉,須要在配置文件中手動開啓。
說明:AOF機制,添加了一個內存緩衝區(buffer)。
(1)將內容寫入緩衝區
(2)當緩衝區被填滿、或者用戶手動執行fsync、或者系統根據指定的寫入磁盤策略自動調用fdatasync命令,纔將緩衝區裏的內容真正寫入磁盤裏。
(3)在緩衝區裏的內容未寫入磁盤以前,可能會丟失。
appendfsync選項,這個選項的值能夠是always、everysec或者no
Always:服務器每寫入一個命令,就調用一次fdatasync,將緩衝區裏面的命令寫入到硬盤。這種模式下,服務器出現故障,也不會丟失任何已經成功執行的命令數據
Everysec(默認):服務器每一秒重調用一次fdatasync,將緩衝區裏面的命令寫入到硬盤。這種模式下,服務器出現故障,最多隻丟失一秒鐘內的執行的命令數據
No:服務器不主動調用fdatasync,由操做系統決定什麼時候將緩衝區裏面的命令寫入到硬盤。這種模式下,服務器遭遇意外停機時,丟失命令的數量是不肯定的
運行速度:always的速度慢,everysec和no都很快
AOF文件過大,合併重複的操做,AOF會使用盡量少的命令來記錄。
(1)folk一個子進程負責重寫AOF文件
(2)子進程會建立一個臨時文件寫入AOF信息
(3)父進程會開闢一個內存緩衝區接收新的寫命令
(4)子進程重寫完成後,父進程會得到一個信號,將父進程接收到的新的寫操做由子進程寫入到臨時文件中
(5)新文件替代舊文件
重寫的本質:就是將操做同一個鍵的命令,合併。從而減少AOF文件的體積
(1)手動:
客戶端向服務器發送BGREWRITEAOF命令
(2)自動:
配置文件中的選項,自動執行BGREWRITEAOF命令
auto-aof-rewrite-min-size <size>,
觸發AOF重寫所需的最小體積:只要在AOF文件的體積大於等於size時,纔會考慮是否須要進行AOF重寫,這個選項用於避免對體積太小的AOF文件進行重寫
auto-aof-rewrite-percentage <percent>
指定觸發重寫所需的AOF文件體積百分比:當AOF文件的體積大於auto-aof-rewrite-min-size指定的體積,而且超過上一次重寫以後的AOF文件體積的percent %時,就會觸發AOF重寫。(若是服務器剛剛啓動不久,尚未進行過AOF重寫,那麼使用服務器啓動時載入的AOF文件的體積來做爲基準值)。將這個值設置爲0表示關閉自動AOF重寫。
寫入機制,默認fysnc每秒執行,性能很好不阻塞服務,最多丟失一秒的數據;
重寫機制,優化AOF文件;
若是誤操做了(FLUSHALL等),只要AOF未被重寫,中止服務移除AOF文件尾部FLUSHALL命令,重啓Redis,能夠將數據集恢復到FLUSHALL 執行以前的狀態。
相同數據集,AOF文件體積較RDB大了不少;
恢復數據庫速度較RDB慢(文本,命令重演)。