Redis和nosql簡介,api呼叫;Redis資料功能(String型別的資料處理);List資料結構(及Java呼叫處理);Hash資料結構;Set資料結構功能;sortedSet(有序集合)數
1、Redis和nosql簡介,api呼叫
14.1/ nosql介紹
NoSQL:一類新出現的資料庫(not only sql),它的特點:
1、 不支援SQL語法
2、 儲存結構跟傳統關係型資料庫中的那種關係表完全不同,nosql中儲存的資料都是KV形式
3、 NoSQL的世界中沒有一種通用的語言,每種nosql資料庫都有自己的api和語法,以及擅長的業務場景
4、 NoSQL中的產品種類相當多:
a) Mongodb 文件型nosql資料庫,擅長做CMS系統(內容管理系統)
b) Redis 記憶體資料庫,資料結構伺服器,號稱瑞士軍刀(精巧),只要你有足夠的想象力,它可以還給你無限驚喜
c) Hbase hadoop生態系統中原生的一種nosql資料庫,重量級的分散式nosql資料庫,用於海量資料的場景
d) Cassandra hadoop生態系統中原生的一種分散式nosql資料庫,後起之秀
NoSQL和SQL資料庫的比較:
1、適用場景不同:sql資料庫適合用於關係特別複雜的資料查詢場景,nosql反之
2、“事務”特性的支援:sql對事務的支援非常完善,而nosql基本不支援事務
3、兩者在不斷地取長補短,呈現融合趨勢
14.2/ redis介紹
14.2.1 簡述
Redis是一個高效能的kv對快取和記憶體資料庫(存的不像mysql那樣的表)
Redis的儲存結構就是key-value,形式如下:
注: redis中的value內部可以支援各種資料結構型別,比如可以存入一個普通的string,還可以存list,set,hashmap,sortedSet(有序的set)
14.2.2 redis應用場景
A、用來做快取(ehcache/memcached)——redis的所有資料是放在記憶體中的(記憶體資料庫)
B、可以在某些特定應用場景下替代傳統資料庫——比如社交類的應用
C、在一些大型系統中,巧妙地實現一些特定的功能:session共享、購物車
只要你有豐富的想象力,redis可以用在可以給你無限的驚喜…….
14.2.3 redis的特性
1、redis資料訪問速度快(資料在記憶體中)
2、redis有資料持久化機制(持久化機制有兩種:1、定期將記憶體資料dump到磁碟;2、aof(append only file)持久化機制——用記日誌的方式記錄每一條資料
3、redis支援叢集模式(容量可以線性擴充套件)
4、redis相比其他快取工具(ehcach/memcached),有一個鮮明的優勢:支援豐富的資料結構
14.3.Redis的api客戶端連線
新建一個maven工程,匯入jedis的maven依賴座標
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.2</version> <type>jar</type> <scope>compile</scope> </dependency> |
然後寫一個類用來測試伺服器跟客戶端的連通性:
public class RedisClientConnectionTest { public static void main(String[] args) { // 構造一個redis的客戶端物件 Jedis jedis = new Jedis("pinshutang.zicp.net", 6379); String ping = jedis.ping(); System.out.println(ping); } } |
15、Redis資料功能(String型別的資料處理)
15.1 String型別的資料
(常作為快取使用)
1/插入和讀取一條string型別的資料
redis notrue-centos:6379> set sessionid-0001 "zhangsan" OK redis notrue-centos:6379> get sessionid-0001 "zhangsan" |
2/對string型別資料進行增減(前提是這條資料的value可以看成數字)
192.168.106.80:6379> DECR key (integer) -1 192.168.106.80:6379> DECR key (integer) -2 192.168.106.80:6379> DECR key (integer) -3 192.168.106.80:6379> INCR key (integer) -2 192.168.106.80:6379> INCR key (integer) -1 192.168.106.80:6379> DECRBY key 4 (integer) -5 192.168.106.80:6379> DECRBY key -7 (integer) 2 192.168.106.80:6379> INCRBY key 2 (integer) 4 192.168.106.80:6379> INCRBY key -9 (integer) -5 |
3/一次性插入或者獲取多條資料
192.168.106.80:6379> MSET key1 1 key2 2 key3 3 OK 192.168.106.80:6379> MGET key1 key2 key3 1) "1" 2) "2" 3) "3" |
4/在插入一條string型別資料的同時為它指定一個存活期限
setex key seconds value 例如: # bancao這條資料就只會存活10秒鐘,過期會被redis自動清除 192.168.106.80:6379> setex bancao 10 testvalue OK 192.168.106.80:6379> get bancao "testvalue" 192.168.106.80:6379> get bancao "testvalue" 192.168.106.80:6379> get bancao (nil) 192.168.106.80:6379> get bancao (nil) |
案例:
其中maven專案的pom檔案的內容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.toto.redis</groupId> <artifactId>redistest</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.2</version> <type>jar</type> <scope>compile</scope> </dependency> --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> </dependencies> </project> |
程式碼:
package cn.toto.redis.client; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.junit.Before; import org.junit.Test; import com.google.gson.Gson; import cn.toto.redis.bean.ProductInfo; import redis.clients.jedis.Jedis; public class StringStructureData { private Jedis jedis = null; @Before public void init() { //這裡的是hadoop2可以用ip地址替換 jedis = new Jedis("hadoop2",6379); } @Test public void testString() { jedis.set("user02:name", "ruhua"); jedis.set("user03:name", "滑板鞋"); String u02 = jedis.get("user02:name"); String u03 = jedis.get("user03:name"); System.out.println(u02); System.out.println(u03); } @Test public void testObjectCache() throws Exception { ProductInfo p = new ProductInfo(); p.setName("username"); p.setDescription("備註資訊"); p.setCatelog("unknow"); p.setPrice(10.8); //將物件序列化成位元組陣列 ByteArrayOutputStream ba = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(ba); //用物件序列化流來將p物件序列化,然後把序列化之後的二進位制資料寫到ba流中 oos.writeObject(p); //將ba流轉成byte陣列 byte[] pBytes = ba.toByteArray(); //將物件序列化之後的byte陣列存到redis的string結構陣列中 jedis.set("product:01".getBytes(), pBytes); //根據key從redis中取出物件的byte資料 byte[] pBytesResp = jedis.get("product:01".getBytes()); //將byte資料反序列化列出物件 ByteArrayInputStream bi = new ByteArrayInputStream(pBytesResp); ObjectInputStream oi = new ObjectInputStream(bi); //從物件讀取流中取出物件 ProductInfo pResp = (ProductInfo) oi.readObject(); System.out.println(pResp); } @Test public void testObjectToJsonCache() { ProductInfo p = new ProductInfo(); p.setName("ABC"); p.setDescription("劉亦菲專用"); p.setCatelog("化妝品"); p.setPrice(10.8); //利用gson將物件轉成json串 Gson gson = new Gson(); String pJson = gson.toJson(p); //將json串存入redis jedis.set("prodcut:02", pJson); //從redis中取出物件的json串 String pJsonResp = jedis.get("prodcut:02"); //將返回的json解析成物件 ProductInfo pResponse = gson.fromJson(pJsonResp, ProductInfo.class); //顯示物件的屬性 System.out.println(pResponse); } } |
16、List資料結構(及Java呼叫處理)
16.1 List圖示
16.2 List功能演示
#從頭部(左邊)插入資料 redis>LPUSH key value1 value2 value3 #從尾部(右邊)插入資料 redis>RPUSH key value1 value2 value3 #讀取list中指定範圍的values redis>LRANGE key start end redis> lrange task-queue 0 -1 讀取整個list #從頭部彈出一個元素 LPOP key #從尾部彈出一個元素 RPOP key #從一個list的尾部彈出一個元素插入到另一個list RPOPLPUSH key1 key2 ## 這是一個原子性操作 例如下面的案例: #從頭部(左邊)插入資料 192.168.106.81:6379> lpush task task0 task1 task2 (integer) 3 192.168.106.81:6379> lrange task 0 2 1) "task2" 2) "task1" 3) "task0" 192.168.106.81:6379> lrange task 0 -2 1) "task2" 2) "task1" 192.168.106.81:6379> lrange task 0 -1 1) "task2" 2) "task1" 3) "task0" #從尾部(右邊)插入資料 192.168.106.81:6379> rpush ids id0 id1 id2 (integer) 3 192.168.106.81:6379> lrange ids 0 -1 1) "id0" 2) "id1" 3) "id2" 192.168.106.81:6379> lpop ids "id0" 192.168.106.81:6379> lpop ids "id1" 192.168.106.81:6379> rpop task "task0" 192.168.106.81:6379> rpoplpush task ids "task1" 192.168.106.81:6379> lrange ids 0 -1 1) "task1" 2) "id2" 192.168.106.81:6379> |
16.3 List的應用案例demo
1 需求描述
任務排程系統:
生產者不斷產生任務,放入task-queue排隊
消費者不斷拿出任務來處理,同時放入一個tmp-queue暫存,如果任務處理成功,則清除tmp-queue,否則,將任務彈回task-queue
2 程式碼實現
1/生產者
——模擬產生任務
package cn.toto.redis.productAndCustomer; import java.util.Random; import java.util.UUID; import redis.clients.jedis.Jedis; public class TaskProducer { //獲取一個redis的客戶端連線物件 public static Jedis getRedisConnection(String host,int port) { Jedis jedis = new Jedis(host,port); return jedis; } public static void main(String[] args) { Jedis jedis = getRedisConnection("hadoop3", 6379); Random random = new Random(); //生成任務 while(true) { try { //生成任務的速度有一定的隨機性,在1-2秒之間 Thread.sleep(random.nextInt(1000) + 1000); //生成一個任務 String taskid = UUID.randomUUID().toString(); //往任務佇列"task-queue"中插入,第一次插入時,"task-queue"還不存在 //但是lpush方法會在redis庫中建立一條新的list資料 jedis.lpush("task-queue", taskid); System.out.println("向任務佇列中插入了一個新的任務:" + taskid); } catch (Exception e) { e.printStackTrace(); } } } } |
2/消費者
——模擬處理任務,並且管理暫存佇列
package cn.toto.redis.productAndCustomer; import java.util.Random; import redis.clients.jedis.Jedis; public class TaskConsumer { public static void main(String[] args) { Jedis jedis = new Jedis("hadoop3",6379); Random random = new Random(); while(true) { try { //從task-queue中取一個任務,同時放入tmp-queue String taskid = jedis.rpoplpush("task-queue", "tmp-queue"); //模擬處理任務 Thread.sleep(1000); //模擬有成功又有失敗的情況 int nextInt = random.nextInt(13); if (nextInt % 7 == 0) {//模擬失敗的情況 //在失敗的情況下,需要將任務從tmp-queue彈回"task-queue" jedis.rpoplpush("tmp-queue", "task-queue"); System.out.println("----任務處理失敗:" + taskid); } else {//模擬成功的情況 //成功的情況下,將任務從"tmp-queue"清除 jedis.rpop("tmp-queue"); System.out.println("任務處理成功:" + taskid); } } catch (Exception e) { e.printStackTrace(); } } } } |
上述機制是一個簡化版,真實版的任務排程系統會更加複雜,如下所示:
(增加了一個專門用來管理暫存佇列的角色,以便就算消費者程式失敗退出,那些處理失敗的任務依然可以被彈回task-queue)
17、Hash資料結構
17.1 Hash圖示
Redis中的Hashes型別可以看成具有String Key和String Value的map容器
17.2 Hash功能演示
1、往redis庫中插入一條hash型別的資料
redis> HSET key field value |
舉例:
redis 127.0.0.1:6379> hset user001:zhangsan iphone 6 (integer) 1 redis 127.0.0.1:6379> hset user001:zhangsan xiaomi 7 (integer) 1 redis 127.0.0.1:6379> hset user001:zhangsan meizu 8 (integer) 1 |
在redis庫中就形成了這樣一條資料:
2、從redis庫中獲取一條hash型別資料的value
ü 取出一條hash型別資料中所有field-value對
redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "iphone" 2) "6" 3) "xiaomi" 4) "7" 5) "meizu" 6) "8" |
ü 取出hash資料中所有fields
redis 127.0.0.1:6379> HKEYS user001:zhangsan 1) "iphone" 2) "xiaomi" 3) "meizu" |
ü 取出hash資料中所有的value
redis 127.0.0.1:6379> hvals user001:zhangsan 1) "6" 2) "7" 3) "8" |
ü 取出hash資料中一個指定field的值
redis 127.0.0.1:6379> hget user001:zhangsan xiaomi "8" |
ü 為hash資料中指定的一個field的值進行增減
redis 127.0.0.1:6379> HINCRBY user001:zhangsan xiaomi 1 (integer) 8 |
ü 從hash資料中刪除一個欄位field及其值
redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "iphone" 2) "6" 3) "xiaomi" 4) "7" 5) "meizu" 6) "8" redis 127.0.0.1:6379> HDEL user001:zhangsan iphone (integer) 1 redis 127.0.0.1:6379> hgetall user001:zhangsan 1) "xiaomi" 2) "7" 3) "meizu" 4) "8" |
18、Set資料結構功能
集合的特點:無序、無重複元素
1、 插入一條set資料
redis 127.0.0.1:6379> sadd frieds:zhangsan bingbing baby fengjie furong ruhua tingting (integer) 6 redis 127.0.0.1:6379> scard frieds:zhangsan (integer) 6 redis 127.0.0.1:6379> |
2、獲取一條set資料的所有members
redis 127.0.0.1:6379> smembers frieds:zhangsan 1) "fengjie" 2) "baby" 3) "furong" 4) "bingbing" 5) "tingting" 6) "ruhua" |
3、判斷一個成員是否屬於某條指定的set資料
redis 127.0.0.1:6379> sismember frieds:zhangsan liuyifei #如果不是,則返回0 (integer) 0 redis 127.0.0.1:6379> sismember frieds:zhangsan baby #如果是,則返回1 (integer) 1 |
4、求兩個set資料的差集
#求差集 redis 127.0.0.1:6379> sdiff frieds:zhangsan friends:xiaotao 1) "furong" 2) "fengjie" 3) "ruhua" 4) "feifei" #求差集,並將結果存入到另一個set redis 127.0.0.1:6379> sdiffstore zhangsan-xiaotao frieds:zhangsan friends:xiaotao (integer) 4 #檢視差集結果 redis 127.0.0.1:6379> smembers zhangsan-xiaotao 1) "furong" 2) "fengjie" 3) "ruhua" 4) "feifei" |
5、 求交集,求並集
#求交集 redis 127.0.0.1:6379> sinterstore zhangsan:xiaotao frieds:zhangsan friends:xiaotao (integer) 2 redis 127.0.0.1:6379> smembers zhangsan:xiaotao 1) "bingbing" 2) "baby" #求並集 redis 127.0.0.1:6379> sunion frieds:zhangsan friends:xiaotao 1) "fengjie" 2) "tangwei" 3) "liuyifei" 4) "bingbing" 5) "ruhua" 6) "feifei" 7) "baby" 8) "songhuiqiao" 9) "furong" 10) "yangmi" |
案例程式碼:
package cn.toto.redis.hashcart; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.junit.Before; import org.junit.Test; import redis.clients.jedis.Jedis; public class BuyCartServiceImpl { private Jedis jedis = null; private static final String CART_PRIFIX = "cart:"; @Before public void init() { jedis = new Jedis("hadoop3",6379); } /** * 新增商品到購物車 */ @Test public void testAddItemToCart() { jedis.hset(CART_PRIFIX + "user02", "上衣", "2"); jedis.hset(CART_PRIFIX + "user02", "鞋子", "1"); jedis.hset(CART_PRIFIX + "user02", "毛衣", "3"); jedis.hset(CART_PRIFIX + "user02", "羽絨服", "4"); jedis.close(); } /** * 檢視購物車資訊,當本類中的其它單元測試執行完成之後,可以通過執行這個單元測試方法檢視內容 * @author toto * @date 2017-1-14 * @note begin modify by 塗作權 2017-1-14 原始建立 */ @Test public void testGetCartInfo() { Map<String, String> cart = jedis.hgetAll(CART_PRIFIX + "user02"); Set<Entry<String, String>> entrySet = cart.entrySet(); for(Entry<String, String> ent : entrySet) { System.out.println(ent.getKey() + ":" + ent.getValue()); } jedis.close(); } /** * 編輯快取中的內容 */ @Test public void editCart() { //給蠟燭商品項的數量加1 jedis.hincrBy(CART_PRIFIX + "user02", "羽絨服", 1); jedis.close(); } /** * 刪除記憶體中上衣這個內容 */ @Test public void delItemFromCart() { jedis.hdel(CART_PRIFIX + "user02", "上衣"); jedis.close(); } } |
生產者消費者:
生產者: package cn.toto.redis.listqueue; import java.util.Random; import java.util.UUID; import redis.clients.jedis.Jedis; public class TaskProducer { private static Jedis jedis = null; public static void main(String[] args) throws Exception { jedis = new Jedis("hadoop2",6379); Random random = new Random(); while(true) { int nextInt = random.nextInt(1000); Thread.sleep(1000 + nextInt); //生成一個任務的id String taskid = UUID.randomUUID().toString(); jedis.lpush("task-queue", taskid); System.out.println("生成了一個任務:" + taskid); } } } |
消費者: package cn.toto.redis.listqueue; import java.util.Random; import redis.clients.jedis.Jedis; /** * @brief TaskProcessor.java 任務處理模組 */ public class TaskProcessor { private static Jedis jedis = null; public static void main(String[] args) throws Exception { Random random = new Random(); jedis = new Jedis("hadoop2",6379); while(true) { Thread.sleep(1500); //從任務佇列中取出一個任務,同時放入到暫存佇列中 String taskid = jedis.rpoplpush("task-queue", "tmp-queue"); //處理任務 if (random.nextInt(19) % 9 == 0) { //模擬失敗 //失敗的情況下,需要將任務從暫存佇列彈回任務佇列 jedis.rpoplpush("tmp-queue", "task-queue"); System.out.println("該任務處理失敗:" + taskid); } else { //模擬成功 //成功的情況下,只需要將任務從暫存佇列清除 jedis.rpop("tmp-queue"); System.out.println("任務處理成功:" + taskid); } } } } |
Set的案例:
package cn.toto.redis.set; import java.util.Set; import redis.clients.jedis.Jedis; public class TestSet { public static void main(String[] args) { Jedis jedis = new Jedis("hadoop2",6379); jedis.sadd("friends:shuangshuang", "dandan","lulu","lili"); jedis.sadd("friends:laobi", "laowang","laodu","laoli","lili","lulu"); //判斷一個成員是否屬於指定的集合 Boolean isornot = jedis.sismember("friends:laobi", "shuangshuang"); System.out.println(isornot); //求兩個集合的差並交集 Set<String> ssDiffbb = jedis.sdiff("friends:shuangshuang","friends:laobi"); Set<String> ssUnionbb = jedis.sunion("friends:shuangshuang","friends:laobi"); Set<String> ssInterbb = jedis.sinter("friends:shuangshuang","friends:laobi"); //列印結果 for (String mb : ssUnionbb) { System.out.println(mb); } } } |
19、sortedSet(有序集合)資料結構
19.1 sortedSet圖示
sortedset中儲存的成員都有一個附帶的分數值
而redis就可以根據分數來對成員進行各種排序(正序、倒序)
1、 sortedSet儲存內容示意圖:
19.2 SortedSet功能演示
1、往redis庫中插入一條sortedset資料
redis 127.0.0.1:6379> zadd nansheng:yanzhi:bang 70 liudehua 90 huangbo 100 weixiaobao 250 yangwei 59 xiaotao (integer) 5 |
2、 從sortedset中查詢有序結果
#正序結果 redis 127.0.0.1:6379> zrange nansheng:yanzhi:bang 0 4 1) "xiaotao" 2) "liudehua" 3) "huangbo" 4) "weixiaobao" 5) "yangwei" #倒序結果 redis 127.0.0.1:6379> zrevrange nanshen:yanzhi:bang 0 4 1) "yangwei" 2) "weixiaobao" 3) "huangbo" 4) "liudehua" 5) "xiaotao" |
3、 查詢某個成員的名次
#在正序榜中的名次 redis 127.0.0.1:6379> zrank nansheng:yanzhi:bang xiaotao (integer) 0 #在倒序榜中的名次 redis 127.0.0.1:6379> zrevrank nansheng:yanzhi:bang xiaotao (integer) 4 |
4、修改成員的分數
redis 127.0.0.1:6379> zincrby nansheng:yanzhi:bang 300 xiaotao "359" redis 127.0.0.1:6379> zrevrank nansheng:yanzhi:bang xiaotao (integer) 0 |
案例:
下面的這個先執行: package cn.toto.redis.sortedset; import java.util.Random; import redis.clients.jedis.Jedis; public class LolBoxPlayer { public static void main(String[] args) throws Exception { Jedis jedis = new Jedis("hadoop2",6379); Random random = new Random(); String[] heros = {"易大師","德邦","劍姬","蓋倫","阿卡麗 |