隨處可見的學(xué)習(xí)筆記-Redis入門

他山之石,可以攻玉

題圖--引自網(wǎng)絡(luò),侵刪,請(qǐng)聯(lián)系我

引言

前兩天寫了個(gè)NodeJs爬蟲,很簡(jiǎn)單那種。就是爬取某個(gè)網(wǎng)站的首頁解析所有的Url然后再爬取內(nèi)頁。這里就帶來了一個(gè)問題。眾所周知,Js是單線程異步的,并以此為賣點(diǎn)??墒窃谶@個(gè)場(chǎng)景就有一個(gè)很明顯的問題。
比如首頁有30個(gè)Url它會(huì)一次性解析30個(gè)Url然后同時(shí)去請(qǐng)求,失敗率不可謂不高。類似的場(chǎng)景我曾經(jīng)使用過用數(shù)組來模擬棧用于控制并發(fā),然實(shí)在不能說很優(yōu)雅,各種判斷,輪訓(xùn),時(shí)間長(zhǎng)了再去看不合心意,于是便想到Redis。
這是一個(gè)NoSql數(shù)據(jù)庫(kù),經(jīng)常用來做緩存,因?yàn)槭谴嬖趦?nèi)存里的效率很高,當(dāng)然也支持存進(jìn)硬盤做持久化,我看上它是有一個(gè)事務(wù),能否用這個(gè)事務(wù)來做并發(fā)控制呢?就是我來學(xué)習(xí)它的初衷,畢竟既能解決緩存又可以做并發(fā)控制豈不美哉?

初探

  1. 安裝
    Redis的安裝非常簡(jiǎn)單,以Linux為例:
     前往首頁選擇最新穩(wěn)定版下載(官網(wǎng)下載地址:https://redis.io/download)
          $ wget http://download.redis.io/releases/redis-4.0.9.tar.gz
          $ tar xzf redis-4.0.9.tar.gz
          $ cd redis-4.0.9
          $ make
      好了裝完了  
    
  2. 使用
       $ src/redis-server //開啟本地服務(wù)器
       $ src/redis-cli //開啟本地客戶端 
       redis> set foo bar
       OK
       redis> get foo
       "bar"
       以上是官網(wǎng)示例
       默認(rèn)端口:6379 可以通過 --port 來設(shè)置
       cli 可以通過 -h 指定host地址,-p來指定端口
       然后為了方便我們可以。
       ln -s  /home/pi/redis-4.0.9/src/redis-server  /usr/bin/redis-server
       ln -s  /home/pi/redis-4.0.9/src/redis-cli /usr/bin/redis-cli
   //注意一點(diǎn),默認(rèn)情況下Redis只能本地訪問,設(shè)置密碼以后才能遠(yuǎn)端訪問。
  1. 基本概念
    數(shù)據(jù)結(jié)構(gòu)
    Redis有五種數(shù)據(jù)結(jié)構(gòu):String,Hash,List,Set和zset。
    **String(字符串) **
    簡(jiǎn)單的鍵值對(duì):
    SET name “Redis”
    GET name
    //輸出 Redis
    

** Hash(哈希)**
是個(gè)鍵值集合,存數(shù)據(jù)的方式,當(dāng)作Js的對(duì)象來理解似乎沒有太大問題。
這個(gè)估計(jì)最常用

HMSET obj name “Redis” state “Studying” 
HGGET obj name
//輸出Redis
HGET obj
//輸出 Redis
//Studying

List(列表)
字符串列表,按照輸入順序排序,類比Js的話,就是使用push和unpush的數(shù)組。
我覺得翻譯成隊(duì)列更合適。

 lpush runoob redis
 lpush runoob mongodb
 lpush runoob rabitmq
 lrange runoob 0 10
//輸出
    1) "rabitmq"
     2) "mongodb"
     3) "redis"

Set(集合)
學(xué)術(shù)性的說法叫:String的無序集合。
在我看來..相當(dāng)于先創(chuàng)建一個(gè)空數(shù)組,然后一個(gè)值一個(gè)值往里加,與上面的區(qū)別似乎是這是個(gè)哈系表?。

sadd runoob 
sadd runoob redis
sadd runoob mongodb
sadd runoob rabitmq
sadd runoob rabitmq
smembers runoob
 //輸出
 1) "redis"
 2) "rabitmq"
 3) "mongodb"

zset(有序集合)

自帶排序的集合..
學(xué)術(shù)性的說法:不允許重復(fù)的,每一個(gè)元素關(guān)聯(lián)一個(gè)double類型數(shù)的String類型元素集合。每個(gè)元素雖然不能重復(fù),但是被用來排序的double值是可以重復(fù)的。
通俗一點(diǎn)就是,下標(biāo)只用來排序且允許重復(fù),每個(gè)成員都是字符串且唯一的數(shù)組....

     zadd runoob 0 redis
     zadd runoob 0 mongodb
     zadd runoob 0 rabitmq
     zadd runoob 0 rabitmq
     ZRANGEBYSCORE runoob 0 1000
     1) "mongodb"
     2) "rabitmq"
     3) "redis"
  1. 基本操作
    上面每一種都介紹一下的話,篇幅太長(zhǎng),我重點(diǎn)看看Hash的..其它估計(jì)也都差不多,舉一反三。只要會(huì)增刪改查感覺基本的應(yīng)用就沒問題了。
  增:HMSET key name1 value1 name2 value2 ....
  刪:HDEL key [name value] //允許刪除整個(gè)key 或者只刪除key里面的某一個(gè)值
  改:HMSET key name1 value1 name2 value2 .... //直接覆蓋,和Js的對(duì)象一樣..
  查:HMGET key name //獲取key下某個(gè)字段的值
      HGETALL key   //獲取某key所有的字段和值

一些花樣

HEXISTS key name  //某Key 對(duì)應(yīng)字段是否存在
HSETNX key field value //安全添加只有字段不存在的時(shí)候才會(huì)添加
HKEYS key //獲取所有的關(guān)鍵字
HVALS key //獲取所有的值

5.事務(wù)
恩,到重點(diǎn)了,我原本是想用這個(gè)做并發(fā)控制來著。只看描述似乎是沒問題的。
而且事務(wù)似乎有Watch這個(gè)方法..檢測(cè)某值如果改變則取消...
emmm..高并發(fā)的搶購(gòu)活動(dòng)似乎很有用,問題是,我可能需要一個(gè),改變值才去執(zhí)行的方法。
Redis似乎也有發(fā)布訂閱....emmmm,太繁瑣了..我可能不太需要一個(gè)觀察者..
實(shí)際去閱讀文檔發(fā)現(xiàn),這個(gè)事務(wù)執(zhí)行的是Redis命令...我想想...

讀取所有Url
取一部分用事務(wù)存入Reds。
再一條一條取出來下載。
下完了再執(zhí)行這個(gè)步驟..
emmmm...我為什么不新建一個(gè)數(shù)組?

好吧,言歸正傳。
所謂事務(wù)
Redis 事務(wù)可以一次執(zhí)行多個(gè)命令, 并且?guī)в幸韵聝蓚€(gè)重要的保證:

  • 批量操作在發(fā)送 EXEC 命令前被放入隊(duì)列緩存。
  • 收到 EXEC 命令后進(jìn)入事務(wù)執(zhí)行,事務(wù)中任意命令執(zhí)行失敗,其余的命令依然被執(zhí)行。
  • 在事務(wù)執(zhí)行過程,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
  • 一個(gè)事務(wù)從開始到執(zhí)行會(huì)經(jīng)歷以下三個(gè)階段:

三個(gè)階段:
1. 開始事務(wù)。
2. 命令入隊(duì)。
3. 執(zhí)行事務(wù)。
注意點(diǎn):
Redis的命令執(zhí)行是原子性的,即不成功就失敗,失敗會(huì)回滾狀態(tài),不存在中間態(tài)。頗有“無勝利,毋寧死”的感覺。
但是事務(wù)不一樣,雖然Redis的命令是原子性的,但是Redis的事務(wù)不是,也就是說,如果一組事務(wù)有幾個(gè)失敗了,成功的依舊不會(huì)回滾。
例子:

redis 127.0.0.1:6379> MULTI
OK

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED

redis 127.0.0.1:6379> GET book-name
QUEUED

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6379> SMEMBERS tag
QUEUED

redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"
  1. 其它
    除了上面這些,Redis還有發(fā)布訂閱,腳本,等其它知識(shí)點(diǎn),只是暫時(shí)似乎用不上,所以,在此略過不表。

在Node.js中使用Redis

Node.js有一個(gè)Redis庫(kù),直接

npm install redis;
node
var redis = require('redis')
var client = redis.createClient(port, host); 

這里就能拿到一個(gè)可以執(zhí)行Redis的對(duì)象。
用法示例:

//原命令 hmset key name1 value1 name2 value2...
//在Js中
var mainkey = key;
vae setOBj={
    name1:value1,
    name2:value2,
    ...
}
client.hmset(mainKey,setObj,(err,res)=>{
        if(err){
          console.log(err);
         return;
        }
        console.log(res);//正常這里會(huì)返回OK
        client.quit(); //單次操作建議退出,不退出超時(shí)也會(huì)斷開。
    });

//原命令 hgetall key
var mainid = key;
client.HGETALL(mainid,(err,res)=>{
         if(err){
             console.log(err);
             return;
         }
        result=res;//這里返回的是對(duì)象。
        client.quit();
     
});

值得一提的是,hmset也好,HMSET也罷,它都認(rèn)識(shí)..很方便。
需要注意的是,這里無論寫入還是讀取都是異步的,寫入還好,如果是讀取,恐怕需要注意使用async,await,和Promise來做同步操作。
這里我只是舉了兩個(gè)剛用到的列子,其它還有很多,github上寫的很詳細(xì)注意返回?cái)?shù)據(jù)的時(shí)機(jī)就可以了。

結(jié)語

本以為利用Redis的事務(wù)可以做爬蟲的并發(fā)控制,最后發(fā)現(xiàn),并不能,如果它能在事務(wù)中執(zhí)行下載的話,倒是可以做圖片下載的并發(fā)控制..
還有待研究,這篇文章作為完整的入門來說并不合格,我僅僅只是發(fā)現(xiàn)一個(gè)問題,然后去解決,記錄下來了解決過程罷了。
Node.js的Redis庫(kù)暫時(shí)沒看見中文文檔,示例中有一些用的到的地方,看的有點(diǎn)費(fèi)勁,程序員學(xué)好英語還是非常必要的。
關(guān)于Node.js中使用Redis等待返回,官網(wǎng)有個(gè)示例,不過,我是之后才去看到,所以,我用的自己的方法,但是,這個(gè)方法很蠢,一個(gè)請(qǐng)求創(chuàng)建了兩個(gè)Promis,還有待優(yōu)化。
簡(jiǎn)單介紹下:在Api中使用Async 創(chuàng)建一個(gè)Promis,await,調(diào)用一個(gè)帶Async的函數(shù),函數(shù)中掉用Client的方法,這里再來一個(gè)Promise,await。
去掉Api中的Async Promise await的話,內(nèi)層掉用Client的函數(shù)會(huì)直接返回一個(gè)Promise即使沒有讀到return,推測(cè)是Client函數(shù)中的方法做的,經(jīng)過一遍一遍的測(cè)試,最后變成了如上所述的結(jié)構(gòu)。
//2018/5/30
修正這一段描述,今天看ES6的入門,這里的Promise是async返回的,猛然想到,如果去掉async是沒有promise返回的,當(dāng)時(shí)忽略了這一點(diǎn)。

做完后發(fā)現(xiàn)github上的示例有一個(gè)推薦寫法....emmmmmm...
查文檔很重要..好..那么就這樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容