首頁  >  文章  >  資料庫  >  Redis資料庫常見的鍵值設計有哪些

Redis資料庫常見的鍵值設計有哪些

PHPz
PHPz轉載
2023-05-29 11:50:48822瀏覽

  使用者登入系統

  記錄使用者登入資訊的一個系統,我們簡化業務後只留下一張表。

  關係型資料庫的設計

  mysql>select*fromlogin;

   --------- ------------ ---- ------------- ---------------------

  |user_id|name|login_times |last_login_time|

   --------- ---------------- ------------- --- ------------------

  |1|kenthompson|5|2011-01-0100:00:00|

#  |2| dennisritchie|1|2011-02-0100:00:00|

  |3|JoeArmstrong|2|2011-03-0100:00:00|

#   ------- -- ---------------- ------------- ------------------- --

  user_id表的主鍵,name表示使用者名,login_times表示該使用者的登入次數,每次使用者登入後,login_times會自增,而last_login_time更新為目前時間。

  REDIS的設計

  關係型資料轉換為KV資料庫,我的方法如下:

  key表名:主鍵值:列名

value列值

  一般使用冒號做成分割符,這是不成文的規矩。例如在php-adminforredis系統裡,就是預設以冒號分割,於是user:1user:2等key會分成一組。於是以上的關聯式資料轉換成kv資料後記錄如下:

  Setlogin:1:login_times5

  Setlogin:2:login_times1

  ##  Setlogin:1:last_login_time2011-1-1

  Setlogin:2:last_login_time2011-2-1

  Setlogin:3:last_login_2-1

  Setlogin:3:last_login_time201-

  Setlogin:3: :name”kenthompson「

  setlogin:2:name「dennisritchie」

  setlogin:3:name」JoeArmstrong「

#如果已知主鍵,可以使用get和set方法取得或修改使用者的姓名、登入次數和最後登入時間。

  一般用戶是無法知道自己的id的,只知道自己的用戶名,所以還必須有一個從name到id的映射關係,這裡的設計與上面的有所不同。

  set"login:kenthompson:id"1

  set"login:dennisritchie:id"2

  set"login:JoeArmstrong:id"3

#  set"login:JoeArmstrong:id"3

##  set"login:JoeArmstrong:id"3

##  set"login:JoeArmstrong:id"3

  這樣每次使用者登入的時候業務邏輯如下(python版),r是redis對象,name是已經獲知的使用者名稱。

  #取得使用者的id

  uid=r.get("login:%s:id"%name)

  #自增使用者的登入次數

The following is a possible rephrased sentence: ret = r.incr("login:%s:login_times" % uid)

#  #更新該使用者的最後登入時間

  ret=r.set("login:%s:last_login_time "%uid,datetime.datetime.now())

  如果需求只是已知id,更新或取得某個使用者的最後登入時間,登入次數,關係型和kv資料庫無啥差異。一個透過btreepk,一個透過hash,效果都很好。

  假設有下列需求,找出最近登入的N個使用者。開發人員看看,還是比較簡單的,一個sql搞定。

請執行從「login」表中選擇所有列,按「last_login_time」列進行降序排序,並限制結果集大小為N

  DBA了解需求後,考慮到以後表如果比較大,所以在last_login_time上建立索引。透過從索引的最右側開始存取N筆記錄,然後進行N次回表操作,執行計劃產生了顯著的效果。

  有哪些常見Redis資料庫鍵值的設計

  過了兩天,又來一個需求,需要知道登入次數最多的人是誰。同樣的關係型如何處理?DEV說簡單

  select*fromloginorderbylogin_timesdesclimitN

  DBA一看,又要在login_time上建立一個索引。有沒有覺得有點問題呢,表上每個欄位都有素引。

問題來源在於關係型資料庫的資料儲存不夠靈活,資料只能使用一種按行排列的堆表方式進行儲存。統一的資料結構意味著你必須使用索引來改變sql的存取路徑來快速存取某個列的,而存取路徑的增加又意味著你必須使用統計資料來輔助,於是一大堆的問題就出現了。

  沒有索引,沒有統計計劃,沒有執行計劃,這就是kv資料庫。

針對取得最新的N條資料的需求,在Redis中,鍊錶的後進先出的特性非常適合。我們在上面的登入程式碼之後會加入一段程式碼,維護一個登入的鍊錶,控制他的長度,使得裡面永遠保存的是最近的N個登入使用者。

  #把目前登入人加入到鍊錶裡

  ret=r.lpush("login:last_login_times",uid)

  #保持鍊錶只有N

  ret=redis.ltrim("login:last_login_times",0,N-1)

  這樣需要取得最新登錄人的id,如下的程式碼即可#######  last_login_list=r .lrange("login:last_login_times",0,N-1)######  另外,求登入次數最多的人,對於排序,積分榜這類需求,sortedset非常的適合,我們把使用者和登入次數統一儲存在一個sortedset裡。 ###

  zaddlogin:login_times51

  zaddlogin:login_times12

  zaddlogin:login_times23

  ##」 #對該用戶的登入次數自增1

  ret=r.zincrby("login:login_times",1,uid)

  那麼如何獲得登入次數最多的用戶呢,逆序排列取的排名第N的使用者即可

  ret=r.zrevrange("login:login_times",0,N-1)

  可以看出,DEV需要新增2行程式碼,而DBA不需要考慮索引什麼的。

  TAG系統

  tag在網路應用裡尤其多見,如果以傳統的關係型資料庫來設計有點不倫不類。我們以尋找書的例子來看看redis在這方面的優勢。

  關係型資料庫的設計

  兩張表,一張book的明細,一張tag表,表示每本的tag,一本書存在多個tag。

  mysql>select*frombook;

   ------ -------------------------- ----- ----------------

  |id|name|author|

   ------ ---- --------------------------- ----------------

  | 1|TheRubyProgrammingLanguage|MarkPilgrim|

  |1|Rubyonrail|DavidFlanagan|

  |1|ProgrammingErlang|JoeArmstrong|

#   ------ ------ ------ 1|ProgrammingErlang|JoeArmstrong|

#   ------ ------ ------ -----------------------------------------

  mysql>select *fromtag;

   --------- ---------

  |tagname|book_id|

   ------ --- ---------

  |ruby|1|

  |ruby|2|

  |web|2|

#  |erlang|3|

   --------- ---------

  假如有如此需求,查找即是ruby又是web方面的書籍,如果以關係型資料庫會怎麼處理?

  selectb.name,b.authorfromtagt1,tagt2,bookb

  wheret1.tagname='web'andt2.tagname='ruby'andt1. book_id=t2.book_idandb.id=t1.book_id

  tag表自關聯2次再與book關聯,這個sql還是比較複雜的,如果要求即ruby,但不是web方面的書呢?

  關係型資料其實並不太適合這些集合運算。

  REDIS的設計

  首先book的資料肯定要儲存的,和上面一樣。

  setbook:1:name”TheRubyProgrammingLanguage”

  Setbook:2:name”Rubyonrail」

  Setbook:3:name」ProgrammingErlang」

#  Setbook:3:name」ProgrammingErlang」

#」 1:author”MarkPilgrim”

  Setbook:2:author”DavidFlanagan”

  Setbook:3:author”JoeArmstrong”

#  tag表我們使用集合來存儲數據,因為集合擅長求交集、並集

  saddtag:ruby1

  saddtag:ruby2

  saddtag:web2

  saddtag:erlang saddtag:web2

  saddtag:erlang3

##erlang ,即屬於ruby又屬於web的書?

  inter_list=redis.sinter("tag.web","tag:ruby")

  即屬於ruby,但不屬於web的書?

  inter_list=redis.sdiff("tag.ruby","tag:web")

  屬於ruby和屬於web的書的合集?

  inter_list=redis .sunion("tag.ruby","tag:web")

  簡單到不行阿。

  從以上2個例子可以看出在某些場景裡,關係型資料庫是不太適合的,你可能能夠設計出滿足需求的系統,但總是感覺的怪怪的,有種生搬硬套的感覺。 ######  尤其登入系統這個例子,頻繁的為業務建立索引。放在一個複雜的系統裡,ddl(建立索引)有可能改變執行計畫。由於業務複雜的舊系統中的SQL千奇百怪,導致其他SQL使用不同的執行計劃,因此這個問題難以預估。要求DBA對這個系統裡所有的sql都了解,這點太難了。這個問題在oracle裡尤其嚴重,每個DBA估計都碰到過。雖然現在有線上DDL方法,但對於像MySQL這樣的系統來說,DDL仍然不太方便。碰到大表,DBA凌晨爬起來在業務低高峰期操作,這事我沒少做。使用Redis來處理這種需求非常方便,只需要DBA預估容量即可。 ###

以上是Redis資料庫常見的鍵值設計有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除