Redis&Jedis 學習筆記

Redis

Redis是什麼

Redis 是完全開源免費的一個高性能的key-value數據庫
	內存數據庫

Redis 有以下三個特點:
	1)Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啓的時候可以再次加載進行使用。
	2)Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲(這些數據類型主要指的的是value的,key是正常的字符串)
	3)Redis支持數據的備份

Redis優勢

性能極高
	Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
豐富的數據類型
	Redis支持二進制類型存儲。 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
原子操作
	Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並後的原子性執行。
豐富的特性
	Redis還支持 publish/subscribe(發佈/訂閱模式), 通知, key值過期等等特性。

Redis的下載

Redis官網地址:
	http://redis.io/

Redis最新stable版本下載地址(穩定版本)
	http://download.redis.io/

Redis的源碼同時也託管在github上面
	https://github.com/antirez/redis 

windows版本下載地址(官網提供的是Linux版本)
	https://github.com/MSOpenTech/redis/releases
	或者
	https://github.com/dmajkic/redis/downloads

Redis的安裝

根據自己的情況,新建一個目錄,名字爲redis(例如,D:\redis),注意路徑中不要出現中文字符.
把下載好的Redis的壓縮包解壓到新建的指定文件夾中,可見一些可執行文件(版本不同可能此處會有區別)
redis-server.exe		redis 服務器
redis-cli.exe			redis 命令行客戶端
redis-benchmar.exe		redis 性能測試工具
redis-check-aof.exe		AOF   文件修復工具

Redis簡單的測試

啓動Redis服務器
	開啓一個cmd窗口
	使用cd命令切換目錄到 D:\redis 
	運行 redis-server.exe redis.windows.conf
		啓動成功後會看到啓動的圖案和啓動信息(包括Redis版本、監聽端口號等)
	注意:這個服務器的運行窗口不能關閉,如果關閉Redis服務器就停了
	注意:ctrl+c可以也關閉redis服務器

啓動Redis客戶端
	另開啓一個cmd窗口,原來的不要關閉,不然就無法訪問服務端了
	切換到redis目錄下運行 
		redis-cli.exe -h 127.0.0.1 -p 6379
		或者
		redis-cli.exe
		不加任何參數默認就是127.0.0.1 和 6379

	設置鍵值對操作 set name briup
	取出鍵值對操作 get name

	這時候客戶端使用shutdown命令可以關閉服務端

注意:
	Redis中命令本身不區分大小寫(但是key值是區分
	大小寫的),在redis自帶的客戶端執行相關命令是自帶命令格式的提示的

客戶端常用命令:

clear 清屏
Auth 命令用於檢測給定的密碼和配置文件中的密碼是否相符
Ping 命令使用客戶端向 Redis 服務器發送一個 PING ,如果服務器運作正常的話,會返回一個 PONG 。
	通常用於測試與服務器的連接是否仍然生效,或者用於測量延遲值。
Quit 命令用於關閉當前客戶端與redis服務的連接。
	一旦所有等待中的回覆(如果有的話)順利寫入到客戶端,連接就會被關閉
Select 命令用於切換到指定的數據庫,數據庫索引號 index 用數字值指定,以 0 作爲起始索引值。
dbsize 查看當前數據庫中有多少個key
info   返回redis的相關信息
flushdb  刪除當前選擇數據庫中的所有key
flushall 刪除所有數據庫中的數據

Redis配置參數介紹

redis.windows.conf文件中配置項如下:

1)*Redis默認不是以守護進程的方式運行,可以通過該配置項修改,使用yes啓用守護進程(windows版本中不支持)

	daemonize no

2)*當Redis以守護進程方式運行時,Redis默認會把pid寫入/var/run/redis.pid文件,可以通過pidfile指定(windows版本中不支持)

	pidfile /var/run/redis.pid

3)*指定Redis監聽端口,默認端口爲6379

	port 6379

4)*綁定的主機地址

	bind 127.0.0.1

5)*當客戶端閒置多長時間後關閉連接,如果指定爲0,表示關閉該功能

	timeout 300

6)指定日誌記錄級別,Redis總共支持四個級別:debug、verbose、notice、warning,默認爲notice(redis版本不同會有所區別)

	loglevel notice

7)日誌記錄方式,默認爲標準輸出

	logfile stdout

8)*設置數據庫的數量,默認數據庫的index爲0,可以使用SELECT <dbid>命令在連接上指定數據庫id

	databases 16

9)*指定在多長時間內,有多少次更新操作,就將數據同步到數據文件,可以多個條件配合

	save <seconds> <changes>

	Redis默認配置文件中提供了三個條件:

	save 900 1

	save 300 10

	save 60 10000

	分別表示900秒(15分鐘)內有1個更改,300秒(5分鐘)內有10個更改以及60秒內有10000個更改。
 
10)*指定存儲至本地數據庫時是否壓縮數據,默認爲yes,Redis採用LZF壓縮,如果爲了節省CPU時間,可以關閉該選項,但會導致數據庫文件變的巨大

	rdbcompression yes

11)*指定redis的本地數據庫文件名,默認值爲dump.rdb

	dbfilename dump.rdb

12)*指定redis本地數據庫存放目錄,默認是./ 可以根據自己的情況更改

	dir ./

13)設置當本機爲slav服務時,設置master服務的IP地址及端口,在Redis啓動時,它會自動從master進行數據同步

	slaveof <masterip> <masterport>

14)當master服務設置了密碼保護時,slav服務連接master的密碼

	masterauth <master-password>

15)*設置Redis連接密碼,如果配置了連接密碼,客戶端在連接Redis時需要通過AUTH <password>命令提供密碼,默認關閉

	requirepass briup

16)*設置同一時間最大客戶端連接數,默認無限制,Redis可以同時打開的客戶端連接數爲Redis進程可以打開的最大文件描述符數,如果設置 maxclients 0,表示不作限制。當客戶端連接數到達限制時,Redis會關閉新的連接並向客戶端返回max number of clients reached錯誤信息

	maxclients 128

17)指定Redis最大內存限制,Redis在啓動時會把數據加載到內存中,達到最大內存後,Redis會先嚐試清除已到期或即將到期的Key,當此方法處理後,仍然到達最大內存設置,將無法再進行寫入操作,但仍然可以進行讀取操作。Redis新的vm機制,會把Key存放內存,Value會存放在swap區

	maxmemory <bytes>

18)指定是否在每次更新操作後進行日誌記錄,Redis在默認情況下是異步的把數據寫入磁盤,如果不開啓,可能會在斷電時導致一段時間內的數據丟失。因爲 redis本身同步數據文件是按上面save條件來同步的,所以有的數據會在一段時間內只存在於內存中。默認爲no

	appendonly no

19)*指定更新日誌文件名,默認爲appendonly.aof

	appendfilename appendonly.aof

20)*指定更新日誌條件,共有3個可選值
	no:表示等操作系統進行數據緩存同步到磁盤(快)     
	always:表示每次更新操作後手動調用fsync()將數據寫到磁盤(慢,安全)     
	everysec:表示每秒同步一次(折衷,默認值)

	appendfsync everysec
 
21)指定是否啓用虛擬內存機制,默認值爲no
	VM機制將數據分頁存放,由Redis將訪問量較少的頁即冷數據swap到磁盤上,訪問多的頁面由磁盤自動換出到內存中

	vm-enabled no

22)虛擬內存文件路徑,默認值爲/tmp/redis.swap,不可多個Redis實例共享

	vm-swap-file /tmp/redis.swap

23)將所有大於vm-max-memory的數據存入虛擬內存,無論vm-max-memory設置多小,所有索引數據都是內存存儲的(Redis的索引數據 就是keys),也就是說,當vm-max-memory設置爲0的時候,其實是所有value都存在於磁盤。默認值爲0

	 vm-max-memory 0

24)Redis swap文件分成了很多的page,一個對象可以保存在多個page上面,但一個page上不能被多個對象共享,vm-page-size是要根據存儲的數據大小來設定的,建議如果存儲很多小對象,page大小最好設置爲32或者64bytes;如果存儲很大大對象,則可以使用更大的page,如果不確定,就使用默認值

	 vm-page-size 32

25)設置swap文件中的page數量,由於頁表(一種表示頁面空閒或使用的bitmap)是在放在內存中的,在磁盤上每8個pages將消耗1byte的內存。

	 vm-pages 134217728

26)設置訪問swap文件的線程數,最好不要超過機器的核數,如果設置爲0,那麼所有對swap文件的操作都是串行的,可能會造成比較長時間的延遲。默認值爲4

	 vm-max-threads 4

27)設置在向客戶端應答時,是否把較小的包合併爲一個包發送,默認爲開啓

	glueoutputbuf yes

28)指定在超過一定的數量或者最大的元素超過某一臨界值時,採用一種特殊的哈希算法

	hash-max-zipmap-entries 64

	hash-max-zipmap-value 512

29)指定是否激活重置哈希,默認爲開啓

	activerehashing yes

30)指定包含其它的配置文件,可以在同一主機上多個Redis實例之間使用同一份配置文件,而同時各個實例又擁有自己的特定配置文件

	include /path/to/local.conf

Redis支持的數據類型(主要指的value,key一般就是字符串)

string(字符串) hash(哈希) list(列表) set(集合) zset(sorted set:有序集合)

string(字符串)

	string是redis最基本的類型,一個key對應一個value。
	string類型是二進制安全的。意思是redis的string可以包含任何數據。比如jpg圖片或者序列化的對象 。
	string類型是Redis最基本的數據類型,一個鍵最大能存儲512MB。
	例如:
	127.0.0.1:6379> SET name "briup"
	OK

	127.0.0.1:6379> GET name
	"briup"
	
	//刪除
	127.0.0.1:6379> del name
	(integer) 1

	注意:value中的雙引號要不要都可以

hash(哈希)

	Redis hash 是一個鍵值對集合。
	Redis hash 是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
	例如:
	127.0.0.1:6379> HMSET user username briup password 123
	OK

	127.0.0.1:6379> HGETALL user
	1) "username"
	2) "briup"
	3) "password"
	4) "123"

	127.0.0.1:6379> hget user username
	"briup"
	
	//刪除
	127.0.0.1:6379> del user
	(integer) 1

	例子中使用了 hash 數據類型存儲了包含用戶信息的用戶對象。 
	每個 hash 可以存儲2的31次方個鍵值對。

	注意:命令不區分大小寫

list(列表)

	Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。

	127.0.0.1:6379> lpush mylist briup
	(integer) 1

	127.0.0.1:6379> lpush mylist hello
	(integer) 2

	127.0.0.1:6379> lpush mylist world
	(integer) 3

	127.0.0.1:6379> lrange mylist 0 0
	1) "world"

	127.0.0.1:6379> lrange mylist 0 1
	1) "world"
	2) "hello"

	127.0.0.1:6379> lrange mylist 0 2
	1) "world"
	2) "hello"
	3) "briup"

	//刪除
	127.0.0.1:6379> del mylist
	(integer) 1

	列表最多可存儲2的31次方個元素。

set(集合)

	Redis的Set是string類型的無序集合。
	集合是通過哈希表實現的。

	sadd 命令
	添加一個string元素到,key對應的set集合中,成功返回1,如果元素已經存在則返回0,key對應的set不存在返回錯誤。
	
	例如:
	127.0.0.1:6379> sadd myset hello
	(integer) 1
	127.0.0.1:6379> sadd myset world
	(integer) 1
	127.0.0.1:6379> sadd myset briup
	(integer) 1
	127.0.0.1:6379> sadd myset briup
	(integer) 0
	127.0.0.1:6379> smembers myset
	1) "hello"
	2) "world"
	3) "briup"

	//刪除
	127.0.0.1:6379> del myset
	(integer) 1

	集合中最大的成員數爲 2的31次方。

zset(有序集合)

	Redis中 zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。
	不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。

	zset的成員是唯一的,但分數(score)卻可以重複。

	zadd 命令
	添加元素到集合,元素在集合中存在則更新對應score
	例如:

	127.0.0.1:6379> zadd myzset 2 briup
	(integer) 1

	127.0.0.1:6379> zadd myzset 0 hello
	(integer) 1

	127.0.0.1:6379> zadd myzset 1 world
	(integer) 1
	
	127.0.0.1:6379> zadd myzset 1 gogo
	(integer) 1
	
	127.0.0.1:6379> zrangebyscore myzset 0 3
	1) "hello"
	2) "gogo"
	3) "world"
	4) "briup"
	
	//刪除
	127.0.0.1:6379> del myzset
	(integer) 1

Redis對key的操作命令

DEL 命令用於刪除已存在的鍵。不存在的 key 會被忽略。
EXISTS 命令用於檢查給定 key 是否存在。
Expire 命令用於設置 key 的過期時間。key 過期後將不再可用。
Expireat 命令用於以時間戳(unix timestamp)格式設置 key 的過期時間。key 過期後將不再可用。
PEXPIREAT 命令用於設置 key 的過期時間,單位是毫秒。key 過期後將不再可用
PERSIST 命令用於移除給定 key 的過期時間,使得 key 永不過期。
Keys 命令用於查找所有符合給定模式 pattern 的 key,可以使用*通配符
MOVE 命令用於將當前數據庫的 key 移動到給定的數據庫 db 當中。
	默認Redis中有16個數據庫,下標分別爲0-15,默認使用的是0(select 0)
	通過select可以切換數據庫 例如: select 3
TTL 命令以秒爲單位返回 key 的剩餘過期時間。
Pttl 命令以毫秒爲單位返回 key 的剩餘過期時間。
RANDOMKEY 命令從當前數據庫中隨機返回一個 key 。
Rename 命令用於修改 key 的名稱 。
	rename msg my_msg
Renamenx 命令用於在新的 key 不存在時修改 key 的名稱 。
	renamenx username name
	只要新的名字在redis中不存在的時候纔會修改成功
Type 命令用於返回 key 所儲存的值的類型。

Redis對字符串(String)的操作命令

SET 命令用於設置給定 key 的值。如果 key 已經存儲其他值, SET 就覆寫舊值,且無視類型。
Get 命令用於獲取指定 key 的值。如果 key 不存在,返回 nil 。如果key 儲存的值不是字符串類型,返回一個錯誤。

Getrange 命令用於獲取存儲在指定 key 中字符串的子字符串。字符串的截取範圍由 start 和 end 兩個偏移量決定(包括 start 和 end 在內)。

Getset 命令用於設置指定 key 的值,並返回 key 舊的值。

Mset 命令用於同時設置一個或多個 key-value 對。
Mget 命令返回所有(一個或多個)給定 key 的值。 如果給定的 key 裏面,有某個 key 不存在,那麼這個 key 返回特殊值 nil 。

Setex 命令爲指定的 key 設置值及其過期時間。如果 key 已經存在, SETEX 命令將會替換舊的值。
Psetex 命令以毫秒爲單位設置 key 的生存時間。

Setnx(SET if Not eXists) 命令在指定的 key 不存在時,爲 key 設置指定的值。
Msetnx 命令用於所有給定 key 都不存在時,同時設置一個或多個 key-value 對。

Setrange 命令用指定的字符串覆蓋給定 key 所儲存的字符串值,覆蓋的位置從偏移量 offset 開始。
Strlen 命令用於獲取指定 key 所儲存的字符串值的長度。當 key 儲存的不是字符串值時,返回一個錯誤。

Incr 命令將 key 中儲存的數字值增一。
	如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 INCR 操作。
	如果值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。
	本操作的值限制在 64 位(bit)有符號數字表示之內。
Incrby 命令將 key 中儲存的數字加上指定的增量值。
	如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 INCRBY 命令。
	如果值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。
	本操作的值限制在 64 位(bit)有符號數字表示之內。
Incrbyfloat 命令爲 key 中所儲存的值加上指定的浮點數增量值。
	如果 key 不存在,那麼 INCRBYFLOAT 會先將 key 的值設爲 0 ,再執行加法操作。
Decr 命令將 key 中儲存的數字值減一。
	如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 DECR 操作。
	如果值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。
	本操作的值限制在 64 位(bit)有符號數字表示之內。
Decrby 命令將 key 所儲存的值減去指定的減量值。
	如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 DECRBY 操作。
	如果值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。
	本操作的值限制在 64 位(bit)有符號數字表示之內。
Append 命令用於爲指定的 key 追加值。	
	如果 key 已經存在並且是一個字符串, APPEND 命令將 value 追加到 key 原來的值的末尾。
	如果 key 不存在, APPEND 就簡單地將給定 key 設爲 value ,就像執行 SET key value 一樣。

Redis對哈希(Hash)的操作命令

Hset 命令用於爲哈希表中的字段賦值 。
	如果哈希表不存在,一個新的哈希表被創建並進行 HSET 操作。
	如果字段已經存在於哈希表中,舊值將被覆蓋。
Hsetnx 命令用於爲哈希表中不存在的的字段賦值 。
	如果哈希表不存在,一個新的哈希表被創建並進行 HSET 操作。
	如果字段已經存在於哈希表中,操作無效。
	如果 key 不存在,一個新哈希表被創建並執行 HSETNX 命令。
Hmset 命令用於同時將多個 field-value (字段-值)對設置到哈希表中。
	此命令會覆蓋哈希表中已存在的字段。
	如果哈希表不存在,會創建一個空哈希表,並執行 HMSET 操作
Hget 命令用於返回哈希表中指定字段的值。
Hmget 命令用於返回哈希表中,一個或多個給定字段的值。
	如果指定的字段不存在於哈希表,那麼返回一個 nil 值。
Hgetall 命令用於返回哈希表中,所有的字段和值。
Hexists 命令用於查看哈希表的指定字段是否存在。
Hlen 命令用於獲取哈希表中字段的數量。
Hdel 命令用於刪除哈希表 key 中的一個或多個指定字段,不存在的字段將被忽略。
Hkeys 命令用於獲取哈希表中的所有字段名。
Hvals 命令返回哈希表所有字段的值。
Hincrby 命令用於爲哈希表中的字段值加上指定增量值。
	增量也可以爲負數,相當於對指定字段進行減法操作。
	如果哈希表的 key 不存在,一個新的哈希表被創建並執行 HINCRBY 命令。
	如果指定的字段不存在,那麼在執行命令前,字段的值被初始化爲 0 。
	對一個儲存字符串值的字段執行 HINCRBY 命令將造成一個錯誤。
	本操作的值被限制在 64 位(bit)有符號數字表示之內。
Hincrbyfloat 命令用於爲哈希表中的字段值加上指定浮點數增量值。
	如果指定的字段不存在,那麼在執行命令前,字段的值被初始化爲 0 。

Redis對列表(List)的操作命令

Lpush 命令將一個或多個值插入到列表頭部。 如果 key 不存在,一個空列表會被創建並執行 LPUSH 操作。 當 key 存在但不是列表類型時,返回一個錯誤。

Lrange 返回列表中指定區間內的元素,區間以偏移量 START 和 END 指定。 其中 0 表示列表的第一個元素, 1 表示列表的第二個元素,以此類推。 你也可以使用負數下標,以 -1 表示列表的最後一個元素, -2 表示列表的倒數第二個元素,以此類推。

Lpushx 將一個或多個值插入到已存在的列表頭部,列表不存在時操作無效。
Lpop 命令用於移除並返回列表的第一個元素。
Llen 命令用於返回列表的長度。 如果列表 key 不存在,則 key 被解釋爲一個空列表,返回 0 。 如果 key 不是列表類型,返回一個錯誤。

Lindex 命令用於通過索引獲取列表中的元素。你也可以使用負數下標,以 -1 表示列表的最後一個元素, -2 表示列表的倒數第二個元素,以此類推。

Linsert 命令用於在列表的元素前或者後插入元素。 當指定元素不存在於列表中時,不執行任何操作。 當列表不存在時,被視爲空列表,不執行任何操作。 如果 key 不是列表類型,返回一個錯誤。
	例如:LINSERT list before hello go
	在名字爲list的列表中,往元素hello之前插入數據go

Lset 通過索引來設置元素的值。當索引參數超出範圍,或對一個空列表進行 LSET 時,返回一個錯誤。
Ltrim 對一個列表進行修剪(trim),就是說,讓列表只保留指定區間內的元素	,不在指定區間之內的元素都將被刪除。下標 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。 你也可以使用負數下標,以 -1 表示列表的最後一個元素, -2 表示列表的倒數第二個元素,以此類推。
Lrem 根據參數 COUNT 的值,移除列表中與參數 VALUE 相等的元素。
	COUNT 的值可以是以下幾種:
	count > 0 : 從表頭開始向表尾搜索,移除與 VALUE 相等的元素,數量爲 COUNT 。
	count < 0 : 從表尾開始向表頭搜索,移除與 VALUE 相等的元素,數量爲 COUNT 的絕對值。
	count = 0 : 移除表中所有與 VALUE 相等的值。

Rpush 命令用於將一個或多個值插入到列表的尾部(最右邊)。
Rpushx 命令用於將一個或多個值插入到已存在的列表尾部(最右邊)。如果列表不存在,操作無效
Rpop 命令用於移除並返回列表的最後一個元素。
Rpoplpush 命令用於移除列表的最後一個元素,並將該元素添加到另一個列表並返回。

Blpop 命令移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。
Brpop 命令移出並獲取列表的最後一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。
Brpoplpush 命令從列表中彈出一個值,將彈出的元素插入到另外一個列表中並返回它; 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。

Redis對集合(Set)的操作命令

Sadd 命令將一個或多個成員元素加入到集合中,已經存在於集合的成員元素將被忽略。
	假如集合 key 不存在,則創建一個只包含添加的元素作成員的集合。
	當集合 key 不是集合類型時,返回一個錯誤。
Scard 命令返回集合中元素的數量。
Smembers 命令返回集合中的所有的成員。 不存在的集合 key 被視爲空集合。
Sdiff 命令返回給定集合之間的差集。不存在的集合 key 將視爲空集。
Sdiffstore 命令將給定集合之間的差集存儲在指定的集合中。如果指定的集合 key 已存在,則會被覆蓋。
Sinter 命令返回給定所有給定集合的交集。 不存在的集合 key 被視爲空集。 當給定集合當中有一個空集時,結果也爲空集(根據集合運算定律)。
Sinterstore 命令將給定集合之間的交集存儲在指定的集合中。如果指定的集合已經存在,則將其覆蓋。
Sismember 命令判斷成員元素是否是集合的成員。
Smove 命令將指定成員 member 元素從 source 集合移動到 destination 集合
	SMOVE 是原子性操作。
	如果 source 集合不存在或不包含指定的 member 元素,則 SMOVE 命令不執行任何操作,僅返回 0 。否則, member 元素從 source 集合中被移除,並添加到 destination 集合中去。
	當 destination 集合已經包含 member 元素時, SMOVE 命令只是簡單地將 source 集合中的 member 元素刪除。
	當 source 或 destination 不是集合類型時,返回一個錯誤。
Spop 命令用於移除並返回集合中的一個隨機元素。
Srandmember 命令用於返回集合中的一個隨機元素。
	從 Redis 2.6 版本開始, Srandmember 命令接受可選的 count 參數:
	如果 count 爲正數,且小於集合基數,那麼命令返回一個包含 count 個元素的數組,數組中的元素各不相同。如果 count 大於等於集合基數,那麼返回整個集合。
	如果 count 爲負數,那麼命令返回一個數組,數組中的元素可能會重複出現多次,而數組的長度爲 count 的絕對值。
	該操作和 SPOP 相似,但 SPOP 將隨機元素從集合中移除並返回,而 Srandmember 則僅僅返回隨機元素,而不對集合進行任何改動。
Srem 命令用於移除集合中的一個或多個成員元素,不存在的成員元素會被忽略。
	當 key 不是集合類型,返回一個錯誤。
	在 Redis 2.4 版本以前, SREM 只接受單個成員值。
Sunion 命令返回給定集合的並集。不存在的集合 key 被視爲空集。
Sunionstore 命令將給定集合的並集存儲在指定的集合 destination 中。
Sscan 命令用於迭代集合鍵中的元素。

Redis對有序集合(sorted set)的操作命令

Zadd 命令用於將一個或多個成員元素及其分數值加入到有序集當中。
	如果某個成員已經是有序集的成員,那麼更新這個成員的分數值,並通過重新插入這個成員元素,來保證該成員在正確的位置上。
	分數值可以是整數值或雙精度浮點數。
	如果有序集合 key 不存在,則創建一個空的有序集並執行 ZADD 操作。
	當 key 存在但不是有序集類型時,返回一個錯誤。
Zcard 命令用於計算集合中元素的數量。
Zcount 命令用於計算有序集合中指定分數區間的成員數量。
Zincrby 命令對有序集合中指定成員的分數加上增量 increment
	可以通過傳遞一個負數值 increment ,讓分數減去相應的值,比如 ZINCRBY key -5 member ,就是讓 member 的 score 值減去 5 。
	當 key 不存在,或分數不是 key 的成員時, ZINCRBY key increment member 等同於 ZADD key increment member 。
	當 key 不是有序集類型時,返回一個錯誤。
	分數值可以是整數值或雙精度浮點數。
Zinterstore 命令計算給定的一個或多個有序集的交集,其中給定 key 的數量必須以 numkeys 參數指定,並將該交集(結果集)儲存到 destination 。
	默認情況下,結果集中某個成員的分數值是所有給定集下該成員分數值之和。
Zlexcount 命令在計算有序集合中指定字典區間內成員數量。
Zrange 返回有序集中,指定區間內的成員。
	其中成員的位置按分數值遞增(從小到大)來排序。
	具有相同分數值的成員按字典序(lexicographical order )來排列。
	如果你需要成員按值遞減(從大到小)來排列,請使用 ZREVRANGE 命令。
	下標參數 start 和 stop 都以 0 爲底,也就是說,以 0 表示有序集第一個成員,以 1 表示有序集第二個成員,以此類推。
	你也可以使用負數下標,以 -1 表示最後一個成員, -2 表示倒數第二個成員,以此類推
Zrangebylex 通過字典區間返回有序集合的成員。
Zrangebyscore 返回有序集合中指定分數區間的成員列表。有序集成員按分數值遞增(從小到大)次序排列。
	具有相同分數值的成員按字典序來排列(該屬性是有序集提供的,不需要額外的計算)。
	默認情況下,區間的取值使用閉區間 (小於等於或大於等於),你也可以通過給參數前增加 ( 符號來使用可選的開區間 (小於或大於)。
Zrank 返回有序集中指定成員的排名。其中有序集成員按分數值遞增(從小到大)順序排列。
Zrem 命令用於移除有序集中的一個或多個成員,不存在的成員將被忽略。
	當 key 存在但不是有序集類型時,返回一個錯誤。
Zremrangebylex 命令用於移除有序集合中給定的字典區間的所有成員。
Zremrangebyrank 命令用於移除有序集中,指定排名(rank)區間內的所有成員
Zremrangebyscore 命令用於移除有序集中,指定分數(score)區間內的所有成員。
Zrevrange 命令返回有序集中,指定區間內的成員。
	其中成員的位置按分數值遞減(從大到小)來排列。
	具有相同分數值的成員按字典序的逆序(reverse lexicographical order)排列。
	除了成員按分數值遞減的次序排列這一點外, ZREVRANGE 命令的其他方面和 ZRANGE 命令一樣。
Zrevrangebyscore 返回有序集中指定分數區間內的所有的成員。有序集成員按分數值遞減(從大到小)的次序排列。
	具有相同分數值的成員按字典序的逆序(reverse lexicographical order )排列。
	除了成員按分數值遞減的次序排列這一點外, ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一樣。
Zrevrank 命令返回有序集中成員的排名。其中有序集成員按分數值遞減(從大到小)排序。
	排名以 0 爲底,也就是說, 分數值最大的成員排名爲 0 。
	使用 ZRANK 命令可以獲得成員按分數值遞增(從小到大)排列的排名。
Zscore 命令返回有序集中,成員的分數值。 如果成員元素不是有序集 key 的成員,或 key 不存在,返回 nil 。
Zunionstore 命令計算給定的一個或多個有序集的並集,其中給定 key 的數量必須以 numkeys 參數指定,並將該並集(結果集)儲存到 destination 。
	默認情況下,結果集中某個成員的分數值是所有給定集下該成員分數值之和 。
Zscan 命令用於迭代有序集合中的元素(包括元素成員和元素分值)

Redis中的事務

multi命令 和 exec命令
例如:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tom
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> set dob 1999-11-11
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK

例如:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tom
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> seta dob 1999-11-11
(error) ERR unknown command 'seta'
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> get dob
(nil)

例如:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name tom
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> hget name age
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get name
"tom"
127.0.0.1:6379> get age
"20"

Redis的事務中的錯誤分倆種:
語法錯誤
	這種情況需要區分Redis的版本,Redis 2.6.5之前的版本會忽略錯誤的命令,執行其他正確的命令,2.6.5之後的版本會忽略這個事務中的所有命令,都不執行

運行錯誤 
	運行錯誤表示命令在執行過程中出現錯誤,比如用hget命令獲取string的值
	這種錯誤在命令執行之前Redis是無法發現的,所以在事務裏這樣的命令會被Redis接受並執行。如果事務裏有一條命令執行錯誤,其他命令依舊會執行

Jedis

Jedis 是 Redis 官方首選的 Java 客戶端開發包。

源碼地址:https://github.com/xetorthio/jedis/releases
其相關jar可以從maven的中央倉庫中下載:
http://central.maven.org/maven2/redis/clients/jedis/2.9.0/jedis-2.9.0.jar

jedis的相關jar包

在java中使用jedis中的api操作redis的時候所需jar包主要有倆個:
	jedis-2.x.jar
	commons-pool2-2.4.2.jar

另外需要一個junit的包進行單元測試:
	junit-4.x.jar

Jedis對象的使用

整個過程類似之前使用Connection對象一樣,一個Jedis對象就是一個數據庫連接對象,這不過這個數據庫是Redis(內存數據庫)
1)創建Jedis對象
	Jedis jedis = new Jedis();
	或者:
	Jedis jedis = new Jedis("127.0.0.1",6379);
	或者:
	Jedis jedis = new Jedis(new java.net.URI("redis://:[email protected]:6379/0"));
	注意:這裏最後一個0代表的時候index爲0的數據庫
	
	如果沒有密碼的時候可以這樣創建對象
	Jedis jedis = new Jedis(new java.net.URI("redis://127.0.0.1:6379/0"));

2)使用Jedis對象
	使用Jedis的API對redis進行操作,Jedis中的方法名和Redis中命令名基本上一模一樣,所以只要瞭解Redis中命令的作用,那麼Jedis中對應的方法的作用也是一樣的.

3)關閉Jedis對象
	每個Jedis對象在內部都會建立一個Socket和Redis服務器連接,所以Jedis對象使用完畢後可以進行關閉。

所以在使用junit進行基本的jedis操作測試的時候,可以編寫如下代碼:
public class RedisTest {
	private Jedis jedis;
	@Before
	public void before(){
		jedis = new Jedis();
	}
	@After
	public void after(){
		jedis.close();
	}
	
	@Test
	public void test(){
		//使用jedis對進行操作
	}
	
}

意思是,在單元測試方法執行之前,junit先調用使用了@Before註解的方法,然後再調用使用了@Test註解的方法,最後再調用使用了@After註解的方法

Jedis對Redis中key的操作

@Test
public void test_key(){
	//選擇數據庫 默認是0
	System.out.println(jedis.select(0));
	//查看當前數據庫
	System.out.println(jedis.getDB());
	//查看當前數據庫中所有key值
	System.out.println(jedis.keys("*"));
	//判斷當前數據庫中是否存在名字爲name的key值
	System.out.println(jedis.exists("name"));
	//判斷key值的在Redis中的數據類型
	System.out.println(jedis.type("name"));
	//查看key的過期時間 單位是毫秒 -1表示永不過期 -2表示已經過去
	System.out.println(jedis.pttl("name"));
	//查看key的過期時間 單位是秒   -1表示永不過期 -2表示已經過去
	System.out.println(jedis.ttl("name"));
	//修改key的名字 不管新名字是否被使用 如果老的key不存在會報錯
	System.out.println(jedis.rename("name", "username"));
	//修改key的名字 新名字沒有被使用的時候才生效 如果老的key不存在會報錯
	System.out.println(jedis.renamenx("username", "name"));
	//把指定的key移動到指定的數據庫中(數據庫索引表示)
	System.out.println(jedis.move("name",3));
	//在當前數據庫中隨機獲取一個key
	System.out.println(jedis.randomKey());
	//設置指定key的過期時間 單位是秒
	System.out.println(jedis.expire("name", 10));
	//設置指定key的過期時間 單位是毫秒
	System.out.println(jedis.pexpire("name", 5000L));
	//設置指定key的過期時間 時間用時間戳表示
	System.out.println(jedis.expireAt("name", 1000000L));
	//在指定的key過期之前,移除key上的過期時間
	System.out.println(jedis.persist("name"));
	
	//當前數據庫中key的數量 返回值Long類型
	jedis.dbSize();
	//清空當前db
	jedis.flushDB();
	//清空所有db
	jedis.flushAll();
}

Jedis對Redis中string的操作

@Test
public void test_String(){
	//設置k-v的值,如果k已經則會覆蓋舊值,且無視數據類型
	System.out.println(jedis.set("name", "zhangsan"));
	//獲得指定key的值
	System.out.println(jedis.get("name"));
	//通過下標截取指定key的值
	System.out.println(jedis.getrange("name", 0, 3));
	//替換key值,並且返回舊值
	System.out.println(jedis.getSet("name", "briup"));
	//可以連續設置多組K-V
	System.out.println(jedis.mset("name","briup","age","20"));
	//獲得多個key分別對應的值
	System.out.println(jedis.mget("name","age"));
	//設置K-V的同時設置一下過期時間,單位是秒
	System.out.println(jedis.setex("password", 10, "123"));
	//設置K-V的同時設置一下過期時間,單位是毫秒
	System.out.println(jedis.psetex("myname", 10000L, "test"));
	//設置K-V,只有指定的key沒有被使用的時候才生效
	System.out.println(jedis.setnx("name","tom"));
	//連續設置多組K-V,只有在所有指定的key都沒有被使用的時候才生效
	System.out.println(jedis.msetnx("a","testA","b","testB"));
	//使用第三個參數的值,從下標爲3的位置開始覆蓋指定key的值
	System.out.println(jedis.setrange("name", 3, "aaaa"));
	//返回指定key的值的長度
	System.out.println(jedis.strlen("name"));
	//指定的值自增1 num的值必須是整數
	System.out.println(jedis.incr("num"));
	//指定的值自增5 num的值必須是整數
	System.out.println(jedis.incrBy("num",5));
	//指定的值自減1 num的值必須是整數
	System.out.println(jedis.decr("num"));
	//指定的值自減5 num的值必須是整數
	System.out.println(jedis.decrBy("num", 5));
	//指定的值自增10.5 
	System.out.println(jedis.incrByFloat("num", 10.5));
	//指定的key的值後面進行追加
	System.out.println(jedis.append("name", "gogogo"));
}

Jedis對Redis中hash的操作

@Test
public void test_Hash(){
	//爲哈希表中的字段賦值
	//給key爲user的hash表中的name字段賦值爲briup
	System.out.println(jedis.hset("user", "name", "briup"));
	//爲哈希表中的字段賦值 字段name不存在是才生效
	System.out.println(jedis.hsetnx("user", "name", "tom"));
	//連續爲哈希表中的多個字段賦值
	Map<String,String> map = new HashMap<>();
	map.put("age", "20");
	map.put("dob", "2016-11-11");
	System.out.println(jedis.hmset("user",map));
	//獲得名字爲user的hash表中指定字段的值
	System.out.println(jedis.hget("user", "name"));
	//連續獲得名字爲user的hash表中多個字段的值
	System.out.println(jedis.hmget("user", "age","dob"));
	//獲得名字爲user的hash表中所有字段的值
	System.out.println(jedis.hgetAll("user"));
	//檢查名字爲user的hash表中指定字段是否存在
	System.out.println(jedis.hexists("user", "name"));
	//獲得名字爲user的hash表中字段的個數
	System.out.println(jedis.hlen("user"));
	//刪除名字爲user的hash表中的指定字段,同時忽略掉不存在的字段
	System.out.println(jedis.hdel("user", "dob","f1","f2"));
	//獲得名字爲user的hash表中所有的字段名
	System.out.println(jedis.hkeys("user"));
	//獲得名字爲user的hash表中所有的字段值
	System.out.println(jedis.hvals("user"));
	//名字爲user的hash表中的age字段值自增1
	System.out.println(jedis.hincrBy("user", "age", 1L));
	//名字爲user的hash表中的age字段值自增10.5
	System.out.println(jedis.hincrByFloat("user", "age", 10.5));
}

Jedis對Redis中其他類型的操作

Jedis對Redis中list的操作
Jedis對Redis中set的操作
Jedis對Redis中zset的操作
和Redis中的命令名字及用戶一樣,這裏就不再一一列舉

Jedis處理Redis中的事務

Transaction tx = jedis.multi();

//操作數據

//List集合中是每個操作返回的執行結果
List<Object> results = tx.exec();

例如:

@Test
public void test_List(){
	//開啓事務之後 要是tx來操作,而不是jedis對象
	Transaction tx = jedis.multi();
	tx.set("age", "20");
	tx.set("test", "test");
	Response<String> response = tx.get("name");
	
	//返回值是List集合,裏面是每個操作的執行結果
	tx.exec();
	
	//事務中查詢到的數據 要到事務結束後再拿出結果
	System.out.println(response.get());
}

JedisPool的使用(連接池)

在不同的線程中使用相同的Jedis實例會發生併發訪問問題。但是創建太多的Jedis實例也不好,因爲這意味着會建立很多Socket連接。單一Jedis實例不是線程安全的。爲了避免這些問題,可以使用JedisPool, JedisPool是一個線程安全的網絡連接池。可以用JedisPool創建一些可靠Jedis實例,可以從池中拿到Jedis的實例。 這種方式可以解決那些問題並且會實現高效的性能 

1)創建JedisPool
	JedisPool pool = new JedisPool();
	或者
	JedisPool pool = new JedisPool("127.0.0.1",6379);
	或者
	JedisPoolConfig config = new JedisPoolConfig();  
	//設置最大連接數
	config.setMaxTotal(80);
	//設置最大空閒數
	config.setMaxIdle(20);
	//設置超時時間
	config.setMaxWaitMillis(3000);
	JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379); 

2)使用JedisPool獲得Jedis對象
	JedisPool pool = new JedisPool();
	Jedis jedis = pool.getResource();

3)從JedisPool獲得的Jedis對象再使用後調用close方法即可
	通過查看源碼可知,close方法內部會把這個jedis對象進回收
	public void close(){
		if (this.dataSource != null) {
			if (this.client.isBroken())
				this.dataSource.returnBrokenResource(this);
			else
				this.dataSource.returnResource(this);
		}else{
			this.client.close();
		}
	}

使用Jedis存儲java對象

Jedis類繼承了BinaryJedis類
Jedis中的方法的參數基本上全都是String類型,BinaryJedis類中的方法參數基本全都是byte[]類型的,所以Jedis對象也可以調用那些可以接受byte[]類型參數的方法,而且java中的任何可以被序列化的對象都可以轉換爲字節數組. 

1)創建工具類SerializingUtils
負責把對象和byte[]之間進行轉換
public class SerializingUtils {

	public static byte[] serialize(Object obj) {
		ByteArrayOutputStream bos = null;
		ObjectOutputStream out = null;
		try {
			bos = new ByteArrayOutputStream();
			out = new ObjectOutputStream(bos);
			out.writeObject(obj);
			out.flush();
		}
		catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				if(out!=null)out.close();
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
		return bos.toByteArray();
	}
	
	public static Object deserialize(byte[] data) {
		ObjectInputStream in = null;
		Object obj = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(data);
			in = new ObjectInputStream(bis);
			obj = in.readObject();
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(in!=null)in.close();
			}catch (IOException e) {
				e.printStackTrace();
			}
		}
		return obj;
	}
	
}


2)創建工具類JedisUtils
把Jedis對Redis的操作進行封裝,使其更加方便使用,例如可以直接存/取java對象,要求對象實現序列化接口,同時也可以使用之前的Jedis相關方法

src下面的資源文件redis.properties
redis.pool.maxTotal=100
redis.pool.maxIdle=20
redis.pool.maxWait=3000
redis.ip=127.0.0.1
redis.port=6379

封裝的工具類:
注意:這裏只時封裝了幾個方法,可以根據自己的實現需求而進一步的封裝擴展
public class JedisUtils {
	public static JedisPool jedisPool;

	static {
		//ResourceBundle會查找classpath下的xxx.properties的文件,xxx是方法中指定的
		ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
		int maxTotal = Integer.parseInt(resourceBundle.getString("redis.pool.maxTotal"));
		int maxIdle = Integer.parseInt(resourceBundle.getString("redis.pool.maxIdle"));
		int maxWait = Integer.parseInt(resourceBundle.getString("redis.pool.maxWait"));
		String ip = resourceBundle.getString("redis.ip");
		int port = Integer.parseInt(resourceBundle.getString("redis.port"));

		JedisPoolConfig config = new JedisPoolConfig();
		// 設置最大連接數
		config.setMaxTotal(maxTotal);
		// 設置最大空閒數
		config.setMaxIdle(maxIdle);
		// 設置超時時間
		config.setMaxWaitMillis(maxWait);
		// 初始化連接池
		jedisPool = new JedisPool(config, ip, port);

	}
	
	public static void set(Object key, Object value) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.set(SerializingUtils.serialize(key), SerializingUtils.serialize(value));
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			jedis.close();
		}
	}
	
	public static Object get(Object key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			byte[] keyBytes = SerializingUtils.serialize(key);
			if(jedis.exists(keyBytes)){
				return SerializingUtils.deserialize(jedis.get(keyBytes));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			jedis.close();
		}
		return null;
	}
	
	public static void del(Object key) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.del(SerializingUtils.serialize(key));
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			jedis.close();
		}
	}
	
	public static void clear() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			jedis.flushDB();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			jedis.close();
		}
	}
	public static int getSize() {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			jedis.close();
		}
		return jedis.dbSize().intValue();
	}
	
	public static Jedis getResource(){
		return jedisPool.getResource();
	}

}

Redis和mybatis結合

注意:Jedis是用來操作Redis的

mybatis緩存分爲一級緩存和二級緩存

一級緩存,又叫本地緩存,是PerpetualCache類型的永久緩存,保存在執行器中(BaseExecutor),而執行器又在SqlSession(DefaultSqlSession)中,所以一級緩存的生命週期與SqlSession是相同的。

二級緩存,又叫自定義緩存,實現了Cache接口的類都可以作爲二級緩存,所以可配置如encache等的第三方緩存。二級緩存以namespace名稱空間爲其唯一標識,被保存在Configuration核心配置對象中。

所以我們可以自定義緩存類,實現Mybatis提供的緩存接口Cache,其中的方法使用Jedis來實現,然後把這個緩存實現類配置爲Mybatis的二級緩存提供者即可

建表語句及實體類:

create table t_user(
	id number primary key,
	name varchar2(50) not null,
	age number,
	dob date
);

public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	private long id;
	private String name;
	private int age;
	private Date dob;

	get/set
}

Mybatis配置文件及config.properties文件

src下面的mybatis-config.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC 
	"-//mybatis.org//DTD Config 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<properties resource="config.properties"></properties>
	
	<settings>
		<!-- 開啓二級緩存 -->
		<setting name="cacheEnabled" value="true"/>
		<!-- 禁止延遲加載 -->
		<setting name="lazyLoadingEnabled" value="false" />  
	</settings>
	
	<typeAliases>
		<package name="com.briup.bean"/>
	</typeAliases>
	
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"></transactionManager>
			<dataSource type="POOLED">
				<property name="driver" value="${driver}" /> 
				<property name="url" value="${url}" /> 
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	
	<mappers>
		<mapper resource="com/briup/dao/UserMapper.xml"/>
	</mappers>
	
</configuration>


src下面的config.properties文件

driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:XE
username=briup
password=briup

Mybatis映射接口和映射文件

映射接口:
public interface UserMapper {
	public void insertUser(User user);
	public List<User> findAllUsers();
}

映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC 
	"-//mybatis.org//DTD Mapper 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.briup.dao.UserMapper">
	<cache type="com.briup.cache.MybatisRedisCache"></cache>
	
	<insert id="insertUser" parameterType="User">
		<selectKey resultType="long" keyProperty="id" order="BEFORE">
			select my_seq.nextval from dual
		</selectKey>
		insert into t_user(id,name,age,dob) 
		values(#{id},#{name},#{age},#{dob})
	</insert>
	
	<select id="findAllUsers" resultType="User">
		select id,name,age,dob
		from t_user
	</select>
	
</mapper>

Jedis相關配置文件和工具類

src下面的config.properties

封裝Jedis的工具類
JedisUtils.java
SerializingUtils.java
封裝MyBatis的工具類
MyBatisSqlSessionFactory.java

自定義cache接口實現類MybatisRedisCache.java

package com.briup.cache;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
import com.briup.util.JedisUtils;
import redis.clients.jedis.Jedis;
/**
 * Cache爲緩存接口,給緩存供應商的SPI(Service Provider Interface)
 * Cache接口的實現類必須有一個具有String類型參數的構造方法,該參數作爲實現類對象的id,對其進行唯一標識
 */
public class MybatisRedisCache implements Cache{

	private String id;
	
	public MybatisRedisCache(String id) {
		this.id = id;
	}
	
	/**
	 * 清空緩存
	 */
	@Override
	public void clear() {
		JedisUtils.clear();
	}
	
	/**
	 * 獲取緩存對象的唯一標識
	 */
	@Override
	public String getId() {
		return this.id;
	}
	
	/**
	 * 從緩存對象中獲取key對應的value
	 */
	@Override
	public Object getObject(Object key) {
		return JedisUtils.get(key);
	}
	
	/**
	 * 獲取讀寫鎖
	 * 可選的方法,從3.2.6起這個方法不再被框架核心調用
	 * 任何需要的鎖,都必須由緩存供應商提供
	 */
	@Override
	public ReadWriteLock getReadWriteLock() {
		
		return null;
	}
	
	/**
	 * 獲取緩存對象中存儲的鍵/值對的數量
	 * 可選的方法,沒有被框架核心調用
	 */
	@Override
	public int getSize() {
		return JedisUtils.getSize();
	}
	
	/**
	 * 保存key/value到緩存對象中
	 * key可以是任何對象
	 */
	@Override
	public void putObject(Object key, Object value) {
		JedisUtils.set(key, value);
	}
	
	/**
	 * 可選的方法,沒有被核心框架調用,移除key對應的value
	 */
	@Override
	public Object removeObject(Object key) {
		
		return null;
	}
	
	/**
	 * 重新equals方法
	 */
	@Override
	public boolean equals(Object o) {
		if (getId() == null)
			throw new CacheException("Cache instances require an ID.");
		if (this == o)
			return true;
		if (!(o instanceof Cache))
			return false;

		Cache otherCache = (Cache) o;
		return getId().equals(otherCache.getId());
	}
	
	/**
	 * 重新hashCode方法
	 */
	@Override
	public int hashCode() {
		if (getId() == null)
			throw new CacheException("Cache instances require an ID.");
		return getId().hashCode();
	}

}

注意:在mybatis中開啓二級緩存(雖然默認也是開啓的)
<settings>
	<setting name="cacheEnabled" value="true"/>
</settings>

配置標籤

在需要緩衝的映射文件中加入<cache/>標籤,並且指定提供緩存功能的cache實現類的全限定名
<cache type="com.briup.cache.MybatisRedisCache"></cache>

測試方法

@Test
public void findAllUsers(){
	SqlSession session = null;
	try {
		session = MyBatisSqlSessionFactory.openSession();
		
		UserMapper dao = session.getMapper(UserMapper.class);
		
		List<User> list = dao.findAllUsers();
		System.out.println(list);
		
	} catch (Exception e) {
		e.printStackTrace();
	}finally {
		if(session!=null)session.close();
	}
}

第一次運行,mybatis【發出】對應select進行查詢數據庫,然後在redis中查看key可知,已經把數據存進了redis中,
之後再運行,mybatis【不發出】sql語句,數據直接從redis中取出

注意事項

注意1
在MyBatis中標籤中有flushCache、useCache這兩個配置屬性
如果屬性是在select標籤中設置: 
	flushCache默認爲false,表示任何時候此標籤中的sql語句被調用,都不會去清空本地緩存和二級緩存。 
	useCache默認爲true,表示會將本條語句的查詢結果進行二級緩存。 
如果屬性是在insert、update、delete標籤中設置
	flushCache默認爲true,表示任何時候語句被調用,都會導致本地緩存和二級緩存被清空。 
	useCache屬性在該情況下不存在。

注意2
<settings>
	<!-- 開啓二級緩存 -->
	<setting name="cacheEnabled" value="true"/>

	<!-- 禁用延遲加載 --> 
	<!-- 因爲不應該把一個代理對象緩存到Redis裏面 --> 
	<setting name="lazyLoadingEnabled" value="false" />  
</settings>

Redis和Spring結合(同時還要加上Mybatis)

主要依賴的核心jar爲:
	spring-data-redis-1.x.x.RELEASE.jar
注意:
	spring3.2.4需要結合使用spring-data-redis-1.6.2.RELEASE.jar

	spring4.3.3可以結合使用spring-data-redis-1.6.2.RELEASE.jar或者更高的版本

從3.1開始,spring引入了對Cache的支持。當我們在調用一個方法時會把該方法參數和返回結果作爲一個鍵值對存放在緩存中,等到下次利用同樣的參數來調用該方法時將不再執行該方法,而是直接從緩存中獲取結果進行返回。
	使用Spring Cache需要我們做的事情:
		1.配置Spring對Cache的支持
		2.配置jedis工廠、redis模板、cache管理器
		3.聲明哪些方法要使用緩存

配置Spring對Cache的支持

	配置Spring對基於註解的Cache的支持,首先我們需要在Spring的配置文件中引入cache命名空間,其次通過<cache:annotation-driven />就可以啓用Spring對基於註解的Cache的支持。

	<cache:annotation-driven/>有一個cache-manager屬性用來指定當前所使用的CacheManager對應的bean的名稱,默認值是cacheManager,所以當我們的CacheManager的id爲cacheManager時我們可以不指定該參數,否則就需要我們指定了。

	<cache:annotation-driven/>還可以指定一個mode屬性,可選值有proxy和aspectj。默認是使用proxy。當mode爲proxy時,只有緩存方法在外部被調用的時候Spring Cache纔會發生作用,這也就意味着如果一個緩存方法在其聲明對象內部被調用時Spring Cache是不會發生作用的。而mode爲aspectj時就不會有這種問題。另外使用proxy時,只有public方法上的@Cacheable等標註纔會起作用,如果需要非public方法上的方法也可以使用Spring Cache時把mode設置爲aspectj。

	<cache:annotation-driven/>還可以指定一個proxy-target-class屬性,表示是否要代理class,默認爲false。緩存註解@Cacheable、@cacheEvict等是可以標註在接口上,這對於基於接口的代理來說是沒有什麼問題的,但是需要注意的是當我們設置proxy-target-class爲true或者mode爲aspectj時,是直接基於class進行操作的,定義在接口上的@Cacheable等Cache註解不會被識別到,那對應的Spring Cache也不會起作用了。
	同時注意<aop:config proxy-target-class="false">這裏設置爲false纔行

配置jedis工廠、redis模板、cache管理器

	<!-- 配置jedis工廠 -->
	<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="127.0.0.1"></property>
		<property name="port" value="6379"></property>
		<property name="usePool" value="true"></property>
		<property name="database" value="0"></property>
	</bean>
	
	<!-- 配置redis模板 -->
	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory"></property>
	</bean>
	
	<!-- 配置cache管理器 -->
	<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
		<constructor-arg index="0" ref="redisTemplate"></constructor-arg>
	</bean>

聲明某些方法要使用緩存

@Cacheable

		標記在一個方法上,表示該方法是支持緩存的.對於一個支持緩存的方法,Spring會在其被調用後將其返回值緩存起來,以保證下次利用同樣的參數來執行該方法時可以直接從緩存中獲取結果,而不需要再次執行該方法
		@Cacheable中常用的有三個屬性,value、key和condition。

		value屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪個Cache上的,對應Cache的名稱。注意是cache的名稱,而不是cache中的key,key值會默認生成也可以是自己指定的。cache的名稱是我們操作時用的,cache的key是redis中存儲value時用的。

		key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式。當我們沒有指定該屬性時,Spring將使用默認策略生成key。
			例如:使用user的id值作爲緩存的key值
			@Cacheable(value="users", key="#user.id")
			public User find(User user) {..}

		condition屬性指定緩存發生的條件,有的時候我們可能希望滿足一定條件才進行緩存。其值是通過SpringEL表達式來指定的,當爲true時表示進行緩存處理;當爲false時表示不進行緩存處理
			例如:user的id值大於等於100的時候才進行緩存
			@Cacheable(value="users",condition="#user.id>=100")
			public User find(User user) {..}

@CachePut

		對於使用@Cacheable標註的方法,Spring在每次執行前都會檢查Cache中是否存在相同key的緩存元素,如果存在就不再執行該方法,而是直接從緩存中獲取結果進行返回,否則纔會執行並將返回結果存入指定的緩存中。@CachePut也可以聲明一個方法支持緩存功能。與@Cacheable不同的是,使用@CachePut標註的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。

@CacheEvict

		該註解是用來標註在需要清除緩存元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發緩存的清除操作。

		@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的意思與@Cacheable對應的屬性類似。

		allEntries屬性
		allEntries是boolean類型,表示是否需要清除緩存中的所有元素。默認爲false。
		@CacheEvict(value="users", allEntries=true)
		public void delete(Integer id) {...}

		beforeInvocation屬性
		清除操作默認是在對應方法成功執行之後觸發的,但是方法如果因爲拋出異常而未能成功返回時也不會觸發清除操作。使用beforeInvocation可以改變觸發清除操作的時間,當我們指定該屬性值爲true時,Spring會在調用該方法之前清除緩存中的指定元素
		@CacheEvict(value="users", beforeInvocation=true)
		public void delete(Integer id) {...}

注意1:
測試的項目是把SpringMVC學習中的SSM項目裏面的代碼拿過來進行測試的,沒有修改之前的配置,只是去掉了web層而已.


注意2:
爲了測試方便,在項目中的測試使用了spring-test-3.2.4.RELEASE.jar和Junit4.12(必須是4.12版本或以上)
例如:可以自動幫我們讀取spring配置文件並且自動完成依賴注入
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class Spring_Test {
	
	@Resource
	private User user;
	
	@Test
	public void test(){
		System.out.println(user);
	}
	
}

之後測試的時候這裏可以讀取service層配置文件、dao層配置文件、redis的配置文件,然後將service的實現類對象注入到此測試類中,直接使用並調用service方法並觀察spring的cache註解是否起作用即可。

SSM結合Redis

其實就是在上面的spring和redis結合的情況下,再加入SpringMVC即可	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章