Redis實戰總結-簡單介紹和常用數據結構及命令

* 序言

最近花了一些時間將《Redis實戰》,和網上關於Redis的一些博客研究了下。針對這段時間的學習做一個總結,內容如下:

  1. Redis介紹
  2. Redis的數據結構及命令
  3. Redis的管道及PUB/SUB機制
  4. Redis配置、複製及持久化
  5. Redis高可用性
  6. Redis內存優化及Lua腳本編程

其中第一篇博客介紹1、2兩部分,第二篇博客介紹3部分,第三篇博客介紹4、5兩部分,第四篇博客介紹6部分。

1. Redis介紹

Redis是一個開源(遵守BSD協議),基於內存的key-value高性能存儲系統,可以用作數據庫、速緩存和消息隊列代理。它支持字符串(STRING)、哈希表(HASH)、列表(LIST)、集合(SET)、有序集合(ZSET)等數據結構。

Redis 相對於其它 key - value 緩存產品,比如memorycache(分佈式緩存),Guavacache(本地緩存)有下面三大特點:

  • Redis支持數據持久化,可以將內存中的數據存儲在磁盤中,重啓的時候能夠還原Redis環境。
  • Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
  • Redis支持數據備份,即master-slave(主從)模式的數據備份(不支持主主複製)。

2. Redis的數據結構

Redis可以存儲鍵與5種不同數據結構類型之間的映射,這五種數據結構類型爲:STRING,HASH,LIST,SET,ZSET。每種數據結構都有自身的操作命令,當然也有一部分操作命令是對這些數據結構都通用的,比如DEL,RENAME等。

String

在Redis裏面,字符串可以存儲以下三種類型:

  • 字符串
  • 整數
  • 浮點數

Redis常見字符串命令:

命令 用例及描述
GET GET key——獲取給定鍵的值
SET SET key value——設置給定鍵的值
DEL DEL key——刪除給定的鍵(適用於所有類型)
INCR INCR key——將鍵存儲的值加上1
DECR INCR key——將鍵存儲的值減去1
INCRBY INCRBY key amount——將鍵存儲的值加上整數amount
DECRBY DECRBY key amount——將鍵存儲的值減去整數amount

HASH

和字符串一樣,散列存儲的值既可以是字符串又可以是數字值,同樣對於數字值可以進行自增或自減。
Redis中常見散列命令:

命令 用例及描述
HSET HSET key field value——在散列裏關聯起給定的鍵值對
HGET HGET key field——獲取指定散列鍵的值
HGETALL HGETALL key——獲取散列包含的所有鍵值對
HDEL HDEL key field [field …]——刪除散列包含的鍵
HMSET HMSET key field value [field value …]——同HSET,可以批量操作
HMGET HMGET key field [field …]——同HGET,可以批量操作
HLEN HLEN key——返回散列中包含的鍵值對數量
HEXISTS HEXISTS key field——判斷散列中是否存在鍵
HKEYS HKEYS key——獲取散列中包含的所有鍵
HVALS HVALS key——獲取散列中包含的所有值

LIST

Redis列表(鏈式存儲結構)允許用戶從列表額兩端推入或者彈出元素,以獲取元素,以及執行各種常見的列表操作。
Redis中常見列表命令:

命令 用例及描述
RPUSH RPUSH key value [value …]——將一個值或多個值推入到列表的右端
LPUSH LPUSH key value [value …]——將一個值或多個值推入到列表的右端
LINDEX LINDEX key index——返回列表偏移量爲index的元素
LRANGE LRANGE key start stop——返回列表從start偏移量到stop偏移量的所有元素(包括start和stop處的元素)
LPOP LPOP key——移除並返回列表最左邊元素
RPOP RPOP key——移除並返回列表最又邊元素
LTRIM LTRIM key start stop——對列表裁剪,只保留從start偏移量到stop偏移量的所有元素(包括start和stop處的元素)

Redis列表還存在一些阻塞式彈出推入命令,可以應用在消息傳遞和任務隊列等場景中。

SET

Redis的集合以無序的方式來存儲多個各不相同的元素,用戶可以很方便的添加元素,移除元素以及判斷元素是否存在與集合中。
Redis中常見集合命令:

命令 用例及描述
SADD SADD key member [member …]——將一個或多個元素添加到集合中
SREM SREM key member [member …]——從集合中移除一個或多個元素
SISMEMBER SISMEMBER key member——檢查元素member是否存在於集合中
SCARD SCARD key——統計集合中元素數量
SMEMBERS SMEMBERS key——返回集合中包含的所有元素
SMOVE SMOVE source destination member——將集合source中的元素member移動到集合destination中,移植成功返回1

ZSET

有序集合和散列存儲着鍵與值之間映射類似,也存在成員和分值(正常以時間或者值得大小作爲分值)的映射,並且提供了分值的處理命令。
Redis中常見有序集合命令:

命令 用例及描述
ZADD ZADD key score member [score member …]——將帶有給定分值的成員添加到有序結合中
ZREM ZREM key member [member …]——從有序集合中移除給定的元素
ZCARD ZCARD key——返回有序集合中包含的成員數量
ZINCRBY ZINCRBY key increment member——將member的分值加1
ZCOUNT ZCOUNT key min max——返回分值介於min~max之間的成員變量
ZRANK ZRANK key member——返回member的分值

當然有序集合還有許多比較有用的命令,就不一一列舉了。

SORT命令

SORT命令可以實現按照給定的選項,對輸入的列表,集合,有序集合,散列進行排序,返回排序後的結果。

命令 用例及描述
SORT sort key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC

3.Redis其他命令

Redis除了上述數據結構操作命令外,還有發佈訂閱命令,實現基本事務特性的MULTI命令和EXEC命令,自動過期EXPIRE命令。下面介紹這些命令,其中發佈訂閱命令發在下篇博客中介紹。

MULTI和EXEC命令

在Redis中,有時可能需要同時處理多個命令,即需要將這些命令一個事物,一次性提交給Redis。Redis爲實現這種機制,引進了multi和exec命令。首先需要先執行multi命令,然後在輸入我們需要執行的命令,最後在執行exec命令。
這裏寫圖片描述

EXPIRE命令

在使用Redis存儲數據時,有些數據存儲一段時間後就不會再使用了,比如不同系統之間的會話密鑰(保證兩個小時後自動失效),因而Redis引入了一種“帶有生存時間”特殊鍵。如果爲一個鍵設置了生存時間,則到了生存時間,Redis會自動的(應用程序透明,由Redis線程自己負責)將改建刪除。
常見的幾個命令:

命令 用例及描述
PERSIST PERSIST key——移除鍵的過期時間
TTL TTL key——查看給定鍵距離過期還有多少秒
EXPIRE EXPIRE key seconds——設置鍵的生存時間,單位爲秒

4. 在JAVA中實現Redis連接池

在Java中使用Jedis.jar操作Redis,下面給出demo演示在Java中如何構建Redis緩存連接池,並給出散列(MAP)相關命令的代碼實現。

新建Maven工程,在pom.xml中添加依賴(maven和log4j)

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

配置log4j,log4j.properties

#config root logger
log4j.rootLogger = DEBUG,system.out
log4j.appender.system.out=org.apache.log4j.ConsoleAppender
log4j.appender.system.out.layout=org.apache.log4j.PatternLayout
log4j.appender.system.out.layout.ConversionPattern=MINAServer Logger-->%5p{%F:%L}-%m%n

#config this Project.file logger
log4j.logger.thisProject.file=INFO,thisProject.file.out
log4j.appender.thisProject.file.out=org.apache.log4j.DailyRollingFileAppender
log4j.appender.thisProject.file.out.File=logContentFile.log
log4j.appender.thisProject.file.out.layout=org.apache.log4j.PatternLayout

JedisUtil類:

package util;

import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Map;
import java.util.Set;

/**
 * 搭建Redis緩存連接池
 * @author guweiyu
 */
public class JedisUtil {

    private static Logger logger = Logger.getLogger(JedisUtil.class.getName());

    // 創建JedisPoll實例
    private static JedisPool jedisPool;

    // 設置Jedis連接相關參數
    // Redis服務器IP,端口號
    private static String REDIS_ADDRESS = "127.0.0.1";
    private static Integer REDIS_PORT = 6379;

    // 最大連接數, 默認8個
    private static Integer MAX_TOTAL = 8;
    // 最大空閒連接數, 默認8個
    private static Integer MAX_IDLE = 8;
    // 獲取連接時的最大等待毫秒數(如果設置爲阻塞時BlockWhenExhausted),如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1)
    private static Integer MAX_WAIT_MILLIS = 10000;
    // Re
    private static Integer TIMEOUT = 10000;


    /**
     * 初始化Redis緩存連接池
     */
    public static void init() {
        try {
            // 構建Redis配置
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_TOTAL);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT_MILLIS);

            // 創建Redis緩存連接池子
            jedisPool = new JedisPool(config, REDIS_ADDRESS, REDIS_PORT, TIMEOUT);
            logger.info("Redis連接池初始化成功");
        } catch (Exception e) {
            logger.error("Redis連接池初始化失敗", e);
            e.printStackTrace();
        }
    }

    /**
     * 關閉redis連接實例
     * @param jedis
     */
    private static void safeReturn(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    /**
     * 判斷散列中是否存在鍵
     * @param key
     * @param field
     * @return
     */
    public static boolean hexist(String key, String field) {
        Jedis jedis = jedisPool.getResource();
        try {
            return jedis.hexists(key, field);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 在散列裏批量關聯起給定的鍵值對,並設置超時時間,單位秒
     * @param key
     * @param hash
     * @param timeout
     */
    public static void setHash(String key, Map<String, String> hash, int timeout) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.hmset(key, hash);
            jedis.expire(key, timeout);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 在散列裏批量關聯起給定的鍵值對
     * @param key
     * @param hash
     */
    public static void setHash(String key, Map<String, String> hash) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.hmset(key, hash);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 在散列裏關聯起給定的鍵值對
     * @param key
     * @param field
     * @param value
     */
    public static void addHashValue(String key, String field, String value) {

        Jedis jedis = jedisPool.getResource();
        try {
            jedis.hset(key, field, value);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 獲取指定散列鍵的值
     * @param key
     * @param field
     * @return
     */
    public static String getHashValue(String key, String field) {

        Jedis jedis = jedisPool.getResource();
        try {
            return jedis.hget(key, field);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 獲取散列包含的所有鍵值對
     * @param key
     * @return
     */
    public static Map<String, String> hgetAll(String key) {
        Jedis jedis = jedisPool.getResource();
        try {
            return jedis.hgetAll(key);
        } finally {
            safeReturn(jedis);
        }
    }

    /**
     * 刪除給定的鍵(適用於所有類型)
     * @param key
     */
    public static void del(String key) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.del(key);
        } finally {
            safeReturn(jedis);
        }
    }
}

主程序測試:

import org.apache.log4j.Logger;
import util.JedisUtil;

import java.util.HashMap;

public class Application {

    private static Logger logger = Logger.getLogger(Application.class);

    private static String MAP_KEY_PREFIX = "hash_map_prefix_";

    private static String TEST1 = "test1";
    private static String TEST2 = "test2";
    private static String TEST3 = "test3";
    private static String TEST4 = "test4";

    public static void main (String[] args) throws Exception {

        logger.info("Main Application is starting");
        JedisUtil.init();

        String key1 = new StringBuilder(MAP_KEY_PREFIX).append(TEST1).toString();
        HashMap<String, String> test1Map = new HashMap<>();
        test1Map.put("user", "guweiyu");
        JedisUtil.setHash(key1, test1Map, 5);
        logger.info("key["+key1+"]:"+JedisUtil.getHashValue(key1, "user"));

        logger.info("key["+key1+"]:"+JedisUtil.hgetAll(key1));

        Thread.sleep(6000);
        logger.info("key["+key1+"]:"+JedisUtil.hgetAll(key1));

        JedisUtil.del(key1);
        logger.info("key["+key1+"]:"+JedisUtil.hexist(key1, "user"));

        String key2 = new StringBuilder(MAP_KEY_PREFIX).append(TEST2).toString();
        HashMap<String, String> test2Map = new HashMap<>();
        test2Map.put("user", "hebaobao");
        JedisUtil.setHash(key2, test2Map);

        logger.info("key["+key2+"]:"+JedisUtil.getHashValue(key2, "user"));

        logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));

        Thread.sleep(6000);
        logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));

        JedisUtil.addHashValue(key2, "mobino", "173");
        logger.info("key["+key2+"]:"+JedisUtil.hgetAll(key2));
    }
}

程序運行結果:
這裏寫圖片描述

持之以恆!!!!!

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