Redis 快速入門

Redis 快速入門

談到Redis,大家應該都不陌生。它是用c語言開發的一個高性能鍵值數據庫,主要用於緩存領域。本章通過Redis的安裝,Redis的五大數據類型,Redis的Java客戶端,Redis與Spring 的整合 。來讓讀者對它有一個初步的瞭解。下一章再通過介紹配置文件來搭建Redis的主從模式和集羣模式(配置大於編程,先從簡單的編程入手)。

效果圖
Redis 緩存項目效果圖

需求:對商品類目進行Redis緩存處理
技術:Redis,Spring,SpringMVC,Mybatis,EasyUI
說明:EasyUI的樹菜單上一章節有介紹,這裏是爲了方便展示效果。項目結構圖中箭頭所指的文件是需要重點學習的。若對EasyUI 樹菜單感興趣的可以訪問:(該章節源碼中提供商品類名的sql文件)
http://blog.csdn.net/qq_19558705/article/details/78583888
源碼:見文章底部
項目結構
項目結構圖

Redis 安裝

安裝文檔:
https://github.com/ITDragonBlog/daydayup/blob/master/Redis/Redis%E5%AE%89%E8%A3%85.md

Redis 五大數據類型

Redis 五大數據類型有String 類型,Hash 類型,List 類型,Set 類型,Zset(Sortedset)類型。其中常用的是前三個。
官方提供的操作手冊:http://redisdoc.com/
在redis 自帶的客戶端中輸入命令時,可以使用tab自動補齊,新手建議不要偷懶。

String 類型

String 是 redis 最基本的類型,一個key對應一個value。
賦值:set key value
取值:get key
批量賦值:mset key value … keyN valueN
批量取值:mget key … keyN
取值並賦值:getset key value
刪除key:del key … keyN
數值加一:incr key
數值加N:incrby key n
數值減一:decr key
數值減N:decrby key n
字符串追加:append key value
字符串長度:strlen key
*注 形如”key … keyN” 表示可以批量操作

127.0.0.1:6379> set key value
OK
127.0.0.1:6379> get key
"value"
127.0.0.1:6379> mset key1 1 key2 2 key3 3
OK
127.0.0.1:6379> mget key1 key3
1) "1"
2) "3"
127.0.0.1:6379> del key
(integer) 1
127.0.0.1:6379> incr count
(integer) 1
127.0.0.1:6379> incrby count 10
(integer) 11
127.0.0.1:6379> decr count
(integer) 10
127.0.0.1:6379> decrby count 5
(integer) 5
127.0.0.1:6379> set str itdragon
OK
127.0.0.1:6379> append str " blog!"
(integer) 14
127.0.0.1:6379> get str
"itdragon blog!"
127.0.0.1:6379> strlen str
(integer) 14

Hash 散列類型

Redis hash 是一個鍵值對集合,和Java 的HashMap 類似。
Redis hash 是一個String 類型的 field 和 value 的映射表,hash特別適合用於存儲對象(key 可以是對象+id,field 是對象屬性,value則是屬性值)。
給一個字段賦值:hset key field value
給多個字段賦值:hmset key field value … fieldN valueN
取一個字段的值:hget key field
取多個字段的值:gmset key field … fieldN
取所有的字段名和值:hgetall key
刪除字段名和值:hdel key field … fieldN
判斷字段是否存在:hexists key field
獲取key的所有field:hkeys key
獲取key的所有value:hvals key
獲取field個數:hlen key
*注:這裏的field 就是 字段名,value 就是字段值

127.0.0.1:6379> hset user name itdragon
(integer) 1
127.0.0.1:6379> hget user name
"itdragon"
127.0.0.1:6379> hmset user position java study redis
OK
127.0.0.1:6379> hmget user position study
1) "java"
2) "redis"
127.0.0.1:6379> hgetall user
1) "name"
2) "itdragon"
3) "position"
4) "java"
5) "study"
6) "redis"
127.0.0.1:6379> hdel user name 
(integer) 1
127.0.0.1:6379> hdel user position study
(integer) 2
127.0.0.1:6379> hexists user name
(integer) 1
127.0.0.1:6379> hexists user age
(integer) 0
127.0.0.1:6379> hkeys user
1) "name"
2) "position"
3) "study"
127.0.0.1:6379> hvals user
1) "itdragon"
2) "java"
3) "redis"
127.0.0.1:6379> hlen user
(integer) 3

List 類型

Redis 列表是採用來鏈表來存儲的簡單字符串列表,按照插入順序排序。添加元素一般從鏈表兩端開始。
向列表左側加元素:lpush key value … valueN
向列表右側加元素:rpush key value … valueN
遍歷列表:lrange key startIndex endIndex
獲取List長度:llen key
通過下標獲取值:lindex key index
通過下標設置值:lset key index value
列表左側移除第一個元素:lpop key
列表右側移除第一個元素:rpop key
截取保留剩下的列表:ltrim key startIndex endIndex
在制定元素插入值:linsert key after/before index value
把集合第一個元素移到其他集合中:rpoplpush key otherListKey
* 注:若endIndex=-1 表示最後一位;otherListKey 表示其他集合

127.0.0.1:6379> lpush list 1 2
(integer) 2
127.0.0.1:6379> rpush list 3 4 
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "2"
2) "1"
3) "3"
4) "4"
127.0.0.1:6379> lpop list
"2"
127.0.0.1:6379> rpop list
"4"
127.0.0.1:6379> llen list
(integer) 2
127.0.0.1:6379> lindex list 1
"3"
127.0.0.1:6379> linsert list after 1 2
(integer) 3
127.0.0.1:6379> linsert list before 3 4
(integer) 4
127.0.0.1:6379> ltrim list 0 1
OK
127.0.0.1:6379> rpoplpush list newlist
"1"

Set 類型

Redis 的 Set 是String類型的無序集合。它是通過HashTable實現實現的,用法和 List 類型很相似。
新增集合元素:sadd key value … valueN
刪除集合元素:srem key value … valueN
獲取集合所有元素:smembers key
判斷集合元素是否存在:sismember key value
集合差集:sdiff key1 key2
集合交集:sinter key1 key2
集合並集:sunion key1 key2
獲取集合長度:scard key1

127.0.0.1:6379> sadd set a b c d
(integer) 4
127.0.0.1:6379> srem set a b c
(integer) 3
127.0.0.1:6379> smembers set
1) "d"
127.0.0.1:6379> sismember set a
(integer) 0
127.0.0.1:6379> sismember set d
(integer) 1
127.0.0.1:6379> sadd setA 1 2 3 
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"
127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"
127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> scard setA
(integer) 3

Zset 類型

Redis 的 zset(sorted set)和 set 一樣也是string類型元素的集合,且不允許有重複的成員。不同的是 zset 的每個元素都會關聯一個double類型的分數。zset正是通過分數來爲集合中的成員進行排序。zset的成員是唯一的,但分數(score)卻可以重複。
新增集合元素:zadd key score value … scoreN valueN
獲取元素分數:zscore key value
按照分數從小到大排序:zrange key startIndex endIndex
按照分數從大到小排序:zrevrange key startIndex endIndex
遍歷時顯示分數:withscores
統計分數比value少的個數:zrank key value
統計分數比value高的個數:zrevrank key value
輸出分數在制定值內的元素:zrangebyscore key score1 score2
給元素加分:zincrby key score value
獲取元素個數:zcard()
統計分數內的個數:zcount key score1 score2
刪除制定排名內的元素:zremrangebyrank key no1 no2
刪除指定分數內的元素:zremrangebyscore key score1 score2
刪除指定元素:zrem key value
* 注: zcount 統計分數內的個數,score1 <= keyScore =< score2;zremrangebyrank 的 no1 和 no2 表示排名的第幾位。

127.0.0.1:6379> zadd zset 65 A 67 C 66 B
(integer) 3
127.0.0.1:6379> zscore zset C
"67"
127.0.0.1:6379> zrange zset 0 -1
1) "A"
2) "B"
3) "C"
127.0.0.1:6379> zrevrange zset 0 -1
1) "C"
2) "B"
3) "A"
127.0.0.1:6379> zrevrange zset 0 -1 withscores
1) "C"
2) "67"
3) "B"
4) "66"
5) "A"
6) "65"
127.0.0.1:6379> zrank zset C
(integer) 2
127.0.0.1:6379> zrevrank zset C
(integer) 0
127.0.0.1:6379> zrangebyscore zset 65 66
1) "A"
2) "B"
127.0.0.1:6379> zrangebyscore zset 65 66 limit 1 2
1) "B"
127.0.0.1:6379> zincrby zset 10 A
"75"
127.0.0.1:6379> zcard zset
(integer) 3
127.0.0.1:6379> zcount zset 65 66
(integer) 1
127.0.0.1:6379> zremrangebyrank zset 0 1
(integer) 2
127.0.0.1:6379> zremrangebyscore zset 100 200
(integer) 0
127.0.0.1:6379> zrem zset A
(integer) 1

Jedis客戶端

Jedis 是比較主流的 Redis Java 客戶端。
第一步:導入Jedis需要的jar

<!-- Redis客戶端 -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <jedis.version>2.7.2</jedis.version>
</dependency>

第二步:單元測試類
Jedis 的語法和 Redis 幾乎一樣,如果學好了Redis,Jedis也就沒問題了,可謂是買一送一。建議使用連接池的方式。

package com.itdragon.redis;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class TestJedisOperate {

    private final static String HOST = "112.74.83.71";
    private final static int PORT = 6379;

    /**
     * jedis 的語法和 redis 的語法幾乎一致,比較常用的有Hash,String,List
     */
    @Test
    public void jedisSignle() {
        Jedis jedis = new Jedis(HOST, PORT);
        jedis.set("account", "itdragon");
        System.out.println("set , get 操作 : " + jedis.get("account"));
        jedis.mset("account:01", "itdragon01", "account:02", "itdragon02");
        System.out.println("mset , mget 操作 : " + jedis.mget("account:01", "account:02"));
        jedis.hset("user", "name", "ITDragon");
        System.out.println("hset , hget 操作 : " + jedis.hget("user", "name"));
        Map<String, String> userMap = new HashMap<>();
        userMap.put("password", "123456");
        userMap.put("position", "Java");
        jedis.hmset("user", userMap);
        System.out.println("hmset , hmget 操作 : " + jedis.hmget("user", "name", "password", "position"));
        if (0 == jedis.llen("userList")) {
            jedis.lpush("userList", "1", "2", "3");
        }
        System.out.println("List 類型 lpush , lrange 操作 : " + jedis.lrange("userList", 0, -1));
        jedis.sadd("userSet", "1", "2", "2");
        System.out.println("Set 類型 sadd , smembers 操作 : " + jedis.smembers("userSet"));
        Map<String, Double> scoreMembers = new HashMap<>();
        scoreMembers.put("A", 65.0);
        scoreMembers.put("C", 67.0);
        scoreMembers.put("B", 66.0);
        jedis.zadd("userScore", scoreMembers);
        System.out.println("Set 類型 zadd , zrange 操作 : " + jedis.zrange("userScore", 0, -1));
        jedis.close();
    }

    @Test
    public void testJedisPool() {
        JedisPool pool = new JedisPool(HOST, PORT);
        Jedis jedis = pool.getResource();
        System.out.println("通過連接池獲取 key 爲 account 的值 : " + jedis.get("account"));
        jedis.close();
        pool.close();
    }

}

Spring 整合 Redis

創建用於整合redis的文件 applicationContext-jedis.xml
建議使用redis 默認配置(默認,讓生活更美好)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    <!-- 加載配置文件 -->
    <context:property-placeholder location="classpath:resource/*.properties" />
    <!-- 連接池配置 (可以用 redis 默認配置,效果可能會更好)-->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大連接數 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空閒連接數 -->
        <property name="maxIdle" value="10" />
        <!-- 每次釋放連接的最大數目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 釋放連接的掃描間隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 連接最小空閒時間 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 連接空閒多久後釋放, 當空閒時間>該值 且 空閒連接>最大空閒連接數 時直接釋放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 獲取連接時的最大等待毫秒數,小於零:阻塞不確定的時間,默認-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在獲取連接的時候檢查有效性, 默認false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空閒時檢查有效性, 默認false -->
        <property name="testWhileIdle" value="true" />
        <!-- 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
        <property name="blockWhenExhausted" value="false" />
    </bean> 
    <!-- jedis客戶端單機版 -->
    <bean id="redisClient" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="${redis.host}" />
        <constructor-arg name="port" value="${redis.ip}" />
        <!-- <constructor-arg name="poolConfig" ref="jedisPoolConfig" /> -->
    </bean>
    <bean id="jedisClient" class="com.itdragon.common.utils.JedisClientSingle"/>
</beans>

簡單封裝了Jedis 常用方法 JedisClientSingle.java

package com.itdragon.common.utils;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

// 單例的Redis 工具類
public class JedisClientSingle {

    /**
     * connect timed out 問題: 
     * 1. 檢查redis服務是否開啓
     * 2. 檢查是否是因爲防火牆的問題
     * 3. 檢查網絡問題(如果在同一個局域網內幾乎不會出現這個問題)
     * Jedis jedis =new Jedis(HOST,PORT,100000);
     * JedisPool pool = new JedisPool(poolConfig, HOST, PORT, 100000);
     */

    @Autowired
    private JedisPool jedisPool; 

    public String get(String key) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.get(key);
        jedis.close();
        return string;
    }

    public String set(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.set(key, value);
        jedis.close();
        return string;
    }

    public String hget(String hkey, String key) {
        Jedis jedis = jedisPool.getResource();
        String string = jedis.hget(hkey, key);
        jedis.close();
        return string;
    }

    public long hset(String hkey, String key, String value) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(hkey, key, value);
        jedis.close();
        return result;
    }

    public long del(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }

    public long hdel(String hkey, String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(hkey, key);
        jedis.close();
        return result;
    }

}

獲取商品類名接口實現類 ProductCategoryServiceImpl.java

package com.itdragon.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.itdragon.common.pojo.EUTreeNode;
import com.itdragon.common.pojo.ResponseResult;
import com.itdragon.common.utils.JedisClientSingle;
import com.itdragon.common.utils.JsonUtils;
import com.itdragon.mapper.ProductCategoryMapper;
import com.itdragon.pojo.ProductCategory;
import com.itdragon.pojo.ProductCategoryExample;
import com.itdragon.pojo.ProductCategoryExample.Criteria;
import com.itdragon.service.ProductCategoryService;

@Service
public class ProductCategoryServiceImpl implements ProductCategoryService {

    @Autowired
    private ProductCategoryMapper categoryMapper;

    @Autowired
    private JedisClientSingle jedisClientSingle;

    @Value("${CATEGROY_ID_CACHE_REDIS_KEY}")
    private String CATEGROY_ID_CACHE_REDIS_KEY;

    @Override
    public List<EUTreeNode> getCategoryList(Long parentId) {
        long startTime = System.currentTimeMillis();
        List<EUTreeNode> resultList = new ArrayList<>();
        // 從redis緩存中取內容
        try {
            String cacheDatas = jedisClientSingle.hget(CATEGROY_ID_CACHE_REDIS_KEY, parentId.toString());
            if (StringUtils.isNotBlank(cacheDatas)) {
                List<ProductCategory> categories = JsonUtils.jsonToList(cacheDatas, ProductCategory.class);
                for (ProductCategory category : categories) {
                    EUTreeNode node = new EUTreeNode();
                    node.setId(category.getId());
                    node.setText(category.getName());
                    node.setState(category.getIsParent()?"closed":"open");
                    resultList.add(node);
                }
                System.out.println("redis cache Time : " + (System.currentTimeMillis() - startTime));
                return resultList;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        ProductCategoryExample example = new ProductCategoryExample();
        Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(1);
        criteria.andParentIdEqualTo(parentId); // 查詢父節點下的所有子節點
        List<ProductCategory> productCategories = categoryMapper.selectByExample(example);

        for (ProductCategory category : productCategories) {
            EUTreeNode node = new EUTreeNode();
            node.setId(category.getId());
            node.setText(category.getName());
            node.setState(category.getIsParent()?"closed":"open");
            resultList.add(node);
        }
        System.out.println("No redis cache Time : " + (System.currentTimeMillis() - startTime));
        // 向redis緩存中添加內容
        try {
            jedisClientSingle.hset(CATEGROY_ID_CACHE_REDIS_KEY, parentId.toString(), JsonUtils.objectToJson(productCategories));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList;
    }

    // 後面的內容看源碼...
}

源碼:https://github.com/ITDragonBlog/daydayup/tree/master/Redis

到這裏,Redis 的快速入門就結束了。下一章節介紹Redis 的主從和集羣。

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