随处可见的学习笔记-Redis入门

他山之石,可以攻玉

随处可见的学习笔记-Redis入门_第1张图片
题图--引自网络,侵删,请联系我

引言

前两天写了个NodeJs爬虫,很简单那种。就是爬取某个网站的首页解析所有的Url然后再爬取内页。这里就带来了一个问题。众所周知,Js是单线程异步的,并以此为卖点。可是在这个场景就有一个很明显的问题。
比如首页有30个Url它会一次性解析30个Url然后同时去请求,失败率不可谓不高。类似的场景我曾经使用过用数组来模拟栈用于控制并发,然实在不能说很优雅,各种判断,轮训,时间长了再去看不合心意,于是便想到Redis。
这是一个NoSql数据库,经常用来做缓存,因为是存在内存里的效率很高,当然也支持存进硬盘做持久化,我看上它是有一个事务,能否用这个事务来做并发控制呢?就是我来学习它的初衷,毕竟既能解决缓存又可以做并发控制岂不美哉?

初探

  1. 安装
    Redis的安装非常简单,以Linux为例:
     前往首页选择最新稳定版下载(官网下载地址: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 //开启本地服务器
       $ src/redis-cli //开启本地客户端 
       redis> set foo bar
       OK
       redis> get foo
       "bar"
       以上是官网示例
       默认端口:6379 可以通过 --port 来设置
       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
   //注意一点,默认情况下Redis只能本地访问,设置密码以后才能远端访问。
  1. 基本概念
    数据结构
    Redis有五种数据结构:String,Hash,List,Set和zset。
    **String(字符串) **
    简单的键值对:
    SET name “Redis”
    GET name
    //输出 Redis
    

** Hash(哈希)**
是个键值集合,存数据的方式,当作Js的对象来理解似乎没有太大问题。
这个估计最常用

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

List(列表)
字符串列表,按照输入顺序排序,类比Js的话,就是使用push和unpush的数组。
我觉得翻译成队列更合适。

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

Set(集合)
学术性的说法叫:String的无序集合。
在我看来..相当于先创建一个空数组,然后一个值一个值往里加,与上面的区别似乎是这是个哈系表?。

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

zset(有序集合)

自带排序的集合..
学术性的说法:不允许重复的,每一个元素关联一个double类型数的String类型元素集合。每个元素虽然不能重复,但是被用来排序的double值是可以重复的。
通俗一点就是,下标只用来排序且允许重复,每个成员都是字符串且唯一的数组....

     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. 基本操作
    上面每一种都介绍一下的话,篇幅太长,我重点看看Hash的..其它估计也都差不多,举一反三。只要会增删改查感觉基本的应用就没问题了。
  增:HMSET key name1 value1 name2 value2 ....
  删:HDEL key [name value] //允许删除整个key 或者只删除key里面的某一个值
  改:HMSET key name1 value1 name2 value2 .... //直接覆盖,和Js的对象一样..
  查:HMGET key name //获取key下某个字段的值
      HGETALL key   //获取某key所有的字段和值

一些花样

HEXISTS key name  //某Key 对应字段是否存在
HSETNX key field value //安全添加只有字段不存在的时候才会添加
HKEYS key //获取所有的关键字
HVALS key //获取所有的值

5.事务
恩,到重点了,我原本是想用这个做并发控制来着。只看描述似乎是没问题的。
而且事务似乎有Watch这个方法..检测某值如果改变则取消...
emmm..高并发的抢购活动似乎很有用,问题是,我可能需要一个,改变值才去执行的方法。
Redis似乎也有发布订阅....emmmm,太繁琐了..我可能不太需要一个观察者..
实际去阅读文档发现,这个事务执行的是Redis命令...我想想...

读取所有Url
取一部分用事务存入Reds。
再一条一条取出来下载。
下完了再执行这个步骤..
emmmm...我为什么不新建一个数组?

好吧,言归正传。
所谓事务
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
  • 一个事务从开始到执行会经历以下三个阶段:

三个阶段:
1. 开始事务。
2. 命令入队。
3. 执行事务。
注意点:
Redis的命令执行是原子性的,即不成功就失败,失败会回滚状态,不存在中间态。颇有“无胜利,毋宁死”的感觉。
但是事务不一样,虽然Redis的命令是原子性的,但是Redis的事务不是,也就是说,如果一组事务有几个失败了,成功的依旧不会回滚。
例子:

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还有发布订阅,脚本,等其它知识点,只是暂时似乎用不上,所以,在此略过不表。

在Node.js中使用Redis

Node.js有一个Redis库,直接

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

这里就能拿到一个可以执行Redis的对象。
用法示例:

//原命令 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);//正常这里会返回OK
        client.quit(); //单次操作建议退出,不退出超时也会断开。
    });

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

值得一提的是,hmset也好,HMSET也罢,它都认识..很方便。
需要注意的是,这里无论写入还是读取都是异步的,写入还好,如果是读取,恐怕需要注意使用async,await,和Promise来做同步操作。
这里我只是举了两个刚用到的列子,其它还有很多,github上写的很详细注意返回数据的时机就可以了。

结语

本以为利用Redis的事务可以做爬虫的并发控制,最后发现,并不能,如果它能在事务中执行下载的话,倒是可以做图片下载的并发控制..
还有待研究,这篇文章作为完整的入门来说并不合格,我仅仅只是发现一个问题,然后去解决,记录下来了解决过程罢了。
Node.js的Redis库暂时没看见中文文档,示例中有一些用的到的地方,看的有点费劲,程序员学好英语还是非常必要的。
关于Node.js中使用Redis等待返回,官网有个示例,不过,我是之后才去看到,所以,我用的自己的方法,但是,这个方法很蠢,一个请求创建了两个Promis,还有待优化。
简单介绍下:在Api中使用Async 创建一个Promis,await,调用一个带Async的函数,函数中掉用Client的方法,这里再来一个Promise,await。
去掉Api中的Async Promise await的话,内层掉用Client的函数会直接返回一个Promise即使没有读到return,推测是Client函数中的方法做的,经过一遍一遍的测试,最后变成了如上所述的结构。
//2018/5/30
修正这一段描述,今天看ES6的入门,这里的Promise是async返回的,猛然想到,如果去掉async是没有promise返回的,当时忽略了这一点。

做完后发现github上的示例有一个推荐写法....emmmmmm...
查文档很重要..好..那么就这样。

你可能感兴趣的:(随处可见的学习笔记-Redis入门)