随处可见的学习笔记-Redis入门
他山之石,可以攻玉
引言
前两天写了个NodeJs爬虫,很简单那种。就是爬取某个网站的首页解析所有的Url然后再爬取内页。这里就带来了一个问题。众所周知,Js是单线程异步的,并以此为卖点。可是在这个场景就有一个很明显的问题。
比如首页有30个Url它会一次性解析30个Url然后同时去请求,失败率不可谓不高。类似的场景我曾经使用过用数组来模拟栈用于控制并发,然实在不能说很优雅,各种判断,轮训,时间长了再去看不合心意,于是便想到Redis。
这是一个NoSql数据库,经常用来做缓存,因为是存在内存里的效率很高,当然也支持存进硬盘做持久化,我看上它是有一个事务,能否用这个事务来做并发控制呢?就是我来学习它的初衷,毕竟既能解决缓存又可以做并发控制岂不美哉?
初探
安装
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 好了装完了
使用
$ 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只能本地访问,设置密码以后才能远端访问。
基本概念
数据结构
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"
基本操作
上面每一种都介绍一下的话,篇幅太长,我重点看看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"
其它
除了上面这些,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...
查文档很重要..好..那么就这样。