使用redis實現關系型數據庫表設計


前言
最近有一個需求,設計一款文件系統,而該文件系統會對不同文件進行不同的轉碼操作,如rmvb轉碼成mp4 mp3 與m3u8格式,ppt文件轉碼為swf pdf與h5。

經過調研以后發現,如果以關系型數據庫來實現,單表會出現很多冗余字段,如上述兩種文件,需要設計6個字段來存儲相關轉碼信息,但對特定的一種文件來說,只利用了三個字段。

而如果使用分表方式可以避免冗余,單表結果變復雜,后去擴容修改都不容易,因此決定選擇一款nosql作為db,至於為什么最后選擇redis,有多方面原因,在此就不多說了

在每一個栗子中,我都會分別使用redis自帶工具的命令與php腳本完成(redis自帶工具在為安裝目錄下的redis-cli,命令不區分大小寫)

自增主鍵設計
我們都知道關系型數據庫有主鍵這個概念,常用的都是實數int類型的,為了避免重復,這個主鍵往往還是自增的,那么,而redis是使用key=>value存儲的格式,如果使用redis來設計,那么,這個自增主鍵如何來設計呢?

redis提供一個整形自增命令 incr ,每次執行將對應的key增加1 ,並返回對應key增加后的值   格式 incr key

php代碼如下:

$redis = new \Redis();
$connect = $redis->connect("127.0.0.1",6379);
$id=$redis->incr('key');

每次運行都將得到一個唯一的id,如此我們就完成了自增主鍵的設計。

字段設計
關系型數據庫中,字段是預先定一好的,也就是說你不能給一個沒有預先定義的字段中插入數據,而在redis中則沒有這個限制,即用即存就是他的優點

比如 對所有文件來說,filename(文件名稱),filepath(存儲路徑),filetype(文件類型)等字段是共有的,而對ppt文件來說額外的字段為mp4 mp3 與m3u8格式

對視頻文件來說,額外的字段為swf pdf與h5.

查詢索引設計
對於關系型數據庫來說,每個字段,都可以用來作為查詢條件,而對於redis來說,是不行的。為什么呢?上面說過,redis的字段是即用即存的,也就是說,兩條數據,

它們的字段數可能是不一樣的。如此特性決定了,redis的查詢字段都需要預先定義,而且需要選擇那些所有數據均有的字斷做為條件,在這個demo中,我們設計4個查詢

字斷,分別為 主鍵id   文件所屬公司(org_id)  文件所屬人(user_id) 文件類型(type) ,當然你也可以設計更多,這里只是demo,所以只設計4個,方便理解。

主鍵id我們已經得到了,那么其他幾個查詢條件的該如何來設計呢 ?舉個栗子,

比如我現在有A,B,C三個公司 (假設三個公司的id為  100,200,300) 
張三,李四,王五三個用戶(user_id分別111,222,333)

視頻,資料其他三種類型文件(type分別為1,2,3)

在這里需要向大家講解下redis的一種數據類型----集合,准確點來說是無序集合,集合成員是唯一的,這就意味着集合中不能出現重復的數據,

Redis 中 集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是O(1)。集合中最大的成員數為 232 - 1 我們使用多個集合來保存符合每種條件的紀錄主鍵信息

首先假設 A公司的張三新建了一個視頻文件     根據自增運算后 這條記錄的id=1

由於是第一次插入,並不存在查詢條件的集合
我們定義如下 公司集合的key為      file_org_公司id  ,那么對應A公司key就為 file_org_100 ,然后我們向這個key對應的集合中插入這條紀錄的id

集合的插入命令為 sadd key  value

php代碼
$redis->Sadd('file_org_100',1);//1為這條紀錄的主鍵id

對於用戶集合而言  我們定義key為  file_user_用戶id  那么對張三而言  key為  file_user_111

對於類型集合而言  我們定義key為  fiel_type_類型id  那么對於視頻文件來說  key為 file_type_1

我們分別將三個集合寫入完畢,后面將詳細說明,如何使用這三個集合來做為查詢條件

數據寫入
到目前為止,我們已經生成了自增主鍵,查詢索引,該輪到數據寫入了,在這里需要再介紹redis的一種數據結構 哈希(hash)

Redis hash 是一個string類型的field和value的映射表,hash特別適合用於存儲對象。

Redis 中每個 hash 可以存儲 232 - 1 鍵值對(40多億)

哈希是字段與值的的映射關系,所以,字段是不能重復的,而我們剛剛得到了要插入紀錄的id與信息,那么一個字斷與值的映射關系也就得到了

為id   映射   信息                  由於id是唯一的,所以不會存在字斷重復的問題 集合上面張三要寫入的這條信息

我們已經知道這條信息的id為1     而寫入信息也已得知  假設為(filename,type,org_id,user_id,add_time,mp4_url,m3u8_url)等

那么我們就可以向這個集合中寫入數據了   使用命令 hset key 字段名  字斷值      這個hash的名字我們就叫做file吧(理解成關系型數據庫中的表名)

php代碼

$redis->hSet('file',1,json_encode(['id'=>$id,'filename'=>$filename,...]));//為了方便后面的使用,可以將id也存進去
到此,我們一條完成的數據寫入就成功了 使用 hget key 字段名 可以查看剛剛插入的那條紀錄    詳細命令為hget file 1
file為表明 而1是$id=1的字段

事務操作
我們知道,關系型數據庫中提供一種數據看支持,叫做事務

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
事務是一個原子操作:事務中的命令要么全部被執行,要么全部都不執行。

對上述而言  我們一共有5個操作     1.獲取自增主鍵  2-4.插入查詢條件集合  5.寫入數據哈希
如何保證我們的這五步操作都執行成功,而不會出現數據寫入但查詢條件沒有的情況?

redis的設計者早已想到了這點,所以,redis也是支持事務的

命令  multi   開啟事務    exec  執行事務   discard  放棄事務
與關系型數據不同的是,redis放棄事務並不等同與回滾,我們需要自己收拾爛攤子,也就是說,假設1-4步都執行成功  第五步失敗,此時執行discard命令 1-4步的修改已經生

效,所以我們需要手動刪除2-4中插入的數據  而自增的這個主鍵,將永遠不會被再次使用了,但是,如果我們能合理規范鍵名,數據格式,完全可以避免這類問題,這也是redis

事務快速的原因。

php代碼

$redis->multi();//開啟

$id=$redis->incr('key');//獲取自增id

$redis->Sadd('file_org_100',1);//插入查詢條件

$redis->exec();//執行命令$redis->discard();放棄命令

注意 php調用exec得到的結果是一個數組     數據元素為每步操作的返回值(0和1) 而某些操作正確的返回值為0 比如修改替換 所以判斷執行是否成功需要注意執行順序

條件查詢

主鍵查詢我上邊已經告訴過大家了 hget file 1 下面我們來說說說如何來進行條件查詢

再這之前,你可以先聯系聯系插入與事務,盡量多的給數據庫中寫入數據(ps:就一條數據怎么條件查詢啊?)

我們已經定義好了三個集合  高中的數學課程中  我們因該有個概念叫做交集 並集 差集    而redis就能實現關系型數據庫實現不了的  求交集 並集 差集

假設我們現在依舊插入了10條數據了  id分別為1-10   其中屬於A機構5條 B機構的3條 C機構的2條(至於具體id是那個機構的我就隨便寫了)

那么我們現在會有三個公司集合   key分別為  file_org_100,file_org_200,file_org_300,集合中元素分別為(1,3,5,6,7),(2,4,8), (9,10)

其中是張三寫入的有4條  李四3條 王五三條  同樣的key分別為  file_user_111,file_user_222,file_user_333.集合中元素為(1,2,3,4) ,(5,6,7), (8,9,10)

其中有5個視頻  2個文件  3個其他   key分別為  file_type_1,file_type_2,file_type_3 元素為 (1,2,3,4,5),(6,7),(8,9,10)

那么  假設現在的查詢條件要查詢 A公司的紀錄

1.首先  我們跟根據A公司的集合  查詢到A公司的文件id為 1,3,5,6,7

2.然后我們去hash里面拿數據   上面介紹的是取單條數據的方法,我在這里介紹下一次取多個的方法

命令  hmget  key   id1 id2 id3 idn    實際就是  hmget file 1 3 5 6 7

對應php代碼

$redis->hmget('file',[1,3,5,6,7]);

返回值為一個數組  然后處理數組元素即可

2.那么 現在我們要查詢A公司的視頻文件有哪些 步驟如下

1.首先  我們跟根據A公司的集合  查詢到A公司的文件id為 1,3,5,6,7

2.查詢視頻文件集合 為 1,2,3,4,5

3.將兩個集合求交集  也就是滿足即是A公司的還是視頻文件

命令 sinter  第一個集合的key  第二個集合的key  第n個集合的key      返回結果為交集的值

對應的php代碼

$redis->sinter(key1,key2....);

這里我推薦先獲取各個集合,再使用php的array_intersect來獲取,這樣我們就可以再求完交集后控制每次返回的數據數量(分頁)

array_intersect(array1,array2......)

修改與刪除

修改與刪除相應簡單一些

修改就是再次執行  hset  key  id值  用新數據將老數據覆蓋

刪除就是將使用   hdel ket id值  將hash中的信息刪除掉  然后再將雞哥查詢集合中對應的數據拿出來 剔除掉要刪除條的id值后重新寫入,

當然  也可以srem key  元素值  直接刪除元素  還可以考慮有序集合,這里不多說了

持久化存儲配置與備份

關於redis的持久化存儲網上說過很多   兩種方式分別是rdb與aof

我這里使用的是aof  理解成mysql的binlog    修改redis.conf如下為止

appendonly yes  打開aof持久化

appendfsync everysec   每秒刷新到磁盤上

no-appendfsync-on-rewrite yes   在日志重寫時,不進行命令追加操作,而只是將其放在緩沖區里,避免與命令的追加 造成disk io上的沖突

auto-aof-rewrite-percentage 100   當前aof文件大小是上次日志重寫得到的aof文件大小的兩倍時,自動啟動新的日志重寫 數值表示百分比

auto-aof-rewrite-min-size 64mb 當前aof文件啟動新的日志重寫過程的最小值,避免剛剛啟動redis時由於文件尺寸較小 導致頻繁的重寫

aof是類似日志的格式  你的每一個操作都會紀錄下來  恢復時會按照順序再執行一邊 所以較rdb方式安全    但用腦袋想也知道log文件會越來越大的

好在redis提供了一種壓縮日志的方式  命令  bgrewriteaof   上面最后連個配置就是配置這個命令的觸發條件的  當然你也可以手動執行

示例代碼

php代碼 地址 https://code.csdn.net/zhangsheng_1992/redisdemo/tree/master

from:https://blog.csdn.net/zhangsheng_1992/article/details/53858271


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM