Redis中
5
種數據結構的使用場景介紹
一、redis 數據結構使用場景
String——字符串
Hash——字典
List——列表
Set——集合
Sorted Set——有序集合
下面我們就來簡單說明一下它們各自的使用場景:
1
. String——字符串
String 數據結構是簡單的 key-value 類型,value 不僅可以是 String,也可以是數字(當數字類型用 Long 可以表示的時候encoding 就是整型,其他都存儲在 sdshdr 當做字符串)。使用 Strings 類型,可以完全實現目前 Memcached 的功能,並且效率更高。還可以享受 Redis 的定時持久化(可以選擇 RDB 模式或者 AOF 模式),操作日志及 Replication 等功能。除了提供與 Memcached 一樣的 get、set、incr、decr 等操作外,Redis 還提供了下面一些操作:
1
.LEN niushuai:O(
1
)獲取字符串長度
2
.APPEND niushuai redis:往字符串 append 內容,而且采用智能分配內存(每次
2
倍)
3
.設置和獲取字符串的某一段內容
4
.設置及獲取字符串的某一位(bit)
5
.批量設置一系列字符串的內容
6
.原子計數器
7
.GETSET 命令的妙用,請於清空舊值的同時設置一個新值,配合原子計數器使用
2
. Hash——字典
在 Memcached 中,我們經常將一些結構化的信息打包成 hashmap,在客戶端序列化后存儲為一個字符串的值(一般是 JSON 格式),比如用戶的昵稱、年齡、性別、積分等。這時候在需要修改其中某一項時,通常需要將字符串(JSON)取出來,然后進行反序列化,修改某一項的值,再序列化成字符串(JSON)存儲回去。簡單修改一個屬性就干這么多事情,消耗必定是很大的,也不適用於一些可能並發操作的場合(比如兩個並發的操作都需要修改積分)。而 Redis 的 Hash 結構可以使你像在數據庫中 Update 一個屬性一樣只修改某一項屬性值。
存儲、讀取、修改用戶屬性
3
. List——列表
List 說白了就是鏈表(redis 使用雙端鏈表實現的 List),相信學過數據結構知識的人都應該能理解其結構。使用 List 結構,我們可以輕松地實現最新消息排行等功能(比如新浪微博的 TimeLine )。List 的另一個應用就是消息隊列,可以利用 List 的 *PUSH 操作,將任務存在 List 中,然后工作線程再用 POP 操作將任務取出進行執行。Redis 還提供了操作 List 中某一段元素的 API,你可以直接查詢,刪除 List 中某一段的元素。
1
.微博 TimeLine
2
.消息隊列
4
. Set——集合
Set 就是一個集合,集合的概念就是一堆不重復值的組合。利用 Redis 提供的 Set 數據結構,可以存儲一些集合性的數據。比如在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。因為 Redis 非常人性化的為集合提供了求交集、並集、差集等操作,那么就可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。
1
.共同好友、二度好友
2
.利用唯一性,可以統計訪問網站的所有獨立 IP
3
.好友推薦的時候,根據 tag 求交集,大於某個 threshold 就可以推薦
5
. Sorted Set——有序集合
和Sets相比,Sorted Sets是將 Set 中的元素增加了一個權重參數 score,使得集合中的元素能夠按 score 進行有序排列,比如一個存儲全班同學成績的 Sorted Sets,其集合 value 可以是同學的學號,而 score 就可以是其考試得分,這樣在數據插入集合的時候,就已經進行了天然的排序。另外還可以用 Sorted Sets 來做帶權重的隊列,比如普通消息的 score 為
1
,重要消息的 score 為
2
,然后工作線程可以選擇按 score 的倒序來獲取工作任務。讓重要的任務優先執行。
1
.帶有權重的元素,比如一個游戲的用戶得分排行榜
2
.比較復雜的數據結構,一般用到的場景不算太多
二、redis 其他功能使用場景
1
. 訂閱-發布系統
Pub/Sub 從字面上理解就是發布(Publish)與訂閱(Subscribe),在 Redis 中,你可以設定對某一個 key 值進行消息發布及消息訂閱,當一個 key 值上進行了消息發布后,所有訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用作實時消息系統,比如普通的即時聊天,群聊等功能。
2
. 事務——Transactions
誰說 NoSQL 都不支持事務,雖然 Redis 的 Transactions 提供的並不是嚴格的 ACID 的事務(比如一串用 EXEC 提交執行的命令,在執行中服務器宕機,那么會有一部分命令執行了,剩下的沒執行),但是這個 Transactions 還是提供了基本的命令打包執行的功能(在服務器不出問題的情況下,可以保證一連串的命令是順序在一起執行的,中間有會有其它客戶端命令插進來執行)。Redis 還提供了一個 Watch 功能,你可以對一個 key 進行 Watch,然后再執行 Transactions,在這過程中,如果這個 Watched 的值進行了修改,那么這個 Transactions 會發現並拒絕執行。
|
1
. 事務的概念
事務必須服從ACID原則。ACID指的是原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)。
通俗理解,事務其實就是一系列指令的集合。
原子性:操作這些指令時,要么全部執行成功,要么全部不執行。只要其中一個指令執行失敗,所有的指令都執行失敗,數據進行回滾,回到執行指令前的數據狀態。
一致性:事務的執行使數據從一個狀態轉換為另一個狀態,但是對於整個數據的完整性保持穩定。
隔離性:在該事務執行的過程中,無論發生的任何數據的改變都應該只存在於該事務之中,對外界不存在任何影響。只有在事務確定正確提交之后,才會顯示該事務對數據的改變。其他事務才能獲取到這些改變后的數據。
持久性:當事務正確完成后,它對於數據的改變是永久性的。
2
. 並發事務導致的問題
在許多事務處理同一個數據時,如果沒有采取有效的隔離機制,那么並發處理數據時,會帶來一些的問題。
第一類丟失更新:撤銷一個事務時,把其他事務已提交的更新數據覆蓋。
小明去銀行櫃台存錢,他的賬戶里原來的余額為
100
元,現在打算存入
100
元。在他存錢的過程中,銀行年費扣了
5
元,余額只剩
95
元。突然他又想着這
100
元要用來請女朋友看電影吃飯,不打算存了。在他撤回存錢操作后,余額依然為他存錢之前的
100
元。所以那
5
塊錢到底扣了誰的?
臟讀:一個事務讀取到另一個事務未提交的更新數據。
小明的銀行卡余額里有
100
元。現在他打算用手機點一個外賣飲料,需要付款
10
元。但是這個時候,他的女朋友看中了一件衣服
95
元,她正在使用小明的銀行卡付款。於是小明在付款的時候,程序后台讀取到他的余額只有
5
塊錢了,根本不夠
10
元,所以系統拒絕了他的交易,告訴余額不足。但是小明的女朋友最后因為密碼錯誤,無法進行交易。小明非常郁悶,明明銀行卡里還有
100
元,怎么會余額不足呢?(他女朋友更郁悶。。。)
幻讀也叫虛讀:一個事務執行兩次查詢,第二次結果集包含第一次中沒有或某些行已經被刪除的數據,造成兩次結果不一致,只是另一個事務在這兩次查詢中間插入或刪除了數據造成的。
公司財務A在進行員工薪資核算業務,需要對小明的工資進行計算並錄入系統,必須查詢兩次明細信息,然后將后一次的明細信息計算總數出來。財務在第一次明細查詢時,基本工資
2000
元,全勤獎
1000
元,提成
2000
元,共計
5000
元。在進行第二次計算時,財務B突然接到通知,需要把下個月的節日福利也在這個月的工資中發放,每人
100
元。於是財務B在每個人的工資明細中又加了一條節日福利
100
元。而此時財務A獲得第二次查詢小明的工資明細后,發現工資明細變成了
4
條數據,總數是
5100
元。兩次計算結果相差
100
元,財務A奇怪這多出來的一條明細
100
元是哪里來的呢?(都怪財務B不告訴A。。。)
不可重復讀:一個事務兩次讀取同一行的數據,結果得到不同狀態的結果,中間正好另一個事務更新了該數據,兩次結果相異,不可被信任。
小明在手機上購買起購價為1W元理財產品。系統首先要判斷他的余額夠不夠購買理財產品,如果足夠再獲取當前的余額,進行申請。系統第一次讀取到小明的余額還剩1W元,剛好足夠購買產品。但是這個時候剛好他女朋友又看中了一個包包
5000
元,這次密碼終於不會再錯誤的女朋友毫不猶豫刷了小明的銀行卡買下了這個包包。但是這個系統剛好在進行第二次確認,發現小明的余額上只有
5000
元,根本不夠購買。於是系統很生氣,拒絕了小明的購買行為,告訴他,你真是個騙子!!!
第二類丟失更新:是不可重復讀的特殊情況。如果兩個事物都讀取同一行,然后兩個都進行寫操作,並提交,第一個事物所做的改變就會丟失。
小明和女朋友一起去逛街。女朋友看中了一支口紅,(對,女朋友就是用來表現買買買的)小明大方的掏出了自己的銀行卡,告訴女朋友:親愛的,隨便刷,隨便買,我坐着等你。然后小明就坐在商城座椅上玩手機,等着女朋友。這個時候,程序員的聊天群里有人推薦了一本書,小明一看,哎呀,真是本好書,還是限量發行呢,我一定更要買到。於是小明趕緊找到購買渠道,進行付款操作。而同時,小明的女朋友也在不亦樂乎的買買買,他們同時進行了一筆交易操作,但是這個時候銀行系統出了問題,當他們都付款成功后,卻發現,銀行只扣了小明的買書錢,卻沒有扣去女朋友此時交易的錢。哈哈哈,小明真是太開心了!
Spring提供了以下方法控制事務
a.編程式事務管理(基於Java編程控制,很少使用)
利用TransactionTemplate將多個DAO操作封裝起來
*b.聲明式事務管理(基於Spring的AOP配置控制)
-基於TransactionProxyFactoryBean的方式.(很少使用)
需要為每個進行事務管理的類,配置一個TransactionProxyFactoryBean進行增強.
-基於XML配置(經常使用)
一旦配置好之后,類上不需要添加任何東西。
如果Action作為目標對象切入事務,需要在<aop:config>元素里添加proxy-target-
class
=
"true"
屬性。原因是通知Spring框架采用CGLIB技術生成具有事務管理功能的Action類。
-基於注解(配置簡單,經常使用)
在applicationContext.xml中開啟事務注解配置。(applicationContext.xml中只需定義Bean並追加以下元素)
<bean id=
"txManager"
class
=
"..."
>
<property name=
"sessionFactory"
>
</property>
<tx:annotation-driven transaction-manager=
"txManager"
/>
在目標組件類中使用
@Transactional
,該標記可定義在類前或方法前。