當前位置:編程學習大全網 - 編程語言 - 如何跳出SQL這個坑

如何跳出SQL這個坑

探索之壹:Redis? What is it?

簡而言之,Redis是壹種強大的key-value數據庫,之所以強大有兩點:響應速度快(所以數據內存存儲,只在必要時寫入磁盤),特性豐富(支持多種數據類型,以及各類型上的復雜操作)。

事實上,Redis的壹個重要特性就是它並非通常意義上的數據庫,雖然稱之為數據庫是因為它可以為妳存儲和維護數據,但它並不像關系數據庫那樣提供

任何的SQL方言。不過不用擔心,Redis並不是吞噬數據的黑洞,它只是不支持SQL及相關功能,但卻提供了穩健的協議用於與之交互。

在Redis中,沒有數據表的概念,也無須關心select、join、view等操作或功能,同時也不提供類似於int或varchar的數據字段。妳面對的將是相對原始的數據集合及數據類型。

探索之二:Available datatypes

下面我們深入看下這個奇怪的數據庫是如何工作的。如上所見,Redis是基於key-value範式存儲數據,所以先來重點看下"key"的概念。

key本質上就是簡單的字符串,諸如"username"、"password"等。在定義key時,除了不能使用空格,妳可以隨意的使用普通的字

符、數字等,像".",":","_"等在定義key時都能正常使用,所以像"user_name", "user:123:age",

"user:123:username"都是不錯的key的定義方式。

不像RDBMS中的字段名稱,這裏的key是Redis中的重要組成部分,所以我們必須在處理key時多加小心。在下面的講述中,Redis並沒有

table的概念,所以像"SELECT username from users WHERE

user_id=123;"這種簡單任務都只能換種方式實現,為了達到這種目的,在Redis上,壹種方式是通過key

"user:123:username"來獲取結果value。如妳所見,key的定義中攜帶了神秘信息(像user

ids)。在Redis中,key的重要性可見壹斑。(其他key-value數據庫中key的地位也是如此。)

現在妳應該對key有了清楚的了解,下面帶妳進入可用的數據類型的神奇世界。

Strings

String是Redis中最基本的數據類型,它就是普通的二進制安全的字符串,支持最大數據長度為1Gb。

可以通過SET命令給壹個key設置String類型的數據,並可通過GET命令根據key取得結果。如果妳想存儲數字信息,像計數器,妳也可以把它存儲到String數據中,並可使用INCR和DECR對之做自增和自減操作。

Lists

List是string數據的集合,其中各數據項按插入順序排列。妳可以把list當作壹個鎖鏈(chain),所以可以在鎖鏈最左邊(鏈頭)或最右邊(鏈尾)添加壹個新的鏈結(link);當然也可以加在鎖鏈中間,但卻要打斷某個鏈結。

可能通過LPUSH和RPUSH命令給list添加數據(L:left,

R:right),通過LPOP或RPOP命令彈出元素(同時刪除該元素),也可以通過LRANGE獲取指定範圍的元素(僅返回數據,不會刪除任何元

素)。另外也可通過LSET在指定位置添加元素,但通常這種操作比簡單的LPUSH或RPUSH要慢很多。

Hashes

Hashes以簡潔的方式存儲關系更為緊密的數據。Hashes為每個存儲的key實現壹個內置的key-value對來存儲數據,例如對

於"user"這個key,它的值可以是多個字段以及與每個字符相應的值對組成數據集。如果妳熟悉像ruby或javascript等編程語言,這裏的

hashes與那些語言中的hash概念大同小異。

Sets

Sets和它在數學上的同名概念"集合"意義相同,是包含不重復元素的集合。在Redis中,這些對象變成了Redis裏的String類型。正如

妳想,sets與lists不同的地方在於:sets中的元素是無序的,並且不能重復,妳不能在sets中放進兩個相同的數據。  

可以通過SADD往set中添加數據,SREM刪除數據,或者通過SPOP返回並刪除此數據。此外還可以通過SUNION, SINTER, SDIFF命令分別實現集合上的"並集", "交集", "差集"操作。

Ordered sets

Ordered sets與sets類似,不同地方在於Ordered set中的每個元素都有壹個權重,用於與其他元素比較並排序。

當然ordered set與普通sets具有類似的操作,ZADD和ZREM分別是添加和刪除元素。Ordered set也有自己獨有的操作:ZINCR和ZSCORE,前則用於為元素的權重+1,後則則返回元素的權重值。

探索之三:Where are my tables?

使用Redis與我們之前使用的SQL數據表完全不同,沒有語言支持妳在服務器上查詢數據,這裏僅有壹些命令幫妳操作數據庫中的keys值。

Redis中的命令是數據類型敏感型的,也就是說妳不能在list上執行set命令,否則妳將得到壹個執行錯誤的提示。可以通過redis-cli或其他

妳使用的編程語言中的接口給Redis server發送命令。在下面的示例中,我們只強調命令本身,而不關註妳通過哪種方式提交給Redis

server。

想像壹下,壹個簡單的SQL數據庫表,像壹些應用中會用到的保存用戶數據的表:

id username password name surname

1  user1  pass1   Bob  Smith

2  user2  pass2   Mario Rossi

存儲數據

假如我們想把上面的數據存儲到Redis中,妳會如何在Redis中設計數據庫方案呢?也許以應用的視覺來看會更直觀壹些。使用SQL,我們在

SELECT中通過指定用戶id來獲得壹個用戶信息,換句話說就是需要有用於區分不同數據實體的方式,所以我們可以通過壹個唯壹的標識來標識和獲取用戶信

息。所以如果在redis的key中加入用戶的id信息,那麽我們的查詢需求就解決了,在redis中,數據被存儲成如下形式:

Key        Value

user:1:username  user1

user:1:password  pass1

user:1:name    Bob

user:1:surname   Smith

user:2:username  user2

user:2:password  pass2

user:2:name   Mario

user:2:surname Rossi

那麽,給出任壹個用戶id,我們就可以通過key user:id:username,user:id:password,user:id:name,user:id:surname的形式讀出用戶信息。

用戶登錄

上面的存儲形式也能用於用戶登錄,但需要壹種方式能根據username來查詢用戶的id。也就是說我們還需要在username和id之間建立聯系。這可以通過添加另外壹個redis key"user:username:id"來實現。

key      value

user:user1:id  1

user:user2:id  2

現在如果Mario Rossi想要登錄進來,我們可以通過key"user:user2:id"先查出username,進而獲得用戶的所有信息。

主鍵

在Redis中如何保證id值的唯壹性呢。在SQL中,可以通過"id int primary key

auto_increment"定義自增主鍵來實現,現在我們也需要壹種類似的方式為每個用戶生成壹個不同的id。根據前面可用的數據類型中提到的數字數

據,Redis中的方案是這樣的:創建壹個key"user:next_id",並把它作為計數器,每當要添加新用戶時,就對

key"user:next_id"執行INCR命令。

SELECT * FROM users;

下壹個面臨的問題是查詢用戶列表。也許妳認為我們上面的數據存儲已經足以查詢出用戶列表:可以先獲得"user:next_id"的當前值

counter,然後通過壹步或多步遍歷0到counter獲得用戶數據。但如果某個用戶從系統中刪除(下面會講到刪除操作),而我們會遍歷0到

counter中的所有id,這時就會有些id查詢不到任何數據。

盡管這通常不是問題,但我們不想在不存在的用戶數據上浪費時間,所以需要創建另外壹個key"user:list",其value為list或

set類型,用於存儲每壹個新增的用戶id,並在必要的時候從"user:list"中刪除該id。我更傾向於使用list,因為它可能通過LRANGE

命令實現分頁功能。

刪除用戶

還有壹個要面臨的問題是"數據完整性",看看我們在刪除用戶時會發生什麽吧。我們需要刪除每壹個對此用戶的引用,也就是說,需要刪除下面所有的key"user:id:*","user:username:id",以及"user:list"中的用戶id。

探索之四:A Simple use case

為了學習致用,我們嘗試設計壹個虛擬圖書館,並能根據主題對圖書分組。下面的例子比上面的用戶表會稍微復雜些,但妳將學會如何在Redis中處理關聯關系。

在應用中,我們需要收集圖書,並存儲他們的title,author(s), topic(s), pages, price,

ISBN和description。顯然有些圖書的作者不止壹個,並且它也許會涵蓋不同的主題(例如壹本書可以是編程主題,也可以是描述的ruby編

程)。另外壹個作者可能寫了很多本書,而壹個主題必然會包含很多本書。可以看出,這裏出現了作者和圖書、主題和圖書的多對多關系。

SQL場景

首先,我們嘗試使用SQL數據表為此種場景建數據模型,以便於我們更直觀的在Redis領域中模擬:

Books

id title         pages price   isbn     description

1 Programming Ruby  829   $26  0974514055 ruby programming language

2 Erlang Programming 496   $42   0596518188 an introduction to erlang

Authors

id name    surname

1  Dave    Thomas

2  Chad    Fowler

3  Andy    Hunt

4  Francesco  Cesarini

5  SimonThompson

Topics

id  name      description

1   programming Books about programming

2   ruby      Books about ruby

3   erlang     Books about erlang

Books-Authors

book_id author_id

1     1

1     2

1     3

2     4

2     5

Books-Topics

book_id topic_id

1     1

1     2

2     1

2     3

Redis場景

前面已經介紹了如何在Redis中存儲數據,所以這裏理解Books,Authors和Topics這三張表應該不成問題。但當面對Books-

Authors和Book-Topics這種表之間的多對多關聯時,問題變得復雜起來。下面以Topic為例來看如何解決Book與Topic之間的關

聯,壹旦對這個關系清楚了,Book與Author之間的關系也就迎刃而解了。

對於每本book,我們需要知道它屬於哪些topics;同樣對於每個topic,也要處理它包含的每本book。換句話說,對每本book,需要

壹個存儲它所關聯的topic的id列表,對於每個topic,同樣需要壹個存儲它關聯的book的id列表。這正是set大展身手的地方。我們將創建兩

個sets:"book:id:topic"和"topic:id:books",前者保存book的topics'id列表,後者存儲topic的

books'id列表。以前面SQL場景中的數據為例,圖書"Programming

Erlang"(books表中的id為2),將有壹個key為"book:2:topics",value為set類型且數據為(1,3)的數據信息;

而主題"programming"則會有壹個key為"topic:1:books",值為(1,2)的數據集。

經過分析,就得出了Redis場景下的數據模型:

Authors

Strings

- author:id

- author:id:name

- author:id:surname

Sets

- author:id:books

Lists

- authorlist

Books

Strings

- book:id

- book:id:title

- book:id:pages

- book:id:price

- book:id:isbn

- book:id:description

Sets

- books:id:authors - books:id:topics

Lists

- book:list

Topics

String

- topic:id

- topic:id:name

- topic:id:description

Sets

- topic:id:books

Lists

- topic:list

可以看出,在SQL中的多對多關聯,在Redis中可以通過兩個set來實現。妳會發現這種實現相當有用,它給我們提供了壹種可以自由獲得其他信息

的能力:可以通過對所有感興趣的"topic:id:books"集合中交集操作,從而獲得隸屬於多個主題的圖書。例如對集

合"topic:1:books"(programming主題)和"topic:2:books"(ruby主題)做交集,會得到只有壹個元素(1)的

集合,從而獲得id=1的圖書:programming Ruby。

對於這種實現,妳必須特別關註對數據的刪除操作。因為topics裏有對books的引用,同樣books裏有對topics的引用,那刪除如何操

作?以刪除books中的數據為例,首先想到的是要刪除每個key為"book:id:*"的數據,但執行此操作前需要先遍歷topics中的所有key

為"topic:id:books"的集合,並從中刪除待刪除圖書的id,當然也要從books中key為"book:list"的列表中刪除此id。如

果想刪除壹個topic,操作也很類似:從topics中刪除所有key為"topic:id:*"信息之前,需要先遍歷books中的key

為"books:id:topics"的topic

id集,並從中刪除待刪除topic的id,同時從"topic:list"列表中也刪除此id。同樣的操作對於authors壹樣適用。

探索之五:Back home

對於Redis的探索到壹段落,現在回頭看看我們的旅行包裏收獲了哪些精彩。

我們學習了Redis中的數據類型及操作命令,還有壹些其他有趣的東西。 是不是還有幾段記憶深刻的故事呢:

通過對String數據執行INCR命令解決唯壹自增主鍵問題

通過含義豐富的key:"user:username:id"處理用戶登錄場景

通過set實現數據間的多對多關聯

到此為止,Redis之旅已經結束,希望未給妳帶來不快。最後送上壹副良濟:having fun coding free software!

  • 上一篇:單片機設計制作數字電壓表
  • 下一篇:什麽是WAP上網和移動夢網?
  • copyright 2024編程學習大全網