其實,在redis中,就只有key-value這種存儲結構,如何利用這種存儲結構完成複雜的查詢呢?讓我們一起往下看
例如有以下表結構:
CREATE TABLE `student` (
`id` bigint(18) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(30) NOT NULL COMMENT '姓名',
`birth` date DEFAULT NULL COMMENT '出生日期',
`age` int(2) DEFAULT 0 COMMENT '年齡',
`clazz` varchar(30) DEFAULT NULL COMMENT '班級',
`create_tm` datetime not null DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='學生表';
表數據:
INSERT INTO `student` (`id`, `name`, `birth`, `age`, `clazz`) VALUES ('2', '王麗', '2001-02-14', '16', '高二1班');
INSERT INTO `student` (`id`, `name`, `birth`, `age`, `clazz`) VALUES ('3', '張庫', '2000-04-16', '17', '高二2班');
INSERT INTO `student` (`id`, `name`, `birth`, `age`, `clazz`) VALUES ('4', '李四', '2002-04-12', '15', '高一2班');
INSERT INTO `student` (`id`, `name`, `birth`, `age`, `clazz`) VALUES ('5', '何聲', '2000-11-23', '17', '高二1班');
以下都是使用redis的基本命令實現,不分編程語言,如果對redis命令不是很熟的朋友可以閱讀一下Redis 命令參考:http://doc.redisfans.com/
一、構建數據存儲(用途:存儲DB數據)
key值構建:data:[表名(如果表名比較長建議使用縮寫,保證唯一即可,目的是省內存)]:[主鍵] ,例:data:student:1
value值構建:json字符串,例:{"id":1,"name":"張三","birth":"2000-07-07","age":17,"clazz":"高二1班","createTm":1504856483000"}
有時候,爲了方便我們查看緩存是那臺機器什麼時候寫進redis的,可在value值中加入額外的字段,例如:{"id":1,"name":"張三","birth":"2000-07-07","age":17,"clazz":"高二1班","createTm":1504856483000","hostIp":"10.118.62.53","hostTm":"2017-03-22 10:51:22"}
以下爲我們寫入redis的數據(key -> value)
data:student:1 -> {"id":1,"name":"張三","birth":"2000-07-07","age":17,"clazz":"高二1班","createTm":1504856483000}
data:student:2 -> {"id":2,"name":"王麗","birth":"2001-02-14","age":16,"clazz":"高二1班","createTm":1504856486000}
data:student:3 -> {"id":3,"name":"張庫","birth":"2000-04-16","age":17,"clazz":"高二2班","createTm":1504856484000}
data:student:4 -> {"id":4,"name":"李四","birth":"2002-04-12","age":15,"clazz":"高一2班","createTm":1504856480000}
data:student:5 -> {"id":5,"name":"何聲","birth":"2000-11-23","age":17,"clazz":"高二1班","createTm":1504856483000}
到這步,我們可以在redis中實現了select * from student where id = ?的查詢
查詢命令:get data:student:1
二、構建索引存儲(用途:篩選數據,存儲DB數據的主鍵,所有的索引都是爲data:開頭的數據的查詢服務的)
1.全表查詢
構建全數據索引(如可不帶任何條件查詢,需構建所有數據的主鍵索引)
key值構建:idx:[表名],例:idx:student
value值構建:主鍵set集合,例:[1,2,3,4,5]
以下爲我們寫入redis的數據(key -> value)
idx:student -> [1,2,3,4,5]
到這步,我們可以在redis中實現了select * from student的查詢
查詢命令:sort idx:student get data:student:*
在redis分片集羣中,如果data:student:x[1,2,3,4,5]與idx:student不完全在同一個集羣,則不支持sort get 命令組合。查詢方式可以修改爲先查詢idx:student的內容,遍歷idx:student得到多個數據key data:student:x,再通過pipeline的方式批量獲取多個key的值(pipeline可以批量執行redis命令,減少網絡延遲時間)
1)select * from student limit 0,3;
查詢命令:sort idx:student get data:student:* limit 0 3
2.條件查詢(field = ?)設計宗旨:必填條件儘量組合起來,這樣我們才能快速定位redis的key值,從而取到對應的value值,例如查詢時班級是必填字段,那麼就不需要idx:student這個索引了
key值構建:idx:[表名]:[字段名]:[字段值],例:idx:student:clazz:高二1班
value值構建:主鍵set集合,例:[1,2,3]
以下爲我們寫入redis的數據(key -> value)
idx:student:clazz:高二1班 -> [1,2,5]
idx:student:clazz:高二2班 -> [3]
idx:student:clazz:高一2班 -> [4]
到這步,我們可以在redis中實現了select * from student where clazz = '高二1班'的查詢
查詢命令:sort idx:student:clazz:高二1班 get data:student:*
擴展:
1)select * from student where clazz in('高二1班','高二2班');
查詢命令:
1.sunionstore temp:student:64d6bf1ff8194573a65b6f26e7dc1452 idx:student:clazz:高二1班 idx:student:clazz:高二2班 將它們的並集存儲起來,例如 temp:student:64d6bf1ff8194573a65b6f26e7dc1452 [1,2,3,5]
2.sort temp:student:64d6bf1ff8194573a65b6f26e7dc1452 get data:student:*
3.del temp:student:64d6bf1ff8194573a65b6f26e7dc1452 臨時key使用完後記得刪除
2)select * from student where clazz = '高二1班' and age = 17;
查詢命令:sinterstore temp:student:64d6bf1ff8194573a65b6f26e7dc1452 idx:student:clazz:高二1班 idx:student:age:17 將它們的交集存儲起來,例如 temp:student:64d6bf1ff8194573a65b6f26e7dc1452 [1,5]
查詢條件支持模糊搜索(field like ? )注:以下所構建的索引key無法在redis刪除數據的時候移除
key值構建:idx:[表名]:[字段名],例:idx:student:clazz
value值構建:字段值hash集合,hash[key:[字段值],value:[0]],例:{{key:"高二1班",value:0},{key:"高二2班",value:0}}
以下爲我們寫入redis的數據(key -> value)
idx:student:clazz -> [{key:"高二1班",value:0},{key:"高二2班",value:0},{key:"高一2班",value:0}]
到這步,我們可以在redis中實現了select * from student where clazz like '%高二%'的查詢
查詢命令:
1.hscan idx:student:clazz 0 MATCH *高二* 取得值[高二1班,高二2班]
2.sunionstore temp:student:64d6bf1ff8194573a65b6f26e7dc1452 idx:student:clazz:高二1班 idx:student:clazz:高二2班 將它們的並集存儲起來,例如 temp:student:64d6bf1ff8194573a65b6f26e7dc1452 [1,2,3,5]
3.sort temp:student:64d6bf1ff8194573a65b6f26e7dc1452 get data:student:*
4.del temp:student:64d6bf1ff8194573a65b6f26e7dc1452 臨時key使用完後記得刪除
3.構建區間條件索引(field >= ? and field < ?)
key值構建:idx:[表名]:[字段名],例:idx:student:age
value值構建:主鍵zset集合([value:[主鍵],score:[字段值]]),例:[{value:2,score:17},{value:1,score:17} ,{value:3,score:17}]
以下爲我們寫入redis的數據(key -> value)
idx:student:age -> [{value:4,score:15},{value:2,score:16},{value:1,score:17},{value:3,score:17},{value:5,score:17}]
到這步,我們可以在redis中實現了select * from student where age >= 15 and age < 17的查詢
查詢命令:
1.zrangebyscore idx:student:age 15 (17 取得值[4,2]
2.sadd temp:student:64d6bf1ff8194573a65b6f26e7dc1452 4 2 將值存儲到臨時key
3.sort temp:student:64d6bf1ff8194573a65b6f26e7dc1452 get data:student:*
4.del temp:student:64d6bf1ff8194573a65b6f26e7dc1452 臨時key使用完後記得刪除
數據查詢準則:
1.or查詢,in查詢(field = ? or field = ?,filed in(?,?)),使用sunionstore求set集合的並集
2.and查詢(field1 = ? and field2 = ?),使用sinterstore求set集合的交集
3.區間查詢(field >= ? and field < ?),使用zrangebyscore獲取區間內的主鍵
三、構建分值(用途:排序)
排序查詢(order by field1 [desc],field2 [desc],fieldn [desc])注:多個字段排序的情況下,字段值需做定長處理,如果無法做到,則不適合使用該方法進行排序
key值構建:score:[表名]:[字段名1]:...:[字段名n]:[主鍵],例:score:student:createTm:1
value值構建:分值字符串,例:1504856483000
以下爲我們寫入redis的數據(key -> value)
score:student:createTm:1 -> 1504856483000
score:student:createTm:2 -> 1504856486000
score:student:createTm:3 -> 1504856484000
score:student:createTm:4 -> 1504856480000
score:student:createTm:5 -> 1504856483000
到這步,我們可以在redis中實現了select * from student order by create_tm [desc]的查詢
查詢命令:sort idx:student by score:student:createTm:* [desc] get data:student:*
擴展:
1)select * from student where clazz = '高二1班' order by create_tm desc;
查詢命令:sort idx:student:clazz:高二1班 by score:student:createTm:* desc get data:student:*
2)select * from student order by age,create_tm;
以下爲我們寫入redis的數據(key -> value)
score:student:age:createTm:1 -> 171504856483000
score:student:age:createTm:2 -> 161504856486000
score:student:age:createTm:3 -> 171504856484000
score:student:age:createTm:4 -> 151504856480000
score:student:age:createTm:5 -> 171504856483000
查詢命令:sort idx:student by score:student:age:createTm:* get data:student:*
在redis中,我們不僅要考慮如何將數據存儲進去,還要考慮如何將數據刪除。
四、構建索引記錄(用途:通過主鍵找到該主鍵被記錄到哪個索引集合裏)
key值構建:record:[表名]:[主鍵],例:score:student:1
value值構建:索引set集合,例:[idx:student,idx:student:age,idx:student:clazz:高二1班]
當我們要把score:student:1這條數據刪除的時候,需要把對應的索引的1移除
執行命令:
1.srem idx:student 1
2.zrem idx:student:age 1
3.srem idx:student:clazz:高二1班 1
這樣,我們的redis中纔不會有垃圾數據
五、key過期策略
1.使用zset把主鍵,過期參照字段存儲起來,例如:expire:student:createTm [{value:4,score:1504856480000},{value:1,score:1504856483000},{value:5,score:1504856483000},{value:3,score:1504856484000},{value:2,score:1504856486000}]
假如保留30天的數據,使用定時任務定期把距離當前時間30前的數據從expire:student:createTm中取出來,然後構建對應的key進行刪除。
2.使用redis鍵空間通知(keyspace notification),不瞭解的朋友可閱讀http://doc.redisfans.com/topic/notification.html
鍵空間通知主要是使用redis發佈與訂閱(pub/sub),因爲我們存儲在redis中的所有數據都是爲data:開頭的數據的查詢服務的,所以我們可以把
過期時間設在data:開頭的key上,例如:當data:student:1過期後,我們可以通過鍵空間通知回調獲得data:student:1已被redis刪除,從而我們
可以把主鍵爲1相關的數據清除掉。
最後獻上redis存儲key截圖
如想了解Redis 主從/哨兵配置,可閱讀我的文章:http://blog.csdn.net/w13528476101/article/details/70143766