1. 程式人生 > >Redis和nosql簡介,api呼叫;Redis資料功能(String型別的資料處理);List資料結構(及Java呼叫處理);Hash資料結構;Set資料結構功能;sortedSet(有序集合)數

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,還可以存listsethashmapsortedSet(有序的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陣列存到redisstring結構陣列中

       jedis.set("product:01".getBytes(), pBytes);

       //根據keyredis中取出物件的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 = {"易大師","德邦","劍姬","蓋倫","阿卡麗