redis JAVA客戶端(Jedis)測試使用

一、jedis連接(maven項目)

package redis;

import net.sf.json.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.*;

/**
 * Created by Administrator on 2016/6/14.
 */

public class RedisClient {

    public static JedisPool jedisPool; // 池化管理jedis鏈接池

    static {

        //讀取相關的配置
        ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
        int maxActive = Integer.parseInt(resourceBundle.getString("redis.pool.maxActive"));
        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(maxActive);
        //設置最大空閒數
        config.setMaxIdle(maxIdle);
        //設置超時時間
        config.setMaxWaitMillis(maxWait);
        //初始化連接池
        jedisPool = new JedisPool(config, ip, port);
    }

    /**
     * 向緩存中設置字符串內容
     *
     * @param key   key
     * @param value value
     * @return
     * @throws Exception
     */
    public static boolean set(String key, String value) throws Exception {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            jedisPool.returnResource(jedis);
        }
    }

    /**
     * 向緩存中設置對象
     *
     * @param key
     * @param value
     * @return
     */
    public static boolean set(String key, Object value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.set(key.getBytes(), SerializeUtil.serialize(value));
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            jedisPool.returnResource(jedis);
        }
    }

    /**
     * 刪除緩存中得對象,根據key
     *
     * @param key
     * @return
     */
    public static boolean del(String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(key);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            jedisPool.returnResource(jedis);
        }
    }

    /**
     * 根據key 獲取內容
     *
     * @param key
     * @return
     */
    public static Object get(String key) {

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            byte[] value = jedis.get(key.getBytes());
            return SerializeUtil.unserialize(value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            jedisPool.returnResource(jedis);
        }
    }

    public static void main(String[] args) throws Exception {
//        //測試存儲字符串
//        RedisClient.set("hello", "world");
//        System.out.println(RedisClient.get("hello"));
//        //測試存儲對象
//        People people = new People();// 存放對象時 必須實現序列化
//        people.setName("wamhf");
//        people.setAge(26);
//        RedisClient.set("name1", people);
//        People p = (People) RedisClient.get("name1");
//        System.out.println(p.getAge() + "-------" + p.getName());
//        //測試存儲List
//        List list = new ArrayList();
//        list.add("111");
//        list.add("222");
//        list.add("333");
//        RedisClient.set("list", list);
//        List list1 = (List) RedisClient.get("list");
//        for (Object o : list1) {
//            System.out.println(o.toString());
//        }
//        //測試存儲Set
//        Set set = new HashSet<>();
//        set.add("a");
//        set.add(1);
//        RedisClient.set("set", set);
//        Set s = (Set) RedisClient.get("set");
//        for (Object o : s) {
//            System.out.println(o.toString());
//        }
//        System.out.println("---------------------驗證set去重---------------------");
//        Set set1 = new HashSet<>();
//        set1.add("a");
//        set1.add(1);
//        RedisClient.set("set", set1);
//        Set s1 = (Set) RedisClient.get("set");
//        for (Object o : s1) {
//            System.out.println(o.toString());
//        }

        RedisClient redisClient = new RedisClient();
        redisClient.test();
    }

    public void test() {
        Jedis redis = jedisPool.getResource();
        /* -----------------------------------------------------------------------------------------------------------  */
        // KEY操作

        //KEYS
        Set keys = redis.keys("*");//列出所有的key,查找特定的key如:redis.keys("foo")
        Iterator t1 = keys.iterator();
        while (t1.hasNext()) {
            Object obj1 = t1.next();
            System.out.println(obj1);
        }

        //DEL 移除給定的一個或多個key。如果key不存在,則忽略該命令。
//        redis.del("set");

        //TTL 返回給定key的剩餘生存時間(time to live)(以秒爲單位)
        System.out.println(redis.ttl("hello"));

        //PERSIST key 移除給定key的生存時間。
        redis.persist("foo");

        //EXISTS 檢查給定key是否存在。
        redis.exists("foo");

        //MOVE key db  將當前數據庫(默認爲0)的key移動到給定的數據庫db當中。如果當前數據庫(源數據庫)和給定數據庫(目標數據庫)有相同名字的給定key,或者key不存在於當前數據庫,那麼MOVE沒有任何效果。
        redis.move("foo", 1);//將foo這個key,移動到數據庫1

        //RENAME key newkey  將key改名爲newkey。當key和newkey相同或者key不存在時,返回一個錯誤。當newkey已經存在時,RENAME命令將覆蓋舊值。
        redis.rename("foo", "foonew");

        //TYPE key 返回key所儲存的值的類型。
        System.out.println(redis.type("foo"));//none(key不存在),string(字符串),list(列表),set(集合),zset(有序集),hash(哈希表)

        //EXPIRE key seconds 爲給定key設置生存時間。當key過期時,它會被自動刪除。
        redis.expire("foo", 5);//5秒過期
        //EXPIREAT EXPIREAT的作用和EXPIRE一樣,都用於爲key設置生存時間。不同在於EXPIREAT命令接受的時間參數是UNIX時間戳(unix timestamp)。

        //一般SORT用法 最簡單的SORT使用方法是SORT key。
        redis.lpush("sort", "1");
        redis.lpush("sort", "4");
        redis.lpush("sort", "6");
        redis.lpush("sort", "3");
        redis.lpush("sort", "0");

        List list = redis.sort("sort");//默認是升序
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }


      /* -----------------------------------------------------------------------------------------------------------  */
        // STRING 操作

        //SET key value將字符串值value關聯到key。
        redis.set("name", "wangjun1");
        redis.set("id", "123456");
        redis.set("address", "guangzhou");

        //SETEX key seconds value將值value關聯到key,並將key的生存時間設爲seconds(以秒爲單位)。
        redis.setex("foo", 5, "haha");

        //MSET key value [key value ...]同時設置一個或多個key-value對。
        redis.mset("haha", "111", "xixi", "222");

        //redis.flushAll();清空所有的key
        System.out.println(redis.dbSize());//dbSize是多少個key的個數

        //APPEND key value如果key已經存在並且是一個字符串,APPEND命令將value追加到key原來的值之後。
        redis.append("foo", "00");//如果key已經存在並且是一個字符串,APPEND命令將value追加到key原來的值之後。

        //GET key 返回key所關聯的字符串值
        redis.get("foo");

        //MGET key [key ...] 返回所有(一個或多個)給定key的值
        List list1 = redis.mget("haha", "xixi");
        for (int i = 0; i < list1.size(); i++) {
            System.out.println(list1.get(i));
        }

        //DECR key將key中儲存的數字值減一。
        //DECRBY key decrement將key所儲存的值減去減量decrement。
        //INCR key 將key中儲存的數字值增一。
        //INCRBY key increment 將key所儲存的值加上增量increment。


        /* -----------------------------------------------------------------------------------------------------------  */
        // Hash 操作

        //HSET key field value將哈希表key中的域field的值設爲value。
        redis.hset("website", "google", "www.google.cn");
        redis.hset("website", "baidu", "www.baidu.com");
        redis.hset("website", "sina", "www.sina.com");

        //HMSET key field value [field value ...] 同時將多個field - value(域-值)對設置到哈希表key中。
        Map map = new HashMap();
        map.put("cardid", "123456");
        map.put("username", "jzkangta");
        redis.hmset("hash", map);

        //HGET key field返回哈希表key中給定域field的值。
        System.out.println(redis.hget("hash", "username"));

        //HMGET key field [field ...]返回哈希表key中,一個或多個給定域的值。
        List list2 = redis.hmget("website", "google", "baidu", "sina");
        for (int i = 0; i < list2.size(); i++) {
            System.out.println(list2.get(i));
        }

        //HGETALL key返回哈希表key中,所有的域和值。
        Map<String, String> map1 = redis.hgetAll("hash");
        for (Map.Entry entry : map1.entrySet()) {
            System.out.print(entry.getKey() + ":" + entry.getValue() + "\t");
        }

        //HDEL key field [field ...]刪除哈希表key中的一個或多個指定域。
        //HLEN key 返回哈希表key中域的數量。
        //HEXISTS key field查看哈希表key中,給定域field是否存在。
        //HINCRBY key field increment爲哈希表key中的域field的值加上增量increment。
        //HKEYS key返回哈希表key中的所有域。
        //HVALS key返回哈希表key中的所有值。


        /* -----------------------------------------------------------------------------------------------------------  */
        //  LIST 操作
        //LPUSH key value [value ...]將值value插入到列表key的表頭。
        redis.lpush("list", "abc");
        redis.lpush("list", "xzc");
        redis.lpush("list", "erf");
        redis.lpush("list", "bnh");

        //LRANGE key start stop返回列表key中指定區間內的元素,區間以偏移量start和stop指定。下標(index)參數start和stop都以0爲底,也就是說,以0表示列表的第一個元素,以1表示列表的第二個元素,以此類推。你也可以使用負數下標,以-1表示列表的最後一個元素,-2表示列表的倒數第二個元素,以此類推。
        List list3 = redis.lrange("list", 0, -1);
        for (int i = 0; i < list3.size(); i++) {
            System.out.println(list3.get(i));
        }

        //LLEN key返回列表key的長度。
        //LREM key count value根據參數count的值,移除列表中與參數value相等的元素。

        /* -----------------------------------------------------------------------------------------------------------  */
        //  SET 操作
        //SADD key member [member ...]將member元素加入到集合key當中。
        redis.sadd("testSet", "s1");
        redis.sadd("testSet", "s2");
        redis.sadd("testSet", "s3");
        redis.sadd("testSet", "s4");
        redis.sadd("testSet", "s5");

        //SREM key member移除集合中的member元素。
        redis.srem("testSet", "s5");

        //SMEMBERS key返回集合key中的所有成員。
        Set set = redis.smembers("testSet");
        Iterator t2 = set.iterator();
        while (t2.hasNext()) {
            Object obj1 = t2.next();
            System.out.println(obj1);
        }

        //SISMEMBER key member判斷member元素是否是集合key的成員。是(true),否則(false)
        System.out.println(redis.sismember("testSet", "s4"));

        //SCARD key返回集合key的基數(集合中元素的數量)。
        //SMOVE source destination member將member元素從source集合移動到destination集合。

        //SINTER key [key ...]返回一個集合的全部成員,該集合是所有給定集合的交集。
        //SINTERSTORE destination key [key ...]此命令等同於SINTER,但它將結果保存到destination集合,而不是簡單地返回結果集
        //SUNION key [key ...]返回一個集合的全部成員,該集合是所有給定集合的並集。
        //SUNIONSTORE destination key [key ...]此命令等同於SUNION,但它將結果保存到destination集合,而不是簡單地返回結果集。
        //SDIFF key [key ...]返回一個集合的全部成員,該集合是所有給定集合的差集 。
        //SDIFFSTORE destination key [key ...]此命令等同於SDIFF,但它將結果保存到destination集合,而不是簡單地返回結果集。


    }

}
package redis;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * Created by Administrator on 2016/6/14.
 */
public class SerializeUtil {
    public static byte[] serialize(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            // 序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {

        }
        return null;
    }

    public static Object unserialize(byte[] bytes) {
        ByteArrayInputStream bais = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {

        }
        return null;
    }
}
package redis;

import java.io.Serializable;

/**
 * Created by Administrator on 2016/6/14.
 */
public class People implements Serializable {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

三.redis 五種數據類型的使用場景

String

1、String
常用命令:
除了get、set、incr、decr mget等操作外,Redis還提供了下面一些操作:
獲取字符串長度
往字符串append內容
設置和獲取字符串的某一段內容
設置及獲取字符串的某一位(bit)
批量設置一系列字符串的內容

應用場景:
String是最常用的一種數據類型,普通的key/value存儲都可以歸爲此類,value其實不僅是String,
也可以是數字:比如想知道什麼時候封鎖一個IP地址(訪問超過幾次)。INCRBY命令讓這些變得很容易,通過原子遞增保持計數。

實現方式:
m,decr等操作時會轉成數值型進行計算,此時redisObject的encoding字段爲int。

Hash

常用命令:
hget,hset,hgetall 等。
應用場景:
我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶信息對象數據,包含以下信息:
用戶ID,爲查找的key,
存儲的value用戶對象包含姓名name,年齡age,生日birthday 等信息,
如果用普通的key/value結構來存儲,主要有以下2種存儲方式:
第一種方式將用戶ID作爲查找key,把其他信息封裝成一個對象以序列化的方式存儲,
如:set u001 “李三,18,20010101”
這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項信息時,需要把整個對象取回,並且修改操作需要對併發進行保護,引入CAS等複雜問題。
第二種方法是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱作爲唯一標識來取得對應屬性的值,
如:mset user:001:name “李三 “user:001:age18 user:001:birthday “20010101”
雖然省去了序列化開銷和併發問題,但是用戶ID爲重複存儲,如果存在大量這樣的數據,內存浪費還是非常可觀的。
那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value爲一個HashMap,
並提供了直接存取這個Map成員的接口,
如:hmset user:001 name “李三” age 18 birthday “20010101”
也就是說,Key仍然是用戶ID,value是一個Map,這個Map的key是成員的屬性名,value是屬性值,
這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis裏稱內部Map的key爲field), 也就是通過
key(用戶ID) + field(屬性標籤) 操作對應屬性數據了,既不需要重複存儲數據,也不會帶來序列化和併發修改控制的問題。很好的解決了問題。

這裏同時需要注意,Redis提供了接口(hgetall)可以直接取到全部的屬性數據,但是如果內部Map的成員很多,那麼涉及到遍歷整個內部Map的操作,由於Redis單線程模型的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。
實現方式:
上面已經說到Redis Hash對應Value內部實際就是一個HashMap,實際這裏會有2種不同實現,這個Hash的成員比較少時Redis爲了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,對應的value redisObject的encoding爲zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding爲ht。

List

常用命令:
lpush,rpush,lpop,rpop,lrange,BLPOP(阻塞版)等。

應用場景:
Redis list的應用場景非常多,也是Redis最重要的數據結構之一。
我們可以輕鬆地實現最新消息排行等功能。
Lists的另一個應用就是消息隊列,可以利用Lists的PUSH操作,將任務存在Lists中,然後工作線程再用POP操作將任務取出進行執行。

實現方式:
Redis list的實現爲一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括髮送緩衝隊列等也都是用的這個數據結構。

RPOPLPUSH source destination

命令 RPOPLPUSH 在一個原子時間內,執行以下兩個動作:
將列表 source 中的最後一個元素(尾元素)彈出,並返回給客戶端。
將 source 彈出的元素插入到列表 destination ,作爲 destination 列表的的頭元素。
如果 source 和 destination 相同,則列表中的表尾元素被移動到表頭,並返回該元素,可以把這種特殊情況視作列表的旋轉(rotation)操作。
一個典型的例子就是服務器的監控程序:它們需要在儘可能短的時間內,並行地檢查一組網站,確保它們的可訪問性。
redis.lpush “downstream_ips”, “192.168.0.10”
redis.lpush “downstream_ips”, “192.168.0.11”
redis.lpush “downstream_ips”, “192.168.0.12”
redis.lpush “downstream_ips”, “192.168.0.13”
Then:
next_ip = redis.rpoplpush “downstream_ips”, “downstream_ips”

BLPOP

假設現在有 job 、 command 和 request 三個列表,其中 job 不存在, command 和 request 都持有非空列表。考慮以下命令:
BLPOP job command request 30 #阻塞30秒,0的話就是無限期阻塞,job列表爲空,被跳過,緊接着command 列表的第一個元素被彈出。
1) “command” # 彈出元素所屬的列表
2) “update system…” # 彈出元素所屬的值
爲什麼要阻塞版本的pop呢,主要是爲了避免輪詢。舉個簡單的例子如果我們用list來實現一個工作隊列。執行任務的thread可以調用阻塞版本的pop去獲取任務這樣就可以避免輪詢去檢查是否有任務存在。當任務來時候工作線程可以立即返回,也可以避免輪詢帶來的延遲。

Set

4、Set

常用命令:
sadd,srem,spop,sdiff ,smembers,sunion 等。

應用場景:
Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的,當你需要存儲一個列表數據,又不希望出現重複數據時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。
比如在微博應用中,每個人的好友存在一個集合(set)中,這樣求兩個人的共同好友的操作,可能就只需要用求交集命令即可。
Redis還爲集合提供了求交集、並集、差集等操作,可以非常方便的實

實現方式:
set 的內部實現是一個 value永遠爲null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。

Sort Set

5、Sorted set

常用命令:
zadd,zrange,zrem,zcard等

使用場景:
以某個條件爲權重,比如按頂的次數排序.
ZREVRANGE命令可以用來按照得分來獲取前100名的用戶,ZRANK可以用來獲取用戶排名,非常直接而且操作容易。
Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以通過用戶額外提供一個優先級(score)的參數來爲成員排序,並且是插入有序的,即自動排序。
比如:twitter 的public timeline可以以發表時間作爲score來存儲,這樣獲取時就是自動按時間排好序的。
比如:全班同學成績的SortedSets,value可以是同學的學號,而score就可以是其考試得分,這樣數據插入集合的,就已經進行了天然的排序。
另外還可以用Sorted Sets來做帶權重的隊列,比如普通消息的score爲1,重要消息的score爲2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。

需要精準設定過期時間的應用
比如你可以把上面說到的sorted set的score值設置成過期時間的時間戳,那麼就可以簡單地通過過期時間排序,定時清除過期數據了,不僅是清除Redis中的過期數據,你完全可以把Redis裏這個過期時間當成是對數據庫中數據的索引,用Redis來找出哪些數據需要過期刪除,然後再精準地從數據庫中刪除相應的記錄。

實現方式:
Redis sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是所有的成員,排序依據是HashMap裏存的score,使用跳躍表的結構可以獲得比較高的查找效率,並且在實現上比較簡單。

消息訂閱

6、 Pub/Sub

Pub/Sub 從字面上理解就是發佈(Publish)與訂閱(Subscribe),在Redis中,你可以設定對某一個key值進行消息發佈及消息訂閱,
當一個key值上進行了消息發佈後,所有訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用作實時消息系統,比如普通的即時聊天,羣聊等功能。

客戶端1:subscribe rain
客戶端2:PUBLISH rain “my love!!!”
(integer) 2 代表有幾個客戶端訂閱了這個消息

Transaction

7、Transactions

誰說NoSQL都不支持事務,雖然Redis的Transactions提供的並不是嚴格的ACID的事務(比如一串用EXEC提交執行的命令,在執行中服務器宕機,那麼會有一部分命令執行了,剩下的沒執行),但是這個Transactions還是提供了基本的命令打包執行的功能(在服務器不出問題的情況下,可以保證一連串的命令是順序在一起執行的,中間有會有其它客戶端命令插進來執行)。
Redis還提供了一個Watch功能,你可以對一個key進行Watch,然後再執行Transactions,在這過程中,如果這個Watched的值進行了修改,那麼這個Transactions會發現並拒絕執行。
Session 1
(1)第1步
redis 127.0.0.1:6379> get age
“10”
redis 127.0.0.1:6379> watch age
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379>

Session 2
(2)第2步
redis 127.0.0.1:6379> set age 30
OK
redis 127.0.0.1:6379> get age
“30”
redis 127.0.0.1:6379>

Session 1
(3)第3步
redis 127.0.0.1:6379> set age 20
QUEUED
redis 127.0.0.1:6379> exec
(nil)
redis 127.0.0.1:6379> get age
“30”
redis 127.0.0.1:6379>

第一步,Session 1 還沒有來得及對age的值進行修改
  第二步,Session 2 已經將age的值設爲30
  第三步,Session 1 希望將age的值設爲20,但結果一執行返回是nil,說明執行失敗,之後我們再取一下age的值是30,這是由於Session 1中對age加了樂觀鎖導致的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章