Redis 背景 随着互联网+大数据时代的来临,传统的关系型数据库已经不能满足中大型网站日益增长的访问量和数据量。这个时候就需要一种能够快速存取数据的组件来缓解数据库服务I/O的压力,来解决系统性能上的瓶颈
Redis Redis是一个高性能的,开源的,C语言开发的,键值对存储数据的nosql数据库。
NoSQL:not only sql,泛指非关系型数据库 Redis/MongoDB/Hbase Hadoop
关系型数据库:MySQL、oracle、SqlServer
数据库发展历史
在互联网+大数据时代来临之前,企业的一些内部信息管理系统,一个单个数据库实例就能满足系统的需求
随着系统访问用户的增多,数据量的增大,单个数据库实例已经满足不了系统的读取需求
缓存可以缓解系统的读取压力,但是数据量的写入压力持续增大
数据量再次增大,读写分离以后,主数据库的写库压力出现瓶颈、
互联网+大数据时代来临,关系型数据库不能很好的存取一些并发性高,实时性高的,并且数据格式不固定的数据
NoSQL与SQL数据库比较
适用场景不同:SQL数据库适合用于关系特别复杂的数据查询场景,nosql反之
事务:SQL对事务的支持非常完善,而nosql基本不支持事务
两者在不断的取长补短
Redis特性
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
Redis不仅仅支持简单的key-value类型的数据,同时还提供List,set等数据类型
Redis支持数据的备份
Redis作用
Redis安装与启动
Redis可以直接通过加载zip至任意位置进行使用
以下命令需要进入Redis文件夹才可以进行
1 2 3 4 5 6 7 8 查看帮助命令 redis-server --help 启动服务 redis-server.exe 链接客户端 redis-cli.ex
配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 /etc/redis/redis.conf 当redis作为守护进程运行的时候,它会写一个 pid 到 /var/run/redis.pid 文件里面。 daemonize no 监听端口号,默认为 6379,如果你设为 0 ,redis 将不在 socket 上监听任何客户端连接。 port 6379 设置数据库的数目。 databases 16 根据给定的时间间隔和写入次数将数据保存到磁盘 下面的例子的意思是: 900 秒内如果至少有 1 个 key 的值变化,则保存 300 秒内如果至少有 10 个 key 的值变化,则保存 60 秒内如果至少有 10000 个 key 的值变化,则保存 save 900 1 save 300 10 save 60 10000 监听端口号,默认为 6379,如果你设为 0 ,redis 将不在 socket 上监听任何客户端连接。 port 6379 Redis默认只允许本地连接,不允许其他机器连接 bind 127.0.0.1
Redis数据库简单使用 1 2 3 4 5 DBSIZE 查看当前数据库的key数量 keys * 查看key的内容 FLUSHDB 清空当前数据库的key的数量 FLUSHALL 清空所有库的key(慎用) exists key 判断key是否存在
Redis常用五大数据类型 Redis-string
一个key对应一个value
可以包含任何数据 但最大不超过512M
set/get/del/append/strlen
1 2 3 4 5 6 7 set ---- 设置值 get ---- 获取值 mset ---- 设置多个值 mget ---- 获取多个值 append ---- 添加字段 del ---- 删除 strlen ---- 返回字符串长度
1 2 3 4 incr ---- 增加 decr ---- 减少 incrby ----- 制定增加多少 decrby ----- 制定减少多少
1 2 3 getrange ---- 获取指定区间范围内的值,类似between....and的关系 setrange ---- 代表从第几位开始替换,下脚本从零开始 从0 -1表示全部
Redis-list 单值多value 列表是简单的字符串列表,按照插入顺序排序,可以添加一个元素列表的头部(左边)或者尾部(右边)
它的底层实际是个链表
1 2 3 4 lpush/rpush/lrange ---- 从左/从右/获取指定长度 lpush list01 1 2 3 4 5 倒序排列 rpush list02 1 2 3 4 5 正序排列 lrange list01 0 -1 获取list01 中的所有值
1 2 3 lpop/rpop ---- 移除最左/最右 lpop list01 删除元素5 rpop list01 删除元素1
1 2 lrange list01 0 -1 lindex list01 1
1 2 删N个value lrem list01 2 1 在list01中删除2个1
1 2 ltrim ---- 开始index结束index,截取指定范围的值后在赋值给key ltrim list01 0 2 截取list01 从0到2的数据在赋值给list01
rpoplpush list1 list2 将list1中最后一个压入list2中第一位
1 2 3 lrange list01 0 -1 lrange list02 0 -1 rpoplpush list1 list2
1 lset list01 0 x 将list02中第一位换成x
1 linsert list01b before x php 在x之前加字段php
Redis-Hash hash是一个键值对集合
hash是一个string类型的field和value的映射表,hash特别适合存储对象
hset/hget/hmset/hmget/hgetall/hdel
1 2 3 4 5 6 7 设值/取值/设值多个值/取多个值/取全部值/删除值 hset user id 11 hget user id hmset customer id 11 name juran age 26 hmget customer id name age 只返回相应的值 hgetall customer 返回全部 hdel user id 删除id
1 2 hexists key value 在key里面的某个值 存在返回1 ,不存在返回0
1 2 hkeys students hvals students
Redis-set 不重复集合 set是string类型的无序集合
1 2 3 4 sadd/smembers/sismember ---- 添加/查看集合/查看是否存在 sadd set01 1 2 2 3 3 去掉重复添加 smembers set01 得到set01 sismember set01 1 如果存在返回1 不存在返回0
1 2 scard ---- 获取集合里面的元素个数 scard set01
1 2 3 srem ---- 删除集合中元素 srem set01 3 SMEMBERS set01 3已经被删除掉
1 2 3 srandmembe ---- 随机出几个数 sadd set02 1 2 3 4 5 6 7 8 srandmember set02 2
1 2 spop ---- 随机出栈 spop set01
1 smove set01 set03 2 将set01中的2 移动到set03中
1 2 3 4 5 6 7 8 sadd set01 1 2 3 4 5 sadd set02 1 2 3 a b 差集 SDIFF set01 set02 返回 4 5 在第一个set中不在第二个set中 交集 SINTER set01 set02 返回 1 2 3 并集 SUNION set01 set02 返回set01 set02 中的值 去掉重复
Redis-Zset 有序集合
1 2 3 zadd zset01 60 v1 70 v2 80 v3 90 v4 100 v5 zrange zset01 0 -1 带分数返回 withscores
zrangebyscore key start end
1 2 3 4 5 6 zrangebyscore key start end----根据开始结束来取值 zrangebyscore zset01 60 70 zrangebyscore zset01 60 (90 表示不包含90 zrangebyscore zset01 60 90 limit 1 2 从第一条开始截取2条
1 2 zrem key value---- 某score下对应的value值,作用是删除元素 zrem zset01 v1
zcard/zcount key score 区间/zrank key values
1 2 3 zcard 求zset01 总条数 zcount zset01 60 90 求60-90个数 zrank zset01 v2 返回1 返回对应下角标,从0开始
Python操作Redis redispy安装及连接
1 r = redis.StrictRedis(host='localhost',port=6379,db=0)
字符串操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import redisclass TestString (object ): def __init__ (self ): self.r = redis.StrictRedis(host='192.168.75.130' ,port=6379 ) 设置值 def test_set (self ): res = self.r.set('user1' ,'juran-1' ) print(res) 取值 def test_get (self ): res = self.r.get('user1' ) print(res) 设置多个值 def test_mset (self ): d = { 'user2' :'juran-2' , 'user3' :'juran-3' } res = self.r.mset(d) 取多个值 def test_mget (self ): l = ['user2' ,'user3' ] res = self.r.mget(l) print(res) 删除 def test_del (self ): self.r.delete('user2' )
列表相关操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class TestList (object ): def __init__ (self ): self.r = redis.StrictRedis(host='192.168.75.130' ,port=6379 ) 插入记录 def test_push (self ): res = self.r.lpush('common' ,'1' ) res = self.r.rpush('common' ,'2' ) 弹出记录 def test_pop (self ): res = self.r.lpop('common' ) res = self.r.rpop('common' ) 范围取值 def test_range (self ): res = self.r.lrange('common' ,0 ,-1 ) print(res)
集合相关操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class TestSet (object ): def __init__ (self ): self.r = redis.StrictRedis(host='192.168.75.130' , port=6379 ) 添加数据 def test_sadd (self ): res = self.r.sadd('set01' ,'1' ,'2' ) lis = ['Cat' ,'Dog' ] res = self.r.sadd('set02' ,lis) 删除数据 def test_del (self ): res = self.r.srem('set01' ,1 ) 随机删除数据 def test_pop (self ): res = self.r.spop('set02' )
Hash相关操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class TestHash(object): def __init__(self): self.r = redis.StrictRedis(host='192.168.75.130', port=6379) 批量设值 def test_hset(self): dic = { 'id':1, 'name':'huawei' } res = self.r.hmset('mobile',dic) 批量取值 def test_hgetall(self): res = self.r.hgetall('mobile') 判断是否存在 存在返回1 不存在返回0 def test_hexists(self): res = self.r.hexists('mobile','id') print(res)
Scrapy-分布式 什么是scrapy_redis 1 scrapy_redis:Redis-based components for scrapy
工作流程 与正常scrapy工作不同的是
pipeline住的数据会默认存储到Redis
通过Redis实现调度器的队列和指纹集合
scrapy_redis下载 1 2 clone github scrapy_redis源码文件 git clone https://github.com/rolando/scrapy-redis.git
scrapy_redis中的settings文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # Scrapy settings for example project # # For simplicity, this file contains only the most important settings by # default. All the other settings are documented here: # # http://doc.scrapy.org/topics/settings.html # SPIDER_MODULES = ['example.spiders'] NEWSPIDER_MODULE = 'example.spiders' USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)' DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 指定那个去重方法给request对象去重 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 指定Scheduler队列 SCHEDULER_PERSIST = True # 队列中的内容是否持久保存,为false的时候在关闭Redis的时候,清空Redis #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" ITEM_PIPELINES = { 'example.pipelines.ExamplePipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400, # scrapy_redis实现的items保存到redis的pipline } LOG_LEVEL = 'DEBUG' # Introduce an artifical delay to make use of parallelism. to speed up the # crawl. DOWNLOAD_DELAY = 1
scrapy_redis运行 1 2 3 4 allowed_domains = ['dmoztools.net'] start_urls = ['http://www.dmoztools.net/'] scrapy crawl dmoz
运行结束后redis中多了三个键 1 2 3 dmoz:requests 存放的是待爬取的requests对象 获取过程是pop操作即获取一个删除一个 dmoz:item 爬取到的信息 在pipeline中开启RedisPipeline才会存入 dmoz:dupefilter 爬取的requests的指纹 存放的是已经进入schedule队列的request对象的指纹,指纹默认由请求方法,url和请求体组成
普通Scrapy爬虫改写为分布式(scrapy_redis)爬虫
创建Scrapy项目
明确爬取目标
创建爬虫项目
保存数据
改写分布式爬虫
改写爬虫文件
导入模块
继承类
把start_urls改为redis_key
改写配置文件
1 2 3 4 5 6 7 8 # 指定哪个方法去重给request对象 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 指定Scheduler的队列 SCHEDULER = "scrapy_redis.scheduler.Scheduler" SCHEDULER_PERSIST = True ITEM_PIPELINES = { 'example.pipelines.ExamplePipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400, }
爬取当当网案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import scrapyfrom copy import deepcopyfrom scrapy_redis.spiders import RedisSpider ''' - 1 改写爬虫文件 - 导入模块 - 继承类 - 把start_urls ---> redis_key - 2 改写配置文件 ''' class DangdangSpider (RedisSpider ): name = 'dangdang' allowed_domains = ['dangdang.com' ] redis_key = 'dangdang' def parse (self, response ): div_list = response.xpath("//div[@class='con flq_body']/div" ) for div in div_list: item = {} item['b_cate' ] = div.xpath("./dl/dt//text()" ).extract() item['b_cate' ] = [i.strip() for i in item['b_cate' ] if len(i.strip())>0 ] dl_list = div.xpath(".//dl[@class='inner_dl']" ) for dl in dl_list: item['m_cate' ] = dl.xpath('./dt//text()' ).extract() item['m_cate' ] = [i.strip() for i in item['m_cate' ] if len(i.strip()) > 0 ] a_list = dl.xpath('./dd/a' ) for a in a_list: item['s_cate' ] = a.xpath('./text()' ).extract_first() item['s_href' ] = a.xpath('./@href' ).extract_first() if item['s_href' ] is not None : yield scrapy.Request( url=item['s_href' ], callback=self.parse_book_list, meta={'item' :deepcopy(item)} ) def parse_book_list (self,response ): item = response.meta.get('item' ) li_list = response.xpath("//ul[@class='list_aa ']/li" ) for li in li_list: item['book_img' ] = li.xpath('./a[@class="img"]/img/@src' ).extract_first() if item['book_img' ] == 'images/model/guan/url_none.png' : item['book_img' ] = li.xpath('./a[@class="img"]/img/@data-original' ).extract_first() item['book_name' ] = li.xpath('./p[@class="name"]/a/@title' ).extract_first() yield item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" SCHEDULER = "scrapy_redis.scheduler.Scheduler" SCHEDULER_PERSIST = True DEFAULT_REQUEST_HEADERS = { 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' , 'Accept-Language' : 'en' , 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36' } ITEM_PIPELINES = { 'book.pipelines.BookPipeline' : 300 , 'scrapy_redis.pipelines.RedisPipeline' : 400 , }