1. 程式人生 > >Redis入門及在商城案例中的使用

Redis入門及在商城案例中的使用

自學那麼多月以來學到的知識點挺多幾乎每天都在接受新東西,接受的多忘的也多,想回頭再去找也不知道從哪裡找了,所以決定執行好幾個月前就決定的事情-寫部落格,用來記錄自己每次所學習到的東西。

由於自己實習的時候,自己做的專案的資料庫就是用的MySql跟Redis。所以先寫下以前在做商城案例的時候用的redis。

redis屬於NoSql分類,它把資料都是快取在記憶體中的,我們都知道記憶體的讀寫效率跟硬碟不是一個級別的,最後redis會週期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案。既然用redis讀取效率那麼高,最後內容也會新增到磁碟那麼我們就當然要使用它了。

對於redis的安裝我自己也是網上找的。建議自己使用的時候最好linux下安裝一個,windows下裝一個桌面版的。

一、redis的基本操作
redis是一個key-value儲存系統。它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sortedset–有序集合)和hash(雜湊型別)五種資料型別,儲存形式均為字串。

啟動redis
啟動redis服務:[[email protected] bin]# ./redis-server redis.conf
檢視程序:[[email protected] bin]# ps aux|grep redis
root 1793 0.3 0.0 34992 1772 ? Ssl 10:29 0:00 ./redis-server *:6379
root 1797 0.0 0.0 5532 812 pts/0 S+ 10:29 0:00 grep redis
表示啟動成功。
啟動客戶端:[

[email protected] bin]# ./redis-cli
127.0.0.1:6379>
其中6379表示本機的6379埠服務。
連線到該linux:ctrl+c退出,接著切換
[[email protected] bin]# ./redis-cli -h 192.168.25.128 -p 6379
192.168.25.128:6379>
這就切換成功了。接下來進行操作。

1.1、String型別
為了看的更直觀,這裡就直接展示操作內容。
儲存:set key value
取值:get key
刪除:del key
檢視所有鍵:keys *
示例:

192.168.25.128:6379> set str1 123
OK
192.168.25.128:6379> set str2 abc
OK
192.168.25.128:6379> set str3 xixi
OK
192.168.25.128:6379> get str1
"123"
192.168.25.128:6379> get str2
"abc"
192.168.25.128:6379> get str3
"xixi"
192.168.25.128:6379> del str1
(integer) 1
192.168.25.128:6379> keys *
 1) "str2"
2) "str3"

增1:incr key
減1:decr key
注意, 雖然redis儲存形式都是字串,但是自增減的時候要求value必須能解析成數值型別,比如你的value是”1ad”那就不行,就拿商城案例來說,可以用來生成訂單號(要求絕對不要重複),或者新增,減少商品數量時候使用。

示例:先新增鍵值對 str1 3,再自增就成了4

192.168.25.128:6379> set str1 3
OK
192.168.25.128:6379> incr str1
(integer) 4

1.2、Hash型別
相當於一個key對於一個map,map中還有key-value
儲存:hset key field value
取值:hget key field
檢視某個鍵對應的map裡面的所有key:hkeys key
檢視某個鍵對應的map裡面的所有的value:hvals key
檢視某個鍵的map:hgetall key

示例:

192.168.25.128:6379> hset hone field1 123
(integer) 1
192.168.25.128:6379> hset hone field2 abc
(integer) 1
192.168.25.128:6379> hset hone field3 haha
(integer) 1
192.168.25.128:6379> hget hone field1
"123"
192.168.25.128:6379> hget hone field2
"abc"
192.168.25.128:6379> hget hone field3
"haha"
192.168.25.128:6379> hkeys hone
1) "field1"
2) "field2"
3) "field3"
192.168.25.128:6379> hvals hone
1) "123"
2) "abc"
3) "haha"
192.168.25.128:6379> hgetall hone
1) "field1"
2) "123"
3) "field2"
4) "abc"
5) "field3"
6) "haha"

1.3、List型別
儲存:push,分為lpush list v1 v2 v3 v4 …(左邊新增),rpush list v1 v2 v3 v4 …(右邊新增)
取值:pop,分為lpop lpop list(左邊取,移除list最左邊的值) ,rpop rpop list(右邊取,移除list最右邊的值)
檢視list:lrange key 0 -1(0 -1表示檢視所有)
儲存,取值操作跟棧的儲存,取值方法是一樣的,而不是add,get,儲存的值有序可以重複。用pop取值取完後該值就從list中移除了。

示例:

```
192.168.25.128:6379> lpush list1 1 2 3 4 5 6
(integer) 6
192.168.25.128:6379> rpush list1 a b c d e 
(integer) 11
192.168.25.128:6379> lrange list1 0 -1
 1) "6"
 2) "5"
 3) "4"
 4) "3"
 5) "2"
 6) "1"
 7) "a"
 8) "b"
 9) "c"
10) "d"
11) "e"
192.168.25.128:6379> lpop list1
"6"
192.168.25.128:6379> lrange list1 0 -1
 1) "5"
 2) "4"
 3) "3"
 4) "2"
 5) "1"
 6) "a"
 7) "b"
 8) "c"
 9) "d"
10) "e"
192.168.25.128:6379> rpop list1
"e"
192.168.25.128:6379> lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
6) "a"
7) "b"
8) "c"
9) "d"

1.4、Set型別
set中的元素是無序不重複的,出現重複會覆蓋
儲存:sadd key v1 v2 v3 …
移除:srem key v
檢視set集合: smembers key
另外還提供了差集,交集,並集操作
差集:sdiff seta setb(seta中有setb中沒有的元素)
交集:sinter seta setb
並集:sunion seta setb

192.168.25.128:6379> sadd set a b a b c d
(integer) 4
192.168.25.128:6379> srem set a
(integer) 1
192.168.25.128:6379> smembers set
1) "d"
2) "b"
3) "c"
192.168.25.128:6379> sadd seta a b c d e
(integer) 5
192.168.25.128:6379> sadd setb c d e f g 
(integer) 5
192.168.25.128:6379> sdiff seta setb(差集,seta有setb沒有)
1) "a"
2) "b"
192.168.25.128:6379> sdiff setb seta (差集,setb有seta沒有)
1) "g"
2) "f"
192.168.25.128:6379> sinter seta setb(交集)
1) "d"
2) "e"
3) "c"
192.168.25.128:6379> sunion seta setb(並集)
1) "a"
2) "b"
3) "d"
4) "g"
5) "f"
6) "e"
7) "c"

1.5、SortedSet,有序Set
儲存:儲存的時候要求對set進行排序,需要對儲存的每個value值進行打分,預設排序是分數由低到高。zadd key 分數1 v1 分數2 v2 分數3 v3…
取值:取指定的值 zrem key value
取所有的值(不包括分數):zrange key 0 -1,降序取值用zrevrange key 0 -1
取所有的值(帶分數):zrange(zrevrange) key 0 -1 withscores

示例:

192.168.25.128:6379> zadd zset1 1 a 3 b 2 c 5 d(要求給定分數,從而達到排序效果,預設升序)
(integer) 4
192.168.25.128:6379> zrange zset1 0 -1
1) "a"
2) "c"
3) "b"
4) "d"
192.168.25.128:6379> zrem zset1 a 
(integer) 1
192.168.25.128:6379> zrange zset1 0 -1
1) "c"
2) "b"
3) "d"
192.168.25.128:6379> zrevrange zset1 0 -1(按分數降序排)
1) "d"
2) "b"
3) "c"
192.168.25.128:6379> zrevrange zset1 0 -1 withscores
1) "d"
2) "5"
3) "b"
4) "3"
5) "c"
6) "2"

1.6、key命令
由於redis是記憶體儲存資料,所以不能夠儲存過大的資料量,所以儲存在redis中的資料,在不再需要的時候應該清除掉。比如,使用者買完東西下訂單,生成的訂單資訊儲存了在redis中,但是使用者一直沒支付那麼儲存在redis中的訂單資訊就應該清除掉,這個時候就可以通過設定redis的過期時間來完成,一旦達到了過期時間就清除該資訊。
設定key的過期時間:expired key 過期時間(秒)
檢視key的有效剩餘時間:ttl key
清除key的過期時間,持久化該key:persist key
-1:表示持久化
-2: 表示該key不存在

示例:

192.168.25.128:6379> expire zone 60
(integer) 1
192.168.25.128:6379> ttl zone
(integer) 55
192.168.25.128:6379> ttl zone
(integer) 51
192.168.25.128:6379> ttl zone
(integer) 48
192.168.25.128:6379> ttl zone
(integer) 37
192.168.25.128:6379> ttl zone
(integer) 16
192.168.25.128:6379> ttl zone
(integer) -2  -->(該key已經不存在)
192.168.25.128:6379> expire sone 30
(integer) 1
192.168.25.128:6379> ttl sone
(integer) 22
192.168.25.128:6379> persist sone
(integer) 1
192.168.25.128:6379> ttl sone
(integer) -1  -->(該key是持久化的)

對與上面的基本操作,就我個人在案例中以及實習的真實專案中來說,至少得要掌握String型別,Hash型別以及key命令。
二、商城中使用redis

2.1、使用jedis操作redis(單機版)測試

進行測試之前需要引入依賴

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

單機版測試:

@Test
    public void testJedis() throws Exception{
        //建立一個連線jedis物件,引數:host,port
        Jedis jedis = new Jedis("192.168.25.128", 6379);
        //直接使用jedis來操作redis,所有jedis的命令都對應一個方法
        jedis.set("test123", "my first jedis test");
        String s = jedis.get("test123");
        System.out.println(s);
        //關閉連線
        jedis.close();
    }
    輸出結果:my first jedis test
    去客戶端檢視:
    192.168.25.128:6379> get test123
    "my first jedis test"

這裡只測試了String型別,Jedis提供了與redis操作對應的方法來操作redis。
操作hash型別主要有:hset(…),hget(…),hdel(…),hexist(…)
key命令:persist(key),expire(key,seconds)
具體的根據提示來選擇自己需要的。

單機版測試,從連線池獲取jedis:

    @Test//連線單機版
    public void testJedisPool() throws Exception{
        JedisPool jedisPool = new  JedisPool("192.168.25.128", 6379);
        //從連線池獲得一個連線,就是一個jedis物件
        Jedis jedis = jedisPool.getResource();
        //操作redis
        String s = jedis.get("test123");
        System.out.println(s);
        //關閉連線,每次使用完畢後關閉連線,連線池回收資源
        jedis.close();
        //關閉連線池
        jedisPool.close();
    }

這個就跟從資料庫連線池獲取連線道理是一樣的,需要連線的時候從連線池中取獲取一個連線就行,使用完後就放回連線池,而不用重新去建立一個連線,這項技術能大大提高操作redis的效能。

2.2、使用JedisClient操作redis (單機版)

測試的時候我們發現,每次都要自己建立關閉連線,頻繁使用的話會顯得很繁瑣。所以我們可以一開始就定義好對應方法來幫我們關閉連線。使用的時候呼叫自己的方法即可。

其實專案上線後如果網站做大了,那麼用就一個redis肯定是不夠的,萬一總掛對網站影響就大了,所以最後redis需要進行叢集,這在後面會講到。但是開發階段使用單機版就行了,不管是單機還是叢集最後用到的jedis方法還是一樣的,只是實現不一樣。那麼我們可以寫個介面,定義好兩者公共的方法,然後只要分別提供兩個實現類來實現這個介面就行。這個介面的名稱在這裡叫JedisClient(由自己定義,當然也可以叫別的名字)。

介面如下:

public interface JedisClient {
    String set(String key, String value);
    String get(String key);
    Boolean exists(String key);
    Long expire(String key, int seconds);
    Long ttl(String key);
    Long incr(String key);
    Long hset(String key, String field, String value);
    String hget(String key, String field);
    Long hdel(String key, String... field);
    Boolean hexists(String key, String field);
    List<String> hvals(String key);
    Long del(String key);
}

這裡定義的方法主要是針對String型別,Hash型別,key命令。
比如String型別,Hash型別的儲存、獲取、刪除、是否存在指定key,設定過期時間,自增,自間,有效時間等。

單機版實現類如下:

public class JedisClientPool implements JedisClient {

    private JedisPool jedisPool;

    public JedisPool getJedisPool() {
        return jedisPool;
    }
    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
    public String set(String key, String value) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.set(key, value);
        jedis.close();
        return result;
    }
    public String get(String key) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.get(key);
        jedis.close();
        return result;
    }
    public Boolean exists(String key) {
        Jedis jedis = jedisPool.getResource();
        Boolean result = jedis.exists(key);
        jedis.close();
        return result;
    }
    public Long expire(String key, int seconds) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.expire(key, seconds);
        jedis.close();
        return result;
    }
    public Long ttl(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.ttl(key);
        jedis.close();
        return result;
    }

    public Long incr(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.incr(key);
        jedis.close();
        return result;
    }
    public Long hset(String key, String field, String value) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hset(key, field, value);
        jedis.close();
        return result;
    }
    public String hget(String key, String field) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.hget(key, field);
        jedis.close();
        return result;
    }
    public Long hdel(String key, String... field) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.hdel(key, field);
        jedis.close();
        return result;
    }
    public Boolean hexists(String key, String field) {
        Jedis jedis = jedisPool.getResource();
        Boolean result = jedis.hexists(key, field);
        jedis.close();
        return result;
    }
    public List<String> hvals(String key) {
        Jedis jedis = jedisPool.getResource();
        List<String> result = jedis.hvals(key);
        jedis.close();
        return result;
    }
    public Long del(String key) {
        Jedis jedis = jedisPool.getResource();
        Long result = jedis.del(key);
        jedis.close();
        return result;
    }
}

其實這些方法主要還是呼叫了jedis的方法,主要是幫我們建立、關閉了連線然後進行封裝,從而在專案中使用可以簡化操作。
叢集版這裡就不貼出來了,後面講叢集的時候會給出。

2.3、從Spring容器中獲取JedisClient

在案例中,JedisClient是與Spring整合的。不然每次都要自己建立JedisClient物件,使用Spring那麼就不用我們自己建立物件了。

單機版JedisClient與spring整合:

<!-- 連線單機版-->
    <bean class="cn.e3mall.jedis.JedisClientPool">
        <property name="jedisPool" ref="jedisPool"/>
    </bean>
    配置單機版jedis連線池
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <constructor-arg name="host" value="192.168.25.128"/>
        <constructor-arg name="port" value="6379"/>
    </bean>

這樣初始化Spring容器的時候就會建立單機版JedisClient了

測試:

public void testJedisClient() throws Exception{
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
        //從容器中拿到JedisClient物件
        JedisClient jc = ac.getBean(JedisClient.class);
        jc.set("mytest", "jedisClient");
        String s = jc.get("mytest");
        System.out.println(s);  
    }
    輸出:jedisClient

到這裡就可以大膽的在專案中使用JedisClient來操作redis了。

2.4、實現redis在商城案例中進行快取

2.4.1、商城首頁輪播圖使用redis
需求:
在商城首頁,有個輪播圖:
商城首頁
圖種輪播的內容是8個,暫時都是偽造的。

而輪播展示的內容按照以往學習的經驗來說就應該是從資料庫裡面查出來的。這些內容是屬於專案的內容管理模組,所輪播的內容是同屬一個內容分類id,也就是說,程式碼實現是根據內容分類id查詢到內容資訊(4個)然後在頁面顯示,查詢到的這個內容分類id我們可以在專案中自己設定,比如就把這個id放在配置檔案裡面。需要改變輪播的內容時候就去改一下配置檔案裡面的內容分類id。然後會根據這個分類id去資料庫中查詢對應的內容。所涉及的兩張表一個是content_category,一個是content(外來鍵為content_category的id)。為了更直觀,表也給出來了。

兩張表如下:
內容分類表

內容表
分類id為89分類下有4條內容記錄。所以實際上首頁輪播圖展示的內容應該是4個。

不使用NoSql存在的問題:
對於需要的資訊如果每次都去資料庫查,如果人少倒沒事,但如果併發很高的情況那麼資料庫壓力就大了嚴重還可能會宕機,很影響消費體驗。這個時候就可以用redis了來提高效率了。

處理過程:當訪問商城首頁的時候,呼叫服務,檢視快取裡面是否有分類id(比如這裡分類id是89)對應的內容(這裡共4個),如果有的話,直接從快取取出來顯示到頁面。如果沒有則查詢資料庫,查出來的資料需要存入到redis中,再顯示到頁面。

商城首頁表現層工程(e3-portal-web)

public class IndexController {
    @Autowired
    private ContentService contentService;
    @Value("${CONTENT_LUNBO_ID}")
    private Long CONTENT_LUNBO_ID;

    @RequestMapping("/index")
    public String showIndex(Model model){
        //根據分類id查詢對應內容列表
        List<TbContent> ad1List = contentService.getContentListByCid(CONTENT_LUNBO_ID);
        //將結果傳遞給頁面
        model.addAttribute("ad1List", ad1List);
        return "index";
    }
}
屬性配置檔案中:CONTENT_LUNBO_ID=89

內容服務層工程(e3-content-service):
對於redis中用哪種型別的value來存放內容的List集合呢,如果用String型別的話,那麼鍵就是內容分類id,值就是List集合其中存放的是該分類id下的內容物件,但是考慮到該分類id也對應了其它資訊,也要存放在redis中,那麼一個分類id對應兩個不同的查詢集合肯定是不行的。當然可以使用分類id加不同的字首字尾來加以區分。
在這裡為了更直觀而又不衝突,可以考慮用Hash來儲存。key的話根據實際需要自己定義,比如這裡叫CONTENT_LIST,對應的map中的key為內容分類id,值為內容列表。

@Service
public class ContentServiceImpl implements ContentService{

    @Autowired
    private TbContentMapper contentMapper;
    @Autowired
    private JedisClient jedisClient;
    @Value("${CONTENT_LIST}")
    private String CONTENT_LIST;

    /*
     * 根據內容分類id查詢內容列表
     */
    public List<TbContent> getContentListByCid(Long cid) {
        try {
            //如果快取中有直接響應結果
            String json = jedisClient.hget(CONTENT_LIST, cid+"");
            if(StringUtils.isNotBlank(json)){
                List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
                return list;
            }
            //如果沒有查詢資料庫

        } catch (Exception e) {
            e.printStackTrace();
        }

        //-------------------------
        TbContentExample example = new TbContentExample();
        Criteria criteria = example.createCriteria();
        //設定查詢條件
        criteria.andCategoryIdEqualTo(cid);
        //執行查詢,withBlogs()表示 包含大文字
        List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example);
        //----------------------------
        //把結果新增到快取
        try {                                           
            jedisClient.hset(CONTENT_LIST, cid + "",JsonUtils.objectToJson(list));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}
屬性配置檔案中:CONTENT_LIST=CONTENT_LIST

用try,catch對redis的操作包裹起來是因為redis出現的問題是不應該影響正常業務邏輯的,否則出現異常會出現事務回滾的問題。

測試:
訪問商城首頁顯示如下。
這裡寫圖片描述
第一次訪問是從資料庫中查的,查詢出來之後放到redis中,後續則都是從redis中查詢出來。如果通過修改屬性配置檔案中的內容分類id,那麼則在redis中不存在,還會查詢資料庫。

檢視客戶端redis,(可以通過去客戶端使用命令去查詢),建議下載個桌面版的redis連線到linux上安裝好的redis。那麼在桌面版的redis中也能檢視linux上redis中儲存的資訊了。
這裡寫圖片描述
發現原本redis中不存在該資訊,但是現在卻有了。

Redis實現快取同步
考慮這麼個問題,如果在後臺我們在內容分類id(比如為89)下面添加了一個內容或者減少了一個內容。當再次訪問首頁的時候,還是根據分類id為89先去redis中查,發現redis中有資料,按照上面來說就是4條資料,那麼首頁輪播圖就是4個。但是實際上資料庫中已經不是4個了,而是5個或者3個或者其它。這就存在資訊不同步問題了。這裡解決的辦法是,每次新增內容或者減少內容等凡是對內容進行了修改操作,那麼就需要清空redis中的資訊。當下次訪問首頁的時候會去資料庫中查詢最新的內容資訊,存放到redis中,從而實現了快取同步,程式碼如下。

商品管理表現層(e3-manager-web)

/*
 * 內容管理controller
 */
@Controller
public class ContentController {

    @Autowired
    private ContentService contentService;

    @RequestMapping(value="/content/save")
    @ResponseBody
    public E3Result addContent(TbContent tbContent){
        //呼叫服務把內容資料儲存到資料庫
        E3Result result = contentService.addContent(tbContent);
        return result;
    }
}

工具類(E3Result)

/**
 * 自定義響應結構
 */
public class E3Result implements Serializable{

    // 定義jackson物件
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 響應業務狀態
    private Integer status;

    // 響應訊息
    private String msg;

    // 響應中的資料
    private Object data;

    public static E3Result build(Integer status, String msg, Object data) {
        return new E3Result(status, msg, data);
    }

    public static E3Result ok(Object data) {
        return new E3Result(data);
    }

    public static E3Result ok() {
        return new E3Result(null);
    }

    public E3Result() {

    }

    public static E3Result build(Integer status, String msg) {
        return new E3Result(status, msg, null);
    }

    public E3Result(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public E3Result(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }
    get,set方法
   }

內容管理服務層:

@Service
public class ContentServiceImpl implements ContentService{

    @Autowired
    private TbContentMapper contentMapper;
    @Autowired
    private JedisClient jedisClient;
    @Value("${CONTENT_LIST}")
    private String CONTENT_LIST;
    /*
     * 將內容資料插入到內容表
     */
    public E3Result addContent(TbContent content) {
        content.setCreated(new Date());
        content.setUpdated(new Date());
        //插入到資料庫
        contentMapper.insert(content);
        //-------------------
        //快取同步,刪除快取中對應的資料
        jedisClient.hdel(CONTENT_LIST, content.getCategoryId().toString());
        return E3Result.ok();
    }
    /*
     * 根據內容分類id查詢內容列表
     */
    同上
}

刪除內容跟修改內容就不再寫了,同樣都是清除內容分類id對應的快取資料。

至此商城首頁輪播圖使用redis來快取內容資訊就做完了。
2.4.2、在商品詳情中使用redis
在商城的其他地方,比如查詢到商品,點選該商品會進入到商品詳情列表。這裡也使用了redis,比如在促銷日雙11雙12之類,有些商品很暢銷,那麼單位時間的瀏覽量就會很高,對於這種情況我們可以將商品資訊新增到redis中。

注意:雖然邏輯跟首頁輪播類似,點選商品的時候會根據商品id去redis中查詢是否存在該商品資訊,有的話直接響應,如果沒有那麼就去查資料庫,查出來的資料存到redis中再做響應。
但是跟首頁輪播中有一點不同的是,商品資訊不能一直在redis中存放,商品種類過多的話非常耗費redis的儲存空間,所以需要設定一下過期時間,如果這段時間該商品沒人訪問的話就應該將該商品資訊從redis中清除來釋放空間,如果有人訪問的話那麼就重新設定回原來的過期時間。

商品詳情表現層工程(e3-item-web)

/*
 *  商品詳情頁面展示Controller
 */
@Controller
public class ItemController {

    @Autowired
    private ItemService itemService;

    @RequestMapping("/item/{itemId}")
    public String showItemInfo(@PathVariable Long itemId, Model model){
        //呼叫服務取商品基本資訊
        TbItem tbItem = itemService.getItemById(itemId);
        Item item = new Item(tbItem);
        //取商品描述資訊
        TbItemDesc tbItemDesc = itemService.getItemDescById(itemId);
        model.addAttribute("item", item);
        model.addAttribute("itemDesc", tbItemDesc);
        //返回邏輯shitu 
        return "item";
    }
}

商品詳情服務層工程(e3-manager-service)

@Service
public class ItemServiceImpl implements ItemService{

    @Autowired
    private TbItemMapper itemMapper;
    @Autowired
    private TbItemDescMapper descMapper;
    //@Autowired訊息佇列的時候使用
    //private JmsTemplate jmsTemplate;
    //@Resource
    //private Destination topicDestination;
    @Autowired
    private JedisClient jedisClient;
    @Value("${REDIS_ITEM_PRE}")
    private String REDIS_ITEM_PRE;
    @Value("${ITEM_CACHE_EXPIRE}")
    private Integer ITEM_CACHE_EXPIRE;
    public TbItem getItemById(Long itemId) {
        //查詢快取
        try {
            String string = jedisClient.get(REDIS_ITEM_PRE+":"+itemId+":BASE");
            if(StringUtils.isNotBlank(string)){
                TbItem tbItem = JsonUtils.jsonToPojo(string, TbItem.class);
                return tbItem;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //快取中沒有,查詢資料庫
        //設定查詢條件
        TbItemExample example = new TbItemExample();
        Criteria criteria = example.createCriteria();
        criteria.andIdEqualTo(itemId);
        //執行查詢
        List<TbItem> list = itemMapper.selectByExample(example);
        if(list != null && list.size()>0){
            //把結果新增到快取
            try {
    jedisClient.set(REDIS_ITEM_PRE+":"+itemId+":BASE", JsonUtils.objectToJson(list.get(0)));
                //設定過期時間
    jedisClient.expire(REDIS_ITEM_PRE+":"+itemId+":BASE", ITEM_CACHE_EXPIRE);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return list.get(0); 
        }else{
            return null;
        }
    }
    /*
     * 根據商品id取商品描述
     */
    public TbItemDesc getItemDescById(long itemId) {
        //查詢快取
        try {
            String string = jedisClient.get(REDIS_ITEM_PRE+":"+itemId+":DESC");
            if(StringUtils.isNotBlank(string)){
                TbItemDesc tbItemDesc = JsonUtils.jsonToPojo(string, TbItemDesc.class);
                return tbItemDesc;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //快取中沒有,查詢資料庫
        TbItemDesc itemDesc = descMapper.selectByPrimaryKey(itemId);
        //把結果新增到快取
        try {
    jedisClient.set(REDIS_ITEM_PRE+":"+itemId+":DESC", JsonUtils.objectToJson(itemDesc));
            //設定過期時間
    jedisClient.expire(REDIS_ITEM_PRE+":"+itemId+":DESC", ITEM_CACHE_EXPIRE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return itemDesc;
    }
    屬性描述檔案中
    REDIS_ITEM_PRE=ITEM_INFO
    ITEM_CACHE_EXPIRE=3600

這裡使用的是String型別,寫成 字首:d:字尾,這種形式在桌面版redis中檔案目錄會有層次結構。

注:對上面的程式碼不必深究,也不能單獨拿那些程式碼進行測試,主要是知道redis的使用。只要會使用jedis的方法,那麼自己進行封裝就很容易,主要還是理解業務需求,理清了需求redis部分的程式碼實現就不難了。

相關推薦

Redis入門商城案例的使用

自學那麼多月以來學到的知識點挺多幾乎每天都在接受新東西,接受的多忘的也多,想回頭再去找也不知道從哪裡找了,所以決定執行好幾個月前就決定的事情-寫部落格,用來記錄自己每次所學習到的東西。 由於自己實習的時候,自己做的專案的資料庫就是用的MySql跟Redis。所

Vuex初級入門簡單案例

1.為什麼要使用Vuex? (1)方便所有元件共享資訊,方便不同元件共享資訊。 (2)某個元件需要修改狀態和需求。   2.狀態有哪些? (1)元件內部定義的data狀態(通過元件內部修改) (2)元件外部來的pr

【機器人學:運動規劃】快速搜尋隨機樹(RRT---Rapidly-exploring Random Trees)入門在Matlab演示

  快速搜尋隨機樹(RRT -Rapidly-ExploringRandom Trees),是一種常見的用於機器人路徑(運動)規劃的方法,它本質上是一種隨機生成的資料結構—樹,這種思想自從LaValle在[1]中提出以後已經得到了極大的發展,到現在依然有改進的RRT不斷地被提出來。

Redis入門安裝

一、什麼是Redis Redis是Remote Dictionary Server(遠端資料服務)的縮寫,由義大利人antirez(Salvatore Sanflippo) 開發的一款 記憶體高速快取資料庫,該軟體使用C語言編寫,它的資料模型為(key-value),它支援

redis入門教程(更新

1.安裝使用radis 1.下載安裝: 1.下載地址:https://github.com/MSOpenTech/redis/releases 2.配置全域性路徑: 在path中加入解壓後的資料夾目錄 3.radis牛刀小試:

關於Redis入門透明(透明即使用AOP實現低耦合)快取層實現

emm...博主在前些天學習了一下Redis,以及在網上查詢了部分資料、將看到的大牛們寫的相關文章整合了一下。。(如有侵權多見諒...)分享一些經驗,可能寫的或者瞭解的不是很透徹,哈哈...大牛們請無視 那麼在講解之前我們先來簡單的瞭解一下Redis 一、什麼是Redis

Redis安裝使用過程遇到的問題

1. 虛擬機器安裝centos7,不能聯網,並且ifconfig出現command not found: 答:虛擬機器安裝centos7後,無法聯網,因為centos7預設網絡卡未啟用;而且在sbin目錄中沒有ifconfig檔案,因為centos7已經不推薦使用ifcon

Redis基礎入門

是否 解釋器 redis 單獨 示例 lease out field 發布系統 一. 什麽是 Redis Redis 是一個可基於內存,有著完備的持久化機制並以 Key-Value 形式存儲的非關系型數據庫。也稱為數據結構服務器。 二.

JsJSON.stringify()與JSON.parse()與eval()詳解使用案例

div 網絡 blog 處理 ive asc 還要 ava 不同 JSON(JavaScript Object Notation)是一種輕量級的數據交換格式。因為采用獨立於語言的文本格式,也使用了類似於C語言家族的習慣,擁有了這些特性使使JSON稱為理想的數據交換語言,作用

Redis快速入門應用

業務 出現 文件中 配置 反向 spl null 腳本 線程 Redis的使用難嗎?不難,Redis用好容易嗎?不容易。Redis的使用雖然不難,但與業務結合的應用場景特別多、特別緊,用好並不容易。我們希望通過一篇文章及Demo,即可輕松、快速入門並學會應用。

Spring Data Redis入門示例:基於Jedis底層API (二)

client classpath mode beans -name maven依賴 eas edi log 使用底層API:RedisConnectionFactory和RedisConnection可以直接操作Redis,下面是一個簡單的例子: ### Maven依賴 &

Redis入門到高可用(二)—— Redis啟動使用

CP ber eve inf 大小 div redis-cli 號碼 更改 1. 三種啟動方式 ♦? 最簡啟動 ./redis-server 使用Redis默認配置進行啟動; ♦? 動態參數啟動 * redis-server

JavaWebfilter的詳解應用案例

轉載自:http://www.cnblogs.com/vanl/p/5742501.html 一:Filter介紹   Filter可認為是Servlet的一種“變種”,它主要用於對使用者請求(HttpServletRequest)進行預處理,也可以對伺服器響應(HttpServl

RedisSpring-Data-Redis入門學習

繼上一篇Solr和Spring Data Solr學習,我們思考一個問題,使用Solr的目的是什麼?肯定是為了加快伺服器的相應速度。因為即使不適用Solr,通過請求資料庫我們一樣能完成搜尋功能,但是這樣會給伺服器造成很大的壓力。 而Solr僅僅是在搜尋功能中用到了,但是大量請求的資料不僅僅出現在

Redis學習】Redis入門安裝使用

第一份工作的時候就聽著老大說要使用Redis,當時一臉懵逼,到底什麼是Redis呢,Redis又有什麼作用的。不過還沒來得及向老大學習的時候,公司找了些理由就把所有的員工都給開了。趁著這段找工作的時間,正好可以來好好研究一下Redis了。廢話不多說,開始!

redis入門介紹社交行業應用

背景 最近工作中開始使用redis,本文就本人目前的理解對redis做一個概括性的介紹,並簡單舉例幾個工作中的應用,最後總結redis使用中的規範,期望以比較全面的方式整理redis相關知識給大家。 redis介紹 Redis(Remote Dictionary

(三) springboot的本地redis簡要配置使用案例

基於springboot開發中,對於快取資料獲取,重複或者延遲資料等,redis的使用就顯得方便很多。現在簡單的做本地redis的搭建。 (1) 下載window版本的redis安裝 (見我的資源下載)  配置:port根據要求,可改變,啟動redis-service.e

SpringBoot入門案例

一、springboot簡介 Spring Boot是用一些固定的方式來構建生產級別的spring應用。Spring Boot 推崇約定大於配置的方式以便於你能夠儘可能快速的啟動並執行程式。 簡單點說:Spring Boot 稱為搭建程式的腳手架。其最主要作用就是幫我們快

第014講:ScalaMap和HashMap原始碼剖析程式碼實踐(從1000個程式碼案例學習人工智慧和大資料實戰)

第014講:Scala中Map和HashMap原始碼剖析及程式碼實踐/** * A generic trait for immutable maps. Concrete classes have to provide * functionality for the abs