node-redis使用記錄


redis的高速存取性能讓人印象深刻,雖然是分布式存儲,但相比本地內存,性能毫不遜色。

之所以能做到這點,是由於redis的“單線程,多路復用IO”,同一時刻只有一個操作在進行。

而且多次建立從redis存取數據的鏈接,操作完成后關閉,性能表現超出SQL一大截。(雖然這樣的設計某種程度上算是對redis性能的浪費。。。)

redis的nodejs版本API支持其幾乎所有命令,現對項目中涉及的記錄如下。

根據redis中的數據類型區分:

0、建立node-redis的client端連接

 1 // redis 鏈接
 2 var redis = require('redis');
 3 var client = redis.createClient('6379', '127.0.0.1');
 4 
 5 // redis 鏈接錯誤
 6 client.on("error", function(error) {
 7     console.log(error);
 8 });
 9 // redis 驗證 (reids.conf未開啟驗證,此項可不需要)
10 // client.auth("foobared");

在我使用的Nodejs V 0.10中,已附帶了node-reids API,不需要再npm安裝了。

1、set的存取

 1 client.set('key001', 'AAA', function (err, response) {
 2     if (err) {
 3         console.log("err:", err);
 4     } else {
 5         console.log(response);
 6         client.get('key001', function (err, res) {
 7             if (err) {
 8                 console.log("err:", err);
 9             } else {
10                 console.log(res);
11                 client.end();
12             }
13         });
14     }
15 });

運行結果為:

> node redistest.js
OK
AAA

 

2、hash存取

hash set的設值和抽取數據都有單個key和多個key兩種方式:

※ 設定單個key的值,在取值時獲取特定filed下指定key的值:

 1 client.hset('filed002', 'key001', 'wherethersisadoor', function (err, res) {
 2     if (err) {
 3         console.log(err);
 4     } else {
 5         console.log('res:', res);
 6         client.hget('filed002', 'key001', function (err, getRslt) {
 7             if (err) {
 8                 console.log(err);
 9             } else {
10                 console.log('getRslt:', getRslt);
11                 client.end();
12             }
13         });
14     }
15 });

運行結果如下:

> node redistest.js
res: 1
getRslt: wherethersisadoor

注意:當hget方法在指定field下找不到指定的key時,會傳給回調函數null,而非空字符或undefined。

 

※ 設定多個key的值,取值時獲取指定field下指定單個或多個key的值

 1 var qe = {a: 2, b:3, c:4};
 2 client.hmset('field003', qe, function(err, response) {
 3     console.log("err:", err);
 4     console.log("response:", response);
 5     client.hmget('field003', ['a', 'c'], function (err, res) {
 6         console.log(err);
 7         console.log(res);
 8         client.end();
 9     });
10 });

運行結果如下:

> node redistest.js
err: null
response: OK
null
[ '2', '4' ]

hmset方法的設定值可以是JSON格式的數據,但是redis中key的值是以字符串形式存儲的,如果JSON數據層數超過一層,會出現值是'[object Object]'的情況。

hmget方法的返回值是個數組,其中元素的順序對應於參數的key數組中的順序,如果參數數組中有在field內不存在的key,返回結果數組的對應位置會是null,也即無論是否能取到值,結果數組中的元素位置始終與參數的key數組中元素位置一一對應。

獲取hash中所有key的方法是client.keys(fieldname, callback); 需要注意的是如果hash中key的數目很多,這個方法的可能耗費很長時間。 

 

3、sorted sets 有序集合

有序集合是redis中一種有意思的數據結構,集合中元素是有序的,排序的依據是元素對應的score,在向有序集合中加入元素時,需要同時設定數據的值和對應的score,數據在有序集合中的存儲位置依據score確定。

score的類型被限定為浮點數,當兩個不同的元素具有相同的score時,兩者的位置按照字符串大小升序排列。

有序集合中每個元素都被限制為唯一的,向一個有序集合中設定兩個值相同而score不同的元素,只會更新已經被設定元素的score,這點需要注意。

 1 var vals = [];
 2 for (var score = 0; score < 4; score++) {
 3     for (var val = 10; val < 14; val++) {
 4         vals.push(score);
 5         vals.push(val);
 6     }
 7 }
 8 
 9 client.zadd('004', vals, function(err, res) {
10     console.log(err);
11     console.log(res);
12     client.zrange('004', 0, -1, function(err, resp) {
13         console.log(err);
14         console.log('range result:', resp);
15         client.zcount('004', -Infinity, Infinity, function(err, respo) {
16             console.log(err);
17             console.log("len:", respo);
18             client.end();
19         });
20     });
21 });

執行結果如下:

> node redistest.js
null
4
null
range result: [ '10', '11', '12', '13' ]
null
len: 4

zadd方法接收數組作為設定值的參數,數組中數據順序為[score1, key1, score2, key2,...]的形式。

參數數組中原本會有score不同的4組值,但是由於score對應的元素值相同,最終集合中僅存在一組值。

zrange方法獲取指定下標范圍的內的所有key值,包括起始位置和終止位置。

zcount方法獲取指定集合指定范圍內的元素個數,設定為-Infinity, Infinity時,可以獲取數組長度。

 

redis中的游標設計思想類似SQL中的游標,node-redis API中使用scan方法作為游標,對應不同的數據結構,有hscan和zscan等方法。

1 client.zscan('004', 0, 'COUNT', '1', function(err, res) {
2     console.log(err);
3     console.log(res);
4     console.log(res[1].length);
5     client.end();
6 });

執行結果如下:

> node redistest.js
null
[ '0', [ '10', '3', '11', '3', '12', '3', '13', '3' ] ]
8

返回結果是一個數組。數組第一個元素是游標的返回值,標明當前讀取位置,用這個值作為參數再次調用scan方法,將會從前次終止的位置繼續讀取集合。

數組第二個元素是游標讀取的內容,數組內元素格式是[key1, score1, key2, score2...]。'COUNT'參數的作用是限定scan方法的讀取數量,此處為起作用,原因未明,需繼續調查。

 

4、lists 列表

列表中的元素順序就是插入順序,元素沒有唯一性限制。

 1 client.del("003", function(err, respo) {
 2     client.rpush("003", [1, 2, 3, 4, 5], function(err, res) {
 3         console.log(err);
 4         console.log(res);
 5         client.lrange('003', 0, -1, function(err, resp) {
 6             console.log(err);
 7             console.log("resp:", resp);
 8             client.end();
 9         });
10     });
11 });

執行結果如下:

> node redistest.js
null
5
null
resp: [ '1', '2', '3', '4', '5' ]

使用sort方法可得到排序后的結果。

list的push方法有左右兩個版本,lpush和rpush,分別表示從list的頭部和尾部插入元素。

 

總結,對於redis中的大規模數據,單一數據類型並不經常可以很好的實現業務需求,可以通過不同數據類型的組合,在快速存儲數據的基礎上,快速的索引匹配數據查找。在實際使用中,用到的是將大量數據存儲在hash集合中,而hash集合的key存儲在有序集合中,提取key中的數字部分作為score,在有序集合中取出特定位置的key,再根據key去hash集合中取值。

接觸redis的時間不長,這是個很高效的存儲工具,與SQL完全不同,在擴展程序功能方面有很多可以嘗試的地方,以后在設計程序時,可以將redis因素加入考慮。

 

2016/04/10 補充

使用async並發寫入redis時,由於沒有寫入鎖定,獲取key的當前狀態和寫入數據之間的時間內,redis數據也可能發生變化,造成臟數據寫入,這點必須注意。

目前階段,解決這個問題的方案是估計並發程序的大致執行時間,如果單次並發執行時間較長,則不同並發之間在讀-寫間隔內操作同一個key的幾率降低,可以不考慮臟數據問題(暫時解決方案);而如果單次並發時間較短,則設置並發線程數量為1,限定串行執行。

 

現在接觸到的新技術,不同於經年完善的傳統方案,有些設定沒有考慮應用於新的應用領域,有些則是技術革新帶來的新型規范,雖然開發過程中都可以算作踩坑,但並不是所有出現問題的地方都一無是處,要注意平時固執於“經典”觀點,不願接受更新的惰性。


免責聲明!

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



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