redis

Redis的的是完全開源免費的,遵守BSD協議,是一個高性能的鍵值數據庫。是當前最熱門的的的NoSql數據庫之一,也被人們稱爲數據結構服務器。

服務器處理數據的速度,與網站速度息息相關. 但是如果網站的訪問量非常大的時候,我們的數據庫壓力就變大了。數據庫的連接池、處理數據的能力就會面臨很大的挑戰。我們日常使用的關係型數據庫中的數據,全部存儲在我們部署數據庫的機器的硬盤中。緩存就是在內存中存儲的數據備份,當數據沒有發生本質變化的時候,我們避免數據的查詢操作直接連接數據庫,而是去內存中讀取數據,這樣就大大降低了數據庫的讀寫次數,而且從內存中讀數據的速度要比從數據庫查詢要快很多。

redis是一個非關係型的數據庫(not-only-sql即nosql),以鍵值對的方式存儲數據,將數據存放在內存中,存取速度快,但是對持久化的支持不夠好,所以redis一般配合關係型數據庫使用,redis可以做分佈式緩存,用在數據量大,高併發的情況下.redis通過很多命令進行操作,而且redis不適合保存內容大的數據.

性能:我們在碰到需要執行耗時特別久,且結果不頻繁變動的SQL,就特別適合將運行結果放入緩存,這樣,後面的請求就去緩存中讀取,請求使得能夠迅速響應

併發:在大併發的情況下,所有的請求直接訪問數據庫,數據庫會出現連接異常。這個時候,就需要使用的的Redis的做一個緩衝操作,讓請求先訪問到的Redis的的,而不是直接訪問數據庫。

 

redis和mysql的數據同步

redis可以單個使用,也可以搭建集羣,redis在和spring整合使用的時候,單機版和集羣版在配置文件中的配置不同

redis的具體使用:

@Value("${INDEX_CONTENT}")

private String INDEX_CONTENT;

@Override

public TaotaoResult addContent(TbContent content) {

//補全pojo的屬性

content.setCreated( new Date());

content.setUpdated(new Date());

//插入到內容表

contentMapper.insert(content);

//同步緩存,爲什麼要同步緩存呢?當增刪改的時候,這時查詢的仍然是原來的緩存

//可以通過刪除對應的緩存信息來同步緩存(也可以通過更新緩存的方式實現)

jedisClient.hdel(INDEX_CONTENT, content.getCategoryId().toString());

return TaotaoResult.ok();

}

@Override

public List<TbContent> getContentByCid(long cid) {

//先查詢緩存

//查詢緩存不能影響正常業務邏輯

try {

//查詢緩存

String json = jedisClient.hget(INDEX_CONTENT, cid + "");

//查詢到結果,把json轉換成List返回

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);

//執行查詢

List<TbContent> list = contentMapper.selectByExample(example);

//把結果添加到緩存

try {

jedisClient.hset(INDEX_CONTENT, cid + "", JsonUtils.objectToJson(list));

} catch (Exception e) {

e.printStackTrace();

}

//返回結果

return list;

}

redis的優點

①讀寫速度快. 數據存放在內存中,數據結構類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1)

②支持豐富的數據類型,string,hash,list,set,sorted

③支持事務,而且操作都是原子性.(原子性就是事務要麼操作成功,要麼失敗回滾)

④豐富的特性:可以用於緩存,消息隊列,按key設置過期時間,到期後自動刪除

⑤支持數據持久化(將內存數據持久化到磁盤),支持AOF和RDB兩種持久化方式,從而進行數據恢復操作,可以有效地防止數據丟失

⑥支持主從(master-slave)複製來實現數據備份,主機會自動將數據同步到從機

Redis與Memcached的區別與比較

1 、Redis不僅僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。memcache支持簡單的數據類型,String。

2 、Redis支持數據的備份,即master-slave模式的數據備份。

3 、Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啓的時候可以再次加載進行使用,而Memecache把數據全部存在內存之中

4、 redis的速度比memcached快很多

5、Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的IO複用模型。

 

redis的應用場景

String

常用命令: set,get,decr,incr,mget 等。

String數據結構是簡單的key-value類型,value其實不僅可以是String,也可以是數字。
常規key-value緩存應用;

使用場景:常規key-value緩存應用。常規計數: 微博數, 粉絲數

Hash

常用命令: hget,hset,hgetall 等。

Hash是一個string類型的field和value的映射表,hash特別適合用於存儲對象。 比如我們可以Hash數據結構來存儲用戶信息,商品信息等等。

我們簡單舉個實例來描述下Hash的應用場景,比如我們要存儲一個用戶對象數據,包含以下信息:

用戶ID爲查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,如果用普通的key/value結構來存儲,主要有以下2種存儲方式:

 

 第一種方式將用戶ID作爲查找key,把其他信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項信息時,需要把整個對象取回,並且修改操作需要對併發進行保護,引入CAS等複雜問題。

 

   第二種方法是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱作爲唯一標識來取得對應屬性的值,雖然省去了序列化開銷和併發問題,但是用戶ID爲重複存儲,如果存在大量這樣的數據,內存浪費還是非常可觀的。

那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value爲一個HashMap,並提供了直接存取這個Map成員的接口,如下圖:

 

 也就是說,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。

 也就是說,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等

list就是鏈表,Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如微博的關注列表,粉絲列表,最新消息排行等功能都可以用Redis的list結構來實現。

Redis list的實現爲一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷。

使用List結構,我們可以輕鬆地實現最新消息排行等功能。List的另一個應用就是消息隊列

 

Set

常用命令:sadd,spop,smembers,sunion 等

set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的。
當你需要存儲一個列表數據,又不希望出現重複數據時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。

 

Set 就是一個集合,集合的概念就是一堆不重複值的組合。利用Redis提供的Set數據結構,可以存儲一些集合性的數據。

在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。Redis還爲集合提供了求交集、並集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。

Set是集合,是String類型的無序集合,set是通過Hashtable實現的,概念和數學中個的集合基本類似(數學中集合中的元素是可以重複的,但是set是一堆不重複值的組合),可以交集,並集,差集等等,set中的元素是沒有順序的。

 

例如:點贊點踩

使用Redis的Set數據結構存儲數據。 
當前用戶點讚的話,就將當前用戶id存入到對應點贊集合當中,同時判斷點反對集合中是否有此id值,有的話就移除; 
當前用戶點反對的話,與上面操作相反。 
頁面顯示的時候就根據當前用戶id在點贊集合和反對集合中查找,若id值在點贊集合中有對應值,就顯示1,表示當前用戶點贊;若在反對集合中有值,反對處就顯示1.

Sorted Set

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

 和Set相比,Sorted Set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列並且是插入有序的,即自動排序。

比如一個存儲全班同學成績的Sorted Set,其集合value可以是同學的學號,而score就可以是其考試得分,這樣在數據插入集合的時候,就已經進行了天然的排序。另外還可以用Sorted Set來做帶權重的隊列,比如普通消息的score爲1,重要消息的score爲2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。

舉例: 在直播系統中,實時排行信息包含直播間在線用戶列表,各種禮物排行榜,彈幕消息(可以理解爲按消息維度的消息排行榜)等信息,適合使用Redis中的SortedSet結構進行存儲。

 

Redis的併發競爭問題如何解決?

Redis爲單進程單線程模式,採用隊列模式將併發訪問變爲串行訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連接並不存在競爭,但是在Jedis客戶端對Redis進行併發訪問時會發生連接超時、數據轉換錯誤、阻塞、客戶端關閉連接等問題,這些問題均是由於客戶端連接混亂造成。對此有2種解決方法:

1.客戶端角度,爲保證每個客戶端間正常有序與Redis進行通信,對連接進行池化,同時對客戶端讀寫Redis操作採用內部鎖synchronized。 
 
  2.服務器角度,利用setnx實現鎖。

注:對於第一種,需要應用程序自己處理資源的同步,可以使用的方法比較通俗,可以使用synchronized也可以使用lock;第二種需要用到Redis的setnx命令,但是需要注意一些問題。

 

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