javaWeb學習之redis和jedis
1.Redis的概述
概述:Redis(Remote Dictionary Server ),即遠端字典服務,是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API,它和和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別),而且它是NoSQL技術陣營的一員。
Redis的特性
1.Redis與其他key-value快取產品有以下三個特點
- Redis支援資料的持久化,可以將記憶體中的資料儲存到磁碟中,重啟的時候可以再次載入進行使用
- Redis不僅僅支援簡單的key-value型別的資料,同時還提供了list,set,zset,hash等資料結構的儲存
- Redis支援資料的備份,即master-slave模式的資料備份
2.Redis優勢
- 效能極高:Redis能讀的速度是11000次/s,寫的速度是81000次/s
- 豐富的資料型別:Redis支援二進位制案例的String,Lists,Hashes,Sets及Ordered Sets資料型別操作
- 原子性:Redis的所有操作都是原子性的,同時Redis還支援對幾個操作合併後的原子性執行
- 豐富的特性:Redis還支援publish/subscribe,通知,key過期等等特性
2.Redis的下載和安裝
Redis版本說明:
Redis原生只支援linux系統,Redis官方是不支援windows平臺的,windows版本是微軟自己建立的分支,基於官方的redis原始碼進行編譯、釋出、維護的,所以window平臺上的redis版本都略低於官方版本
Reids下載
複製下載地址,然後到linux執行下載命令:
wget http://download.redis.io/releases/redis-6.0.6.tar.gz即可在linux下載
Reids安裝
- 將redis-6.0.6.tar.gz進行解壓
- tar-zxvf redis-6.0.6.tar.gz
- 進入redis-6.0.6目錄
- cd redis-6.0.6
- 對redis進行編譯
- make
- 安裝到指定的目錄
- 編譯後進入src目錄,執行命令make PREFIX=/usr/local/redis install(沒有這個目錄的話就先mkdir這個目錄)
- 安裝完成後,進入這個redis目錄的bin目錄,可以看到
- redis-server:redis伺服器
- redis-cli redis:命令列客戶端
- redis-benchmark redis: 效能測試工具
- redis-check-afo: AFO檔案修復工具
- reids-check-rdb: RDB檔案檢索工具
redis的客戶端測試
1.啟動redis服務
我們需要將redis編譯後的目錄中的redis.conf檔案copy到我們自己的redis目錄中,這個redis.conf檔案是redis的配置檔案
複製redis配置檔案的命令:cp redis.conf /usr/local/redis
複製好之後在我們的redis資料夾下執行:./bin/redis-server ./redis.conf 即可啟動伺服器(載入配置檔案的啟動)
2.客戶端連線伺服器
執行/usr/local/redis/bin/redis-cli進行連線本地的redis服務
3.連線成功後輸入ping,返回Pong則已經連線成功了
3.Redis的使用
菜鳥教程:https://www.runoob.com/redis/redis-tutorial.html
4.redis的資料持久化介紹
概述:redis將記憶體儲存和持久化儲存相結合,即可提供資料訪問的高效性,又可以保證資料儲存的安全性
redis資料持久化機制介紹
1.RDB持久化:該機制是指在指定的時間間隔內將記憶體中的資料集快照寫入磁碟(redis的預設持久化方式)
2.AOF(append only file)持久化:該機制將以日誌的形式記錄伺服器所處理的每一個寫操作,在redis伺服器啟動之初會讀取該檔案來重新構建資料庫,以保證啟動後資料庫中的資料是完整的
3.同時應用AOF和RDB
4.無持久化:可通過配置的方式禁用redis伺服器的持久化功能,這樣我們就可以將redis視為一個功能加強版的memacached了(不推薦這麼做)
redis資料持久化配置
1.RDB快照
預設情況下,redis會將資料集的快照dump到dump.rdb檔案中,此外,我們也可以通過配置檔案來修改redis伺服器dump快照的頻率,在開啟redis.conf檔案後,搜尋找到save,可以看到以下配置資訊:
- save 900 1:在900秒後,如果至少有一個key發生變化,則dump記憶體快照
- save 300 10:在300秒後,如果至少有10個key發生變化,則dump記憶體快照
- save 60 10000:在60秒之後,如果至少有10000個key發生變化,則dump記憶體快照
關於dump.rbd檔案儲存的位置,它的設定在redis.conf檔案中的“dir ./”, 這段配置指的是伺服器啟動時的當前路徑,當前路徑在哪裡,這個dump.rdb檔案就在哪裡
2.AOF日誌檔案方式(持久化到appendonly.afo檔案):
AOF日誌持久化機制的開啟:在redis.conf檔案中將appendonly no改為appendonly yes
AOF同步方式的配置,在redis.conf檔案中有三種同步方式:
- appendfsync always:每次有資料修改發生時就寫入AOF檔案
- appendfsync everysec:每秒鐘同步一次,該策略為AOF的預設策略
- appendfsync no:從不同步,高效但是資料不會被持久化
RDB與AOF的對比總結(抄摘自https://blog.csdn.net/m0_38110132/article/details/76906422)
RDB存在哪些優勢呢?
1). 一旦採用該方式,那麼你的整個Redis資料庫將只包含一個檔案,這對於檔案備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近24小時的資料,同時還要每天歸檔一次最近30天的資料。通過這樣的備份策略,一旦系統出現災難性故障,我們可以非常容易的進行恢復。
2). 對於災難恢復而言,RDB是非常不錯的選擇。因為我們可以非常輕鬆的將一個單獨的檔案壓縮後再轉移到其它儲存介質上。
3). 效能最大化。對於Redis的服務程序而言,在開始持久化時,它唯一需要做的只是fork出子程序,之後再由子程序完成這些持久化的工作,這樣就可以極大的避免服務程序執行IO操作了。
4). 相比於AOF機制,如果資料集很大,RDB的啟動效率會更高。
RDB又存在哪些劣勢呢?
1). 如果你想保證資料的高可用性,即最大限度的避免資料丟失,那麼RDB將不是一個很好的選擇。因為系統一旦在定時持久化之前出現宕機現象,此前沒有來得及寫入磁碟的資料都將丟失。
2). 由於RDB是通過fork子程序來協助完成資料持久化工作的,因此,如果當資料集較大時,可能會導致整個伺服器停止服務幾百毫秒,甚至是1秒鐘。
AOF的優勢有哪些呢?
1). 該機制可以帶來更高的資料安全性,即資料永續性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是非同步完成的,其效率也是非常高的,所差的是一旦系統出現宕機現象,那麼這一秒鐘之內修改的資料將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發生的資料變化都會被立即記錄到磁碟中。可以預見,這種方式在效率上是最低的。至於無同步,無需多言,我想大家都能正確的理解它。
2). 由於該機制對日誌檔案的寫入操作採用的是append模式,因此在寫入過程中即使出現宕機現象,也不會破壞日誌檔案中已經存在的內容。然而如果我們本次操作只是寫入了一半資料就出現了系統崩潰問題,不用擔心,在Redis下一次啟動之前,我們可以通過redis-check-aof工具來幫助我們解決資料一致性的問題。
3). 如果日誌過大,Redis可以自動啟用rewrite機制。即Redis以append模式不斷的將修改資料寫入到老的磁碟檔案中,同時Redis還會建立一個新的檔案用於記錄此期間有哪些修改命令被執行。因此在進行rewrite切換時可以更好的保證資料安全性。
4). AOF包含一個格式清晰、易於理解的日誌檔案用於記錄所有的修改操作。事實上,我們也可以通過該檔案完成資料的重建。
AOF的劣勢有哪些呢?
1). 對於相同數量的資料集而言,AOF檔案通常要大於RDB檔案。RDB 在恢復大資料集時的速度比 AOF 的恢復速度要快。
2). 根據同步策略的不同,AOF在執行效率上往往會慢於RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB一樣高效。
二者選擇的標準,就是看系統是願意犧牲一些效能,換取更高的快取一致性(aof),還是願意寫操作頻繁的時候,不啟用備份來換取更高的效能,待手動執行save的時候,再做備份(rdb)。rdb這個就更有些 eventually consistent的意思了。不過生產環境其實更多都是二者結合使用的。
5.Jedis的介紹與入門
概述:想要通過java來操作redis就需要使用jedis
jedis的使用步驟:
1.導包
- jedis.jar包
2.入門程式碼
import redis.clients.jedis.Jedis;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
jedis.set("foo", "bar");
String value = jedis.get("foo");
System.out.println(value);
}
}
//輸出bar即代表redis連線成功
注意:預設開啟的redis服務端是不支援外部網路連線的,而且也沒有設定密碼,如果想要外部網路連線上這臺伺服器的redis服務,則需要先配置redis的redis.conf檔案
- 配置允許外網訪問:在redis.conf檔案中找到bind 127.0.0.1,把bind 127.0.0.1註釋掉
- 配置密碼:在redis.conf檔案中找到requirepass foobared,把他修改為requirepass admin(自己要設定的密碼)
使用vi編輯器編輯redis.conf檔案時可以使用:/你要查詢的內容進行查詢,如(:/bind 127.0.0.1)
jedis操作String型別
程式碼示例:
import java.util.List;
import redis.clients.jedis.Jedis;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
/* //set,get命令
jedis.set("foo", "bar");
String value = jedis.get("foo");
System.out.println(value);
//mset,mget命令
jedis.mset("username", "luyi", "age", "20");
List<String> list = jedis.mget("username", "age");
System.out.println(list);*/
//append setrange getrange
jedis.append("username", " is a boy");
System.out.println(jedis.get("username"));
jedis.setrange("username", 10, "gril");
System.out.println(jedis.get("username"));
System.out.println(jedis.getrange("username", 10, -1));
}
}
jedis操作list型別
程式碼示例:
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ListPosition;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
//lpush lrange
jedis.lpush("names", "luyi", "luer", "lusan");
List<String> list = jedis.lrange("names", 0, -1);
System.out.println(list);
//lset lindex
jedis.lset("names", 1, "huanger");
String value = jedis.lindex("names", 1);
System.out.println(value);
//linsert
jedis.linsert("names", ListPosition.BEFORE, "lusan", "huangsan");
List<String> list2 = jedis.lrange("names", 0, -1);
System.out.println(list2);
}
}
jedis操作hash型別
程式碼示例:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
/* //hset hget
jedis.hset("user", "username", "luyi");
String value = jedis.hget("user", "username");
System.out.println(value);*/
//hmset hmget
Map<String, String> hash = new HashMap<String, String>();
hash.put("username", "luyi");
hash.put("age", "19");
hash.put("sex", "male");
jedis.hmset("user", hash);
List<String> values = jedis.hmget("user", "username", "age", "sex");
System.out.println(values);
//hgetall hkeys hvals
Map<String, String> map = jedis.hgetAll("user");
for(String key:map.keySet()){
System.out.println(key + "---" + map.get(key));
}
Set<String> keys = jedis.hkeys("user");
System.out.println(keys);
List<String> list = jedis.hvals("user");
System.out.println(list);
}
}
jedis操作set型別
程式碼示例:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
/* //sadd smembers
jedis.sadd("language1", "c++", "c", "c#", "Java");
Set<String> set = jedis.smembers("language1");
System.out.println(set);*/
/* //srem
jedis.srem("language1","c++");
Set<String> set = jedis.smembers("language1");
System.out.println(set);*/
/* //差集sdiff
jedis.sadd("language1", "c++", "c", "c#", "Java");
jedis.sadd("language2", "c++", "python", "Ruby", "Java");
Set<String> sdiff = jedis.sdiff("language1", "language2");
System.out.println(sdiff);*/
/* //交集sdiff
jedis.sadd("language1", "c++", "c", "c#", "Java");
jedis.sadd("language2", "c++", "python", "Ruby", "Java");
Set<String> sinter = jedis.sinter("language1", "language2");
System.out.println(sinter);*/
//並集sunion
jedis.sadd("language1", "c++", "c", "c#", "Java");
jedis.sadd("language2", "c++", "python", "Ruby", "Java");
Set<String> sunion = jedis.sunion("language1", "language2");
System.out.println(sunion);
}
}
jedis操作sortedset型別
程式碼示例:
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
/* //zadd zrange zrangeByScore
Map<String, Double> map = new HashMap<String, Double>();
map.put("張三", 90.0);
map.put("李四", 80.0);
map.put("王五", 70.0);
map.put("趙六", 60.0);
jedis.zadd("zkey", map);
Set<String> set = jedis.zrange("zkey", 0, -1);
System.out.println(set);
Set<String> set2 = jedis.zrangeByScore("zkey", 70.0, 90.0);
System.out.println(set);
Set<Tuple> set3 = jedis.zrangeWithScores("zkey", 0, -1);
for(Tuple t : set3){
System.out.println(t.getScore() + "---" + t.getElement());
}*/
//zscore zrem
Map<String, Double> map2 = new HashMap<String, Double>();
map2.put("張三", 90.0);
map2.put("李四", 80.0);
map2.put("王五", 70.0);
map2.put("趙六", 60.0);
jedis.zadd("zkey", map2);
Double zcore = jedis.zscore("zkey", "張三");
System.out.println(zcore);
jedis.zrem("zkey", "趙六");
Set<String> set4 = jedis.zrange("zkey", 0, -1);
System.out.println(set4);
}
}
jedis中完成key的通用操作
程式碼示例:
package com.luyi.jedis;
import redis.clients.jedis.Jedis;
public class MyFirstJedis {
public static void main(String[] args) {
//新建一個jedis,連線上的地址是192.168.75.142
Jedis jedis = new Jedis("192.168.75.142");
//設定連線的密碼
jedis.auth("admin");
/* //keys
Set<String> keys = jedis.keys("*");
System.out.println(keys);
//del
jedis.del("user");
Set<String> keys2 = jedis.keys("*");
System.out.println(keys2);*/
//關於key時間設定
jedis.expire("zkey", 100);//設定生命週期為200秒
long t = jedis.ttl("zkey");//獲取生命週期值
System.out.println(t);
}
}