內容
先介紹單機版Redis鏡像在Docker下的安裝,然后在容器的Redis Shell中進行常用類型String、List、Set、Hash、SortedSet的增刪改查操作測試,最后再結合SpringBoot項目進行簡單的測試。后續會推出哨兵模式(Sentinel,一主二從三哨兵)和集群模式(Redis Cluster)的安裝和部署,敬請關注。
版本
操作系統: CentOS 7.2 64位
Docker:17.12.1.ce
Redis: 3.2.12
適合人群
linux運維人員,docker運維人員,java人員
說明
轉載請說明出處:Docker入門實踐筆記(三)一篇文章搞懂Docker下安裝Redis,以及Redis與SpringBoot整合
Demo源碼托管:https://github.com/leo-zz/SpringBootDemo
參考
Docker官方文檔:https://hub.docker.com/r/library/redis/
Redis 命令參考:http://redisdoc.com/
前提
服務器需要安裝Docker CE,未安裝的童鞋請參考 Docker入門實踐筆記(一)——安裝Docker CE ;
此示例將redis的緩存持久化到~/redis/data
路徑下,如果需要分區掛載數據盤的童鞋,請參考Linux入門實踐筆記(三)——數據盤格式化和和多分區掛載。
步驟
下載Redis鏡像
使用docker pull
,從Docker倉庫中下載Redis鏡像,本示例下載的版本為3.2.12。使用docker images
查看已經下載的鏡像信息。
#從Docker倉庫中下載Redis鏡像
[user1@iz8vb62snc6e5cage5yvz9z /]$ sudo docker pull redis:3.2.12
3.2.12: Pulling from library/redis f17d81b4b692: Pull complete b32474098757: Pull complete 8980cabe8bc2: Pull complete 58af19693e78: Pull complete a977782cf22d: Pull complete 9c1e268980b7: Pull complete Digest: sha256:7b0a40301bc1567205e6461c5bf94c38e1e1ad0169709e49132cafc47f6b51f3 Status: Downloaded newer image for redis:3.2.12
#查看已經下載的鏡像信息
[user1@iz8vb62snc6e5cage5yvz9z home]$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE redis 3.2.12 87856cc39862 8 days ago 76MB
配置redis.conf
將redis.conf拷貝到/home/user1/redis/config
路徑下,可以修改此文件以配置Redis;本示例使用默認配置。
[user1@iz8vb62snc6e5cage5yvz9z config]$ ls redis.conf [user1@iz8vb62snc6e5cage5yvz9z config]$ pwd /home/user1/redis/config
創建Redis容器
使用docker run
創建Redis容器,然后使用docker ps
查看容器的運行情況。
[user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker run -p 6379:6379
-v /home/user1/redis/config:/usr/local/etc/redis/redis.conf -v /home/user1/redis/data:/data --name jmsRedis
-d redis:3.2.12 redis-server /usr/local/etc/redis/redis.conf 14d9c846b6586953c9528a0d6cbfe3257f4a936892e8d8778260a7aaf62b79c7 [user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14d9c846b658 redis:3.2.12 "docker-entrypoint.s…" 15 seconds ago Up 15 seconds 0.0.0.0:6379->6379/tcp jmsRedis
創建容器的參數釋義:
-p是指定容器到宿主機的端口映射,這里使用的是Redis的默認端口號6379,映射為宿主機的6379。
-v是指定容器到宿主機的文件映射,這里將Redis容器的持久化數據存放路徑/data映射到宿主機的/home/user1/redis/data;將Redis的配置文件/usr/local/etc/redis/redis.conf映射到宿主機的/home/user1/redis/config。
--name是指定容器的名稱為jmsRedis。
-d表示在后台啟動Redis。
redis-server /usr/local/etc/redis/redis.conf 表示啟動指定配置文件的Redis。
容器內測試
執行docker exec進入容器,由於容器中安裝了Redis,故可以執行redis-cli命令進入Redis shell。
[user1@iz8vb62snc6e5cage5yvz9z redis]$ sudo docker exec -it 14d9c846b658 /bin/bash root@14d9c846b658:/data# redis-cli -h localhost -p 6379
localhost:6379>
測試String類型數據的增刪改查
演示Redis中String類型數據的的設置set、查詢get、刪除del操作。
#將字符串值 value 關聯到 key 。如果 key 已經持有其他值, SET 就覆寫舊值,無視類型。 #對於帶有生存時間(TTL)的鍵來,當SET命令成功在這個鍵上執行時,這個鍵原有的TTL將被清除。 #時間復雜度:O(1);返回值:設置操作成功完成時,返回 OK. localhost:6379> set hello leo OK #返回 key 所關聯的字符串值。當 key 不存在時,返回 nil ,否則,返回 key 的值。 #如果 key 不是字符串類型,那么返回一個錯誤。時間復雜度:O(1) localhost:6379> get hello "leo" #刪除給定的一個或多個 key 。不存在的 key 會被忽略。返回值:被刪除 key 的數量。 #時間復雜度:O(N), N 為被刪除的 key 的數量。刪除單個字符串類型的 key ,時間復雜度為O(1)。刪除單個列表、集合、有序集合或哈希表類型的 key ,
時間復雜度為O(M), M 為以上數據結構內的元素數量。 localhost:6379> del hello (integer) 1
llocalhost:6379> get hello (nil)
測試List類型數據的增刪改查
演示Redis中List類型數據的的插入rpush、范圍查詢lrange、按下標查詢lindex、移除頭元素lpop操作。
#將一個或多個值 value 插入到列表 key 的表尾(最右邊),索引依次遞增。 #時間復雜度:O(1),返回值:rpush后list的長度
localhost:6379> rpush fruit apple (integer) 1 localhost:6379> rpush fruit banana pear orange (integer) 4 #返回列表 key 中指定區間內的元素,區間以偏移量 start 和 stop 指定。下標(index)參數 start 和 stop 都以 0 為底,
也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素;也可以使用負數下標,以 -1 表示列表的最后一個元素,
-2 表示列表的倒數第二個元素,以此類推。#時間復雜度:O(S+N), S 為偏移量 start , N 為指定區間內元素的數量。遍歷1次鏈表拿取所有范圍內的數據。
localhost:6379> lrange fruit 0 -1
1) "apple"
2) "banana"
3) "pear"
4) "orange" #返回列表 key 中,下標為 index 的元素。 時間復雜度O(N),需要遍歷鏈表。
localhost:6379> lindex fruit 2
"pear" #移除並返回列表 key 的頭元素。當 key 不存在時,返回 nil 。 #時間復雜度:O(1),頭元素是指index最小的元素。所有元素的index會隨着pop操作更新
localhost:6379> lpop fruit "apple" localhost:6379> lrange fruit 0 -1
1) "banana"
2) "pear"
3) "orange"
測試SET類型數據的增刪改查
演示Redis中Set類型數據的的插入sadd、集合查詢smembers、判斷成員是否存在sismember、移除成員srem操作。
#將一個或多個 member 元素加入到集合 key 當中,已經存在於集合的 member 元素將被忽略。 #假如 key 不存在,則創建一個只包含 member 元素作成員的集合。當 key 不是集合類型時,返回一個錯誤。 #時間復雜度:O(N), N 是被添加的元素的數量。返回值:被添加到集合中的新元素的數量,不包括被忽略的元素。
localhost:6379> sadd animal tiger (integer) 1 localhost:6379> sadd animal panda (integer) 1 localhost:6379> sadd animal lion (integer) 1 localhost:6379> sadd animal fish (integer) 1 localhost:6379> sadd animal lion (integer) 0 #返回集合 key 中的所有成員。不存在的 key 被視為空集合。 #時間復雜度:O(N), N 為集合的基數。
localhost:6379> smembers animal 1) "panda"
2) "tiger"
3) "fish"
4) "lion" #判斷 member 元素是否集合 key 的成員。時間復雜度:O(1) #如果 member 元素是集合的成員,返回 1 。如果 member 元素不是集合的成員,或 key 不存在,返回 0 。
localhost:6379> sismember animal panda (integer) 1 #移除集合 key 中的一個或多個 member 元素,不存在的 member 元素會被忽略。 #時間復雜度:O(N), N 為給定 member 元素的數量。 #返回值:被成功移除的元素的數量,不包括被忽略的元素。當 key 不是集合類型,返回一個錯誤。
localhost:6379> srem animal panda (integer) 1 localhost:6379> sismember animal panda (integer) 0
測試HASH類型數據的增刪改查
演示Redis中Hash類型數據的的插入hset、集合查詢hgetall、根據Key查詢hget、移除hdel操作。
#將哈希表 key 中的域 field 的值設為 value 。如果 key 不存在,一個新的哈希表被創建並進行 HSET 操作。 #如果域 field 已經存在於哈希表中,舊值將被覆蓋。 #返回值:如果 field 是哈希表中的一個新建域,並且值設置成功,返回 1 。如果哈希表中域 field 已經存在且舊值已被新值覆蓋,返回 0 。時間復雜度:O(1)
localhost:6379> hset phones mi8 xiaomi (integer) 1 localhost:6379> hset phones v10 honor (integer) 1 localhost:6379> hset phones pro2 smartisan (integer) 1 localhost:6379> hset phones nex vivo (integer) 1 #返回哈希表 key 中,所有的域和值。在返回值里,緊跟每個域名(field name)之后是域的值(value),所以返回值的長度是哈希表大小的兩倍。 #時間復雜度:O(N), N 為哈希表的大小。
localhost:6379> hgetall phones 1) "mi8"
2) "xiaomi"
3) "v10"
4) "honor"
5) "pro2"
6) "smartisan"
7) "nex"
8) "vivo" #刪除哈希表 key 中的一個或多個指定域,不存在的域將被忽略。返回值:被成功移除的域的數量. #時間復雜度:O(N), N 為要刪除的域的數量。
localhost:6379> hdel phones pro2 (integer) 1 localhost:6379> hdel phones pro2 (integer) 0 #返回哈希表 key 中給定域 field 的值。時間復雜度:O(1);返回值:給定域的值,若不存在則返回nil。
localhost:6379> hget phones pro2 (nil) localhost:6379> hget phones mi8 "xiaomi" localhost:6379> hgetall phones 1) "mi8"
2) "xiaomi"
3) "v10"
4) "honor"
5) "nex"
6) "vivo"
測試Sorted set類型數據的增刪改查
演示Redis中Hash類型數據的的插入zadd、范圍查詢zrange、限定score的范圍查詢zrangebyscore、移除zrem操作。
#Sorted Set新增/刪除一個元素的復雜度為log(N),實現的數據結構感覺像是二叉樹 #將一個或多個 member 元素及其 score 值加入到有序集 key 當中。score 值可以是整數值或雙精度浮點數。 #如果某個 member 已經是有序集的成員,那么更新這個 member 的 score 值,並通過重新插入這個 member 元素,來保證該 member 在正確的位置上。 #時間復雜度:O(M*log(N)), N 是有序集的基數, M 為成功添加的新成員的數量。 #返回值:被成功添加的新成員的數量,不包括那些被更新的、已經存在的成員。
localhost:6379> zadd zphones 1598 vivoZ3 (integer) 1 localhost:6379> zadd zphones 1599 oppoK1 (integer) 1 localhost:6379> zadd zphones 2499 XiaoMi8 (integer) 1 localhost:6379> zadd zphones 1299 SmartisanPro2 (integer) 1 #返回有序集 key 中,指定區間內的成員。其中成員的位置按 score 值遞增(從小到大)來排序。具有相同 score 值的成員按字典序(lexicographical order )來排列。 #以 0 表示有序集第一個成員,以 1 表示有序集第二個成員,支持負數下標,-1 表示最后一個成員,-2 表示倒數第二個成員 #時間復雜度:O(log(N)+M), N 為有序集的基數,而 M 為結果集的基數。
localhost:6379> zrange zphone 0 -1 (empty list or set) localhost:6379> zrange zphones 0 -1
1) "SmartisanPro2"
2) "vivoZ3"
3) "oppoK1"
4) "XiaoMi8" localhost:6379> zrange zphones 0 -1 withscores 1) "SmartisanPro2"
2) "1299"
3) "vivoZ3"
4) "1598"
5) "oppoK1"
6) "1599"
7) "XiaoMi8"
8) "2499" #返回有序集 key 中,所有 score 值介於 min 和 max 之間(包括等於 min 或 max )的成員。有序集成員按 score 值遞增(從小到大)次序排列。 #可選的 LIMIT 參數指定返回結果的數量及區間,注意當 offset 很大時,定位 offset 的操作可能需要遍歷整個有序集,此過程最壞復雜度為 O(N) 時間。 #可選的 WITHSCORES 參數決定結果集是單單返回有序集的成員,還是將有序集成員及其 score 值一起返回。 #min 和 max 可以是-inf和+inf,可以在不知道有序集的最低和最高score值的情況下使用
localhost:6379> zrangebyscore zphones 999 1599 withscores 1) "SmartisanPro2"
2) "1299"
3) "vivoZ3"
4) "1598"
5) "oppoK1"
6) "1599" #移除有序集 key 中的一個或多個成員,不存在的成員將被忽略。當 key 存在但不是有序集類型時,返回一個錯誤。 #時間復雜度:O(M*log(N)), N 為有序集的基數, M 為被成功移除的成員的數量。 #返回值:被成功移除的成員的數量
localhost:6379> zrem zphones SmartisanPro2 (integer) 1 localhost:6379> zrange zphones 0 -1 withscores 1) "vivoZ3"
2) "1598"
3) "oppoK1"
4) "1599"
5) "XiaoMi8"
6) "2499"
搭建SpringBoot測試項目
創建SpringBoot項目
通過IDEA中的Spring Initializr創建SpringBoot項目。
Group和Artifact配置如下。
選擇SpringBoot的版本為1.5.x,並勾選Redis的依賴。
選則項目所在路徑。
項目創建完畢,可以刪除maven wrapper相關的文件和文件夾。
POM
在創建項目中已經勾選了Redis相關的依賴,不需要再在POM文件中添加其他依賴
<!-- 可以看到Spring Initailizer已經幫我們引入了redis的依賴 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yaml
spring: redis: host: 192.168.1.25 #redis服務器的IP地址
port: 6379 #redis監聽的端口號
Redis配置類
@Configuration public class RedisConfig { /** * 注入 RedisConnectionFactory */ @Autowired RedisConnectionFactory redisConnectionFactory; /** * 實例化 RedisTemplate 對象 * */ @Bean public RedisTemplate<String, Object> createRedisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initializeRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 設置數據存入 redis 的序列化方式 * */
private void initializeRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory redisConnectionFactory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); } /** * 實例化 HashOperations 對象,可以使用 Hash 類型操作 * */ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); } /** * 實例化 ValueOperations 對象,可以使用 String 操作 * */ @Bean public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForValue(); } /** * 實例化 ListOperations 對象,可以使用 List 操作 * */ @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList(); } /** * 實例化 SetOperations 對象,可以使用 Set 操作 * */ @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); } /** * 實例化 ZSetOperations 對象,可以使用 ZSet 操作 * */ @Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForZSet(); } }
Redis工具類
本示例主要進行Redis中Hash類型數據的操作。
@Component public class RedisHashUtil<T> { @Autowired protected RedisTemplate<String, Object> redisTemplate; @Resource protected HashOperations<String, String, T> hashOperations; private String getRedisKey() { return "REDIS_DEMO"; } /** * 添加 * * @param key key * @param value 對象 * @param expire 過期時間(單位:秒),傳入 -1 時表示不設置過期時間 */
public void put(String key, T value, long expire) { hashOperations.put(getRedisKey(), key, value); if (expire != -1) { redisTemplate.expire(getRedisKey(), expire, TimeUnit.SECONDS); } } /** * 刪除 * * @param key 傳入key的名稱 */
public void remove(String key) { hashOperations.delete(getRedisKey(), key); } /** * 查詢 * * @param key 查詢的key * @return
*/
public T get(String key) { return hashOperations.get(getRedisKey(), key); } /** * 獲取當前redis庫下所有value * * @return
*/
public List<T> getAll() { return hashOperations.values(getRedisKey()); } /** * 查詢查詢當前redis庫下所有key * * @return
*/
public Set<String> getKeys() { return hashOperations.keys(getRedisKey()); } /** * 判斷key是否存在redis中 * * @param key 傳入key的名稱 * @return
*/
public boolean isKeyExists(String key) { return hashOperations.hasKey(getRedisKey(), key); } /** * 查詢當前key下緩存數量 * * @return
*/
public long count() { return hashOperations.size(getRedisKey()); } /** * 清空redis */
public void clear() { Set<String> set = hashOperations.keys(getRedisKey()); set.stream().forEach(key -> hashOperations.delete(getRedisKey(), key)); } }
測試類
使用Redis中HASH類型數據結構存儲key-value類型的緩存。
@RunWith(SpringRunner.class) @SpringBootTest public class RedisdemoApplicationTests { @Autowired RedisHashUtil<String> redisHashUtil; @Test public void firstRedisTest() { System.out.println("***********************測試向Redis插入數據"); redisHashUtil.put("Mi8","XiaoMi",-1); redisHashUtil.put("V10","Honor",-1); redisHashUtil.put("Pro2","Smartisan",-1); redisHashUtil.put("NEX","VIVO",-1); System.out.println("**********************測試從Redis讀取數據,以及查詢數據總數"); long count = redisHashUtil.count(); List<String> all = redisHashUtil.getAll(); System.out.println("遍歷hash中的value,共計"+redisHashUtil.count()+"個品牌。分別為:"); for (String s:all) { System.out.print(s+" "); } System.out.println(""); System.out.println("***********************測試Redis中是否存在指定數據"); if(redisHashUtil.isKeyExists("Pro2")){ String pro2 = redisHashUtil.get("Pro2"); System.out.println("型號為Pro2的手機存在,其品牌為:"+pro2); } System.out.println("***********************測試從Redis中刪除數據"); redisHashUtil.remove("Pro2"); if(!redisHashUtil.isKeyExists("Pro2")){ System.out.println("型號為Pro2的手機被移除了"); } System.out.print("剩余型號還有:"); redisHashUtil.getKeys().forEach(key-> System.out.print(key+" ")); System.out.println(""); System.out.println("***********************測試清空Redis中的hash數據"); redisHashUtil.clear(); System.out.println("所有手機型號都被清空,剩余:"+redisHashUtil.count()+"個"); } }
測試結果
***********************測試向Redis插入數據 **********************測試從Redis讀取數據,以及查詢數據總數 遍歷hash中的value,共計4個品牌。分別為: XiaoMi Honor Smartisan VIVO ***********************測試Redis中是否存在指定數據 型號為Pro2的手機存在,其品牌為:Smartisan ***********************測試從Redis中刪除數據 型號為Pro2的手機被移除了 剩余型號還有:Mi8 V10 NEX ***********************測試清空Redis中的hash數據 所有手機型號都被清空,剩余:0個
看到這的老鐵都是真愛,源碼已托管到GitHub,歡迎關注。