Redis入门二(centos上安装redis、redis客户端、reids命令、慢查询、pipline与事务、发布订阅、bitmap、HyperLogLog、GEO、持久化)

一、centos上安装redis

在这里插入图片描述

1.编译安装

	1. 先去redis官网的下载页面,左边就是源码包,我直接选择最新版的,直接右键复制链接(Download7.2.4)
		wget https://github.com/redis/redis/archive/7.2.4.tar.gz
	2. 解压
		tar -xzvf 7.2.4.tar.gz
	3. 因为是源码包,没有可执行文件,只能编译安装
		来到源码包目录下才能执行下面命令  cd redis-7.2.4/
		make && make install
		'''
		Redis 在CentOS7中部署如果出现make的问题
			原因:因为Resdis 是C实现的,需要gcc来进行编译,所以原因是系统未安装gcc:
				第一步:yum install -y gcc g++ gcc-c++ make
				第二步,如果再次执行make,如果make出现错误为:致命错误,执行下面这个命令
					make MALLOC=libc
		'''
		安装完毕后,会自动在环境变量中配置  /usr/local/bin/ 路径下会有可执行文件
	4. src目录下,就会有几个可执行文件
		redis-server--->redis服务器
	    redis-cli--->redis命令行客户端
	    redis-benchmark--->redis性能测试工具
	    redis-check-aof--->aof文件修复工具
	    redis-check-rdb--->rdb文件检查工具
	    redis-sentinel--->sentinel服务器,哨兵
	    
    5. 把src路径放到环境变量,这样在任意路径敲redis-server都能找到

2.redis-stack(可执行文件)安装

	1. 先去redis官网的下载页面,选择可执行文件,然后点击All installation options ->进入到更多下载页面中,选择复制下载链接

	2. 然后把链接切断成tar.gz结尾即可,就可以放到centos机器上,安装了
		https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz (切断的复制链接)
		wget https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz

	3. 然后解压即用
		tar -xzvf redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz 
	
	4. 名字有点长,可以给它重命名一下(只用Move移动即可)
		mv redis-stack-server-7.2.0-v9 redis-stack-server
	
	5. 配置环境变量
		cd ~ # 回家
		ls -al # 查看当前文件夹下所有文件和文件夹(包含隐藏的,环境变量就在隐藏的里面,隐藏的都是点开头)
		' .bash_profile 这个文件就是配置用户环境变量'
		vi .bash_profile
			'在进入之前先查看一下redis的bin目录 cd redis-stack-server/bin/,使用pwd查看路径/root/redis-stack-server/bin'
			方式一:
				PATH=$PATH:/root/redis-stack-server/bin   进入文件中,配置这个即可
			方式二:
				PATH=$PATH:$HOME/bin:/root/redis-stack-server/bin  直接在这句后面:/root/redis-stack-server/bin拼接即可
		
	6. 配置环境变量之后,在任意路径下敲 redis-cli、redis-server都能找到
		redis-cli
		redis-server

在这里插入图片描述

3.补充Linux中配置环境变量

	'mac/linux/win的环境变量,是一堆key-value,在操作系统整个运行过程中拿到'
   	-特殊的一个变量PATH: 在window中以 ;分割(在linux/mac中以 :分割),放了很多路径,这些路径下的可执行文件,可以在任意路径下执行
    
    -用户变量,系统变量
    	-用户环境变量--->只有当前用户能用
        -系统环境变量--->所有用户都用
    
	-windows:
    	'输出环境变量'
	    	echo %PATH%  (变量前后用百分号包裹)
	        echo %WORKON_HOME%
        '配置环境变量:点点点的直接添加即可,是图形化界面'
        
    -mac,linux
    	'输出环境变量'
        	 echo $PATH
    	-用户环境变量(每个用户家路径里)
        	cd ~ '回家'
            ls -al '查看当前文件夹下所有文件和文件夹(包含隐藏的,环境变量就在隐藏的里面,隐藏的都是点开头)'
            .bash_profile '这个文件就是配置用户环境变量'
            vi .bash_profile '编辑环境变量-->等同于windows中直接在环境变量的用户变量中新建的一个变量名和变量值(key/value)'
            source .bash_profile ' 让配置生效 (仅仅在当前窗口生效,其他窗口需要关闭重启才能生效)'
            
            
        -系统环境变量

4.redis启动

	方式一:最简单启动
		'''
			因为我使用了上面两种安装方式都安装了,所以我需要看一下启动的到底是哪个的
				方法一:whereis redis-server  # 查看redis-server安装目录,也可以查看redis-cli等
					redis-server: /root/redis-stack-server/bin/redis-server
				方法二:先查看进程号,在使用 ls -l /proc/对应的程序进程号/cwd
					ps -ef|grep redis
					ls -l /proc/54438/cwd
				方法三:which redis-server  # 同方法一一样,仅返回的没有方法一全面
					/root/redis-stack-server/bin/redis-server
		'''
		redis-server  # 启动服务
		ps -ef|grep redis  # 查看进程
		# yum install net-tools -y
		netstat -antpl|grep redis  #查看端口
		redis-cli -h ip -p port ping #命令查看

	方式二:动态参数启动
		redis-serve --port 6380  # 启动,自己设置监听6380端口
		redis-cli -p 6380  # 默认客户端是监听6379,因为我这里改了端口,所以需要指定端口
	
	方式三:配置文件启动
		#配置文件启动(6379对应手机按键MERZ,意大利女歌手Alessia Merz的名字)
		#通过redis-cli连接,输入config get * 可以获得默认配置
		#在redis目录下创建config目录,copy一个redis.conf文件
		#daemonize--》是否是守护进程启动(no|yes)
		#port---》端口号
		#logfile--》redis系统日志
		#dir--》redis工作目录

		1 cd redis-7.2.4/  #  进入到目录
		2 mv redis.conf redis.conf.bak # 备份一下原来的配置文件
		3 vi redis.conf # 在文件中写入
			daemonize yes  #是否以守护进程启动
			pidfile /var/run/redis.pid  #进程号的位置,删除
			port 6379  #端口号
			dir "/root/redis-7.2.4/data"  #工作目录
			logfile 6379.log  #日志位置 
		4 redis-server ./redis.conf # 后台运行

二、redis客户端

1.配置允许远程链接

	'配置允许远程链接'
	1.使用配置文件启动redis
		daemonize yes
		bind 0.0.0.0
		protected-mode no
		requirepass 1234
		
		pidfile /var/run/redis.pid
		port 6379
		dir "/root/redis-7.2.4/data"
		logfile 6379.log

	2.后台启动服务
		redis-server ./redis.conf
	
	3.允许远程链接
		-方式一: win :redis-cli -h 192.168.200.100 -p 6379 -a 1234  # 最好不要直接把密码一并链接
						redis-cli -h 192.168.200.100 -p 6379  # 最好使用这种方式分两次链接
						auth 1234
		-方式二:resp 远程链接
	
	4.如果连不上,就关闭防火墙
		systemctl stop firewalld
    	systemctl status firewalld

2.客户端连接,常用操作

	'客户端连接,常用操作'
		有密码的情况可以两种登陆方式
			-方式一:redis-cli -h 127.0.0.1 -p 6370 -a 1324
			-方式二:先登陆,再通过auth输入密码
			ping   # 返回PONG,说明联通的
			
	'redis配置文件'
		'redis-cli进入'
		CONFIG GET *   # 390对--》100多键值--》配置信息,例如:
			-bind
		    -port
		CONFIG SET maxmemory 128M  # 设置最大使用的内存
		CONFIG set requirepass 4321  # 设置密码 ---》客户端可以改服务端密码
		# 永久生效
		CONFIG REWRITE  # 保存到配置文件

	'''
	由于redis的上面的这个操作的rewrite这个命令,再加上linux操作系统可以通过一个端口写文件,导致可以根据redis直接入侵到linux操作系统,
	这样如果别人写了一个木马,那就会自动拉起来。
	如果写入自己的签名的公钥,用自己的私钥解公钥,自己解自己,所以直接替换公钥,就是通过redis。这也就是入侵提权
	可以查看这篇文章如何入侵的:https://cloud.tencent.com/developer/article/1440296
	'''
	'redis 入侵到 linux操作系统'
		避免:
			-运行redis进程的用户不要用root
		    -运行redis的用户不允许登录

三、redis使用场景

	1  缓存系统:使用最广泛的就是缓存
	
	2  计数器:网站访问量,转发量,评论数(文章转发,商品销量,单线程模型,不会出现并发问题)
		django可以并发操作数据库,并且数据没有错乱,这是因为mysql是一个服务并且支持并发访问的,处理了并发安全的问题,所以不需要我们从程序端考虑并发安全问题
	    redis它没有锁,但是为什么不存在并发安全问题?因为redis数据读写是单线程的
	    
	3 消息队列:发布订阅,阻塞队列实现(简单的分布式,blpop:阻塞队列,生产者消费者)
		IPC 进程间通信
	    具体到项目:遇到问题发通知
	    	-1w个问题,都要发通知
	        -只要产生问题---》就把问题丢到消息队列中---》另一端起程序,发送消息
	        
	        
	4 排行榜:有序集合(阅读排行,点赞排行,推荐(销量高的,推荐))
	
	5 社交网络:很多特效跟社交网络匹配,粉丝数,关注数
	
	6 实时系统:垃圾邮件处理系统,布隆过滤器

四、redis命令(在redis-cli中操作)

1.redis通用命令

	'上一篇博客其实是写了redis的命令,不过是在python中操作,这里直接在redis-cli中操作,其实大差不差'
	1-keys 
		#打印出所有key
		keys * 
		#打印出所有以he开头的key
		keys he*
		#打印出所有以he开头,第三个字母是h到l的范围
		keys he[h-l]
		#三位长度,以he开头,?表示任意一位
		keys he?
		#keys命令一般不在生产环境中使用,生产环境key很多,时间复杂度为o(n),用scan命令
	
	
	2-dbsize   计算key的总数
		dbsize #redis内置了计数器,插入删除值该计数器会更改,所以可以在生产环境使用,时间复杂度是o(1)
	
	3-exists key 时间复杂度o(1)
		#设置a
		set a b
		#查看a是否存在
		exists a
		(integer) 1
		#存在返回1 不存在返回0
	
	4-del key  时间复杂度o(1)
		删除成功返回1,key不存在返回0
	
	5-expire key seconds  时间复杂度o(1)
		expire name 3 #3s 过期
		ttl name  #查看name还有多长时间过期
		persist name #去掉name的过期时间
	
	6-type key  时间复杂度o(1)
		type name #查看name类型,返回string
	
	7-其他
	info命令:内存,cpu,主从相关
	client list  正在连接的会话
	client kill ip:端口 # redis-cli -a 654321 client kill 10.0.0.1:64794
	
	dbsize  总共有多少个key
	flushall  清空所有
	flushdb  只清空当前库
	select 数字  选择某个库  总共16个库
	monitor  记录操作日志,夯住
	redis-cli -a 654321 monitor

2.字符串类型

	1---基本使用get,setdel
	get name       #时间复杂度 o(1)
	set name lqz   #时间复杂度 o(1)
	del name       #时间复杂度 o(1)
	
	
	2---其他使用incr,decr,incrby,decrby
	# 如果值不存在,就会创建
	incr age  #对age这个key的value值自增1
	decr age  #对age这个key的value值自减1
	incrby age 10  #对age这个key的value值增加10
	decrby age 10  #对age这个key的value值减10
	
	'统计网站访问量(单线程无竞争,天然适合做计数器)'
	
	3---set,setnx,setxx
	set name lqz  #不管key是否存在,都设置 
	setnx name lqz #key不存在时才设置(新增操作)
	set name lqz nx #同上
	set name lqz xx #key存在,才设置(更新操作)
	
	4---mget mset
	mget key1 key2 key3     #批量获取key1,key2.。。时间复杂度o(n)
	mset key1 value1 key2 value2 key3 value3    #批量设置时间复杂度o(n)
	#n次get和mget的区别
	#n次get时间=n次命令时间+n次网络时间
	#mget时间=1次网络时间+n次命令时间
	
	5---其他:getset,append,strlen
	getset name lqznb #设置新值并返回旧值 时间复杂度o(1)
	append name 666 #将value追加到旧的value 时间复杂度o(1)
	strlen name  #计算字符串长度(注意中文)  时间复杂度o(1)
	
	6---其他:incrybyfloat,getrange,setrange
	increbyfloat age 3.5  #为age自增3.5,传负值表示自减 时间复杂度o(1)
	getrange key start end #获取字符串制定下标所有的值  时间复杂度o(1)
	setrange key index value #从指定index开始设置value值  时间复杂度o(1)
	
	
	# 缓存
	# 计数器

3.hash类型

	1---hget,hset,hdel
	hget key field  #获取hash key对应的field的value 时间复杂度为 o(1)
	hset key field value #设置hash key对应的field的value值 时间复杂度为 o(1)
	hdel key field #删除hash key对应的field的值 时间复杂度为 o(1)
	
	#测试
	hset user:1:info age 23
	hget user:1:info age
	hset user:1:info name lqz
	hgetall user:1:info
	hdel user:1:info age
	
	2---hexists,hlen
	hexists key field  #判断hash key 是否存在field 时间复杂度为 o(1)
	hlen key   #获取hash key field的数量  时间复杂度为 o(1)
	hexists user:1:info name
	hlen user:1:info  #返回数量
	        
	3---hmget,hmset
	hmget key field1 field2 ...fieldN  #批量获取hash key 的一批field对应的值  时间复杂度是o(n)
	hmset key field1 value1 field2 value2  #批量设置hash key的一批field value 时间复杂度是o(n)
	
	4--hgetall,hvals,hkeys
	hgetall key  #返回hash key 对应的所有field和value  时间复杂度是o(n)
	hvals key   #返回hash key 对应的所有field的value  时间复杂度是o(n)
	hkeys key   #返回hash key对应的所有field  时间复杂度是o(n)
	'小心使用hgetall'
	
	1 计算网站每个用户主页的访问量
	hincrby course:1:info pageview count
	
	2 缓存mysql的信息,直接设置hash格式
	
	
	其他操作
	##其他操作 hsetnx,hincrby,hincrbyfloat
	hsetnx key field value #设置hash key对应field的value(如果field已存在,则失败),时间复杂度o(1)
	hincrby key field intCounter #hash key 对英的field的value自增intCounter 时间复杂度o(1)
	hincrbyfloat key field floatCounter #hincrby 浮点数 时间复杂度o(1)

4.列表类型

	1  插入操作
	#rpush 从右侧插入
	rpush key value1 value2 ...valueN  #时间复杂度为o(1~n)
	#lpush 从左侧插入
	#linsert
	linsert key before|after value newValue   #从元素value的前或后插入newValue 时间复杂度o(n) ,需要遍历列表
	linsert listkey before b java
	linsert listkey after b php
	
	2 删除操作
	lpop key #从列表左侧弹出一个item 时间复杂度o(1)
	rpop key #从列表右侧弹出一个item 时间复杂度o(1)
	
	lrem key count value
	#根据count值,从列表中删除所有value相同的项 时间复杂度o(n)
	1 count>0 从左到右,删除最多count个value相等的项
	2 count<0 从右向左,删除最多 Math.abs(count)个value相等的项
	3 count=0 删除所有value相等的项
	lrem listkey 0 a #删除列表中所有值a
	lrem listkey -1 c #从右侧删除1个c
	
	ltrim key start end #按照索引范围修剪列表 o(n)
	ltrim listkey 1 4 #只保留下表1--4的元素
	
	
	3 查询操作
	lrange key start end #包含end获取列表指定索引范围所有item  o(n)
	lrange listkey 0 2
	lrange listkey 1 -1 #获取第一个位置到倒数第一个位置的元素
	
	lindex key index #获取列表指定索引的item  o(n)
	lindex listkey 0
	lindex listkey -1
	
	llen key #获取列表长度
	
	4 修改操作
	lset key index newValue #设置列表指定索引值为newValue o(n)
	lset listkey 2 ppp #把第二个位置设为ppp
	
	5 实战
	实现timeLine功能,时间轴,微博关注的人,按时间轴排列,在列表中放入关注人的微博的即可
	
	6 其他操作
	blpop key timeout #lpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
	brpop key timeout #rpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
	
	#要实现栈的功能 先进后出
	lpush+lpop
	#实现队列功能
	lpush+rpop
	#固定大小的列表
	lpush+ltrim
	
	#消息队列
	lpush+brpop

5.集合类型(Set)

	sadd key element # 向集合key添加element(如果element存在,添加失败) o(1)
	srem key element # 从集合中的element移除掉 o(1)
	scard key # 计算集合大小
	sismember key element # 判断element是否在集合中
	srandmember key count # 从集合中随机取出count个元素,不会破坏集合中的元素 (抽奖)
	spop key # 从集合中随机弹出一个元素
	smembers key # 获取集合中所有元素 ,无序,小心使用,会阻塞住 
	
	sdiff user:1:follow user:2:follow  #计算user:1:follow和user:2:follow的差集
	sinter user:1:follow user:2:follow  #计算user:1:follow和user:2:follow的交集        
	sunion user:1:follow user:2:follow  #计算user:1:follow和user:2:follow的并集   
	
	sdiff|sinter|suion + store destkey... #将差集,交集,并集结果保存在destkey集合中
	sdiffstore xxx number1 number2
	sinterstore xxx number1 number2
	SUNIONSTORE myset myset1 myset2
	
	
	'总结'
	# 实战
	抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除
	点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中
	标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3
	给标签添加用户,关注该标签的人有哪些
	共同好友:集合间的操作
	
	# 总结4
	sadd:可以做标签相关
	spop/srandmember:可以做随机数相关
	sadd/sinter:社交相关

6.有序集合(ZSet)

	特点   有一个分值字段,来保证顺序
	key                  score                value
	user:ranking           1                   lqz
	user:ranking           99                  lqz2
	user:ranking           88                  lqz3
	
	
	'集合有序集合'
	集合:无重复元素,无序,element
	有序集合:无重复元素,有序,element+score
	
	
	'列表和有序集合'
	列表:可以重复,有序,element
	有序集合:无重复元素,有序,element+score
	

	'API使用 zset'
	zadd key score element # score可以重复,可以多个同时添加,element不能重复 o(logN) 
	
	zrem key element # 删除元素,可以多个同时删除 o(1)
	
	zscore key element # 获取元素的分数 o(1)
	
	zincrby key increScore element # 增加或减少元素的分数  o(1)
	
	zcard key # 返回元素总个数 o(1)
	
	zrank key element # 返回element元素的排名(从小到大排)
	
	zrange key 0 -1 # 返回排名,不带分数  o(log(n)+m) n是元素个数,m是要获取的值
	zrange player:rank 0 -1 withscores  # 返回排名,带分数
	
	zrangebyscore key minScore maxScore  # 返回指定分数范围内的升序元素 o(log(n)+m) n是元素个数,m是要获取的值
	zrangebyscore user:1:ranking 90 210 withscores  # 获取90分到210分的元素
	
	zcount key minScore maxScore  # 返回有序集合内在指定分数范围内的个数 o(log(n)+m)
	
	zremrangebyrank key start end  # 删除指定排名内的升序元素 o(log(n)+m)
	zremrangebyrank user:1:rangking 1 2  # 删除升序排名中1到2的元素
	        
	zremrangebyscore key minScore maxScore  # 删除指定分数内的升序元素 o(log(n)+m)
	zremrangebyscore user:1:ranking 90 210  # 删除分数90到210之间的元素
	        
	 
	
	'其他操作'
	zrevrank #从高到低排序
	zrevrange #从高到低排序取一定范围
	zrevrangebyscore #返回指定分数范围内的降序元素
	zinterstore #对两个有序集合交集
	zunionstore #对两个有序集合求并集
	
	# 实战
	排行榜:音乐排行榜,销售榜,关注榜,游戏排行榜

五、慢查询(排查redis慢的问题)

	# MySQL 中的 7 种日志介绍
	
	# 生命周期
	客户端编写命令  get name---》通过网络 ---》到服务端---》服务端执行命令【查询,修改。。】---》返回数据通过网络返回给客户端
	
	我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.
	
	慢查询发生在第三阶段
	
	客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
	
	
	
	 # 两个配置 配置慢查询
	   -只要是超过慢查询时间的命令,都会被记录
	   -后期通过记录分析,哪些命令是慢的,尽量在生成环境中避免
	    
	   -慢查询是一个队列,里面记录了,超过你设定时间的命令
	   - 通过两个配置:
		slowlog-log-slower-than  慢于多少微秒的都会被记录
		slowlog-max-len          队列长度是多少
	    
	    
	  -配置文件直接配置
	    # 设置记录所有命令
	    config set slowlog-log-slower-than 0
	    # 最多记录100条
	    config set slowlog-max-len 100
	    # 持久化到本地配置文件
	    config rewrite
	    
	    
	  -查看慢查询队列
		slowlog get 100
		slowlog len #获取慢查询队列长度
		slowlog reset #清空慢查询队列
	    
	    
	    
	# 总结:
		开启慢查询记录后---》只要超过某个时间的命令,都会被记录到慢查询队列中
	    后期我们可以通过慢查询队列中的命令,优化程序,提高redis使用效率

六、pipline(管道)与事务

	'redis 其实不支持事务,但是可以通过pipline来模拟事务,pipline只支持单实例redis,如果做集群,没有pipiline'

	redis 支持事务吗?
		不支持,可以通过pipline实现,但是集群环境不能用pipline
	
	
	'python客户端实现'
	import redis
	pool = redis.ConnectionPool(host='10.0.0.111', port=6379)
	r = redis.Redis(connection_pool=pool)
	# pipe = r.pipeline(transaction=False)
	#创建pipeline
	pipe = r.pipeline(transaction=True)
	#开启事务
	pipe.multi()
	pipe.set('name', 'lqz')
	#其他代码,可能出异常
	pipe.set('role', 'nb')
	pipe.execute()
	
	
	
	'原生redis操作,模拟事务'
	0 开启两个客户端
		在第一个客户端执行 mutil  开启事务,放到管道中一次性执行
			multi     # 开启事务
			set name lqz
			set age 18
			exec
	
		在第二个客户端去查询,如果第一个客户端没有执行exec ,查询不到第一个事务未提交的数据
		'隔离级别---》读已提交级别'
	
	
	'原生redis  通过watch+pipline(multi)  模拟乐观锁'
	0 开启两个客户端
	1 第一个客户端上 在开启事务之前,先watch
		wathc age  # 看了一眼,是10
		multi
		decr age   # 它有可能被别人操作了
		exec       
	
		另一台机器
		multi
		decr age
		exec  # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)
	
	
	'集成到python项目中实现基于redis利用redis的乐观锁,实现秒杀系统的数据同步(基于watch实现),'
	import redis
	from threading import Thread
	
	def choose(name, conn):
	    # conn.set('count',10)
	    with conn.pipeline() as pipe:
	        # 先监视,自己的值没有被修改过
	        conn.watch('count')
	        # 事务开始
	        pipe.multi()
	        old_count = conn.get('count')
	        count = int(old_count)
	        # input('我考虑一下')
	        # time.sleep(random.randint(1, 2))
	        if count > 0:  # 有库存
	            pipe.set('count', count - 1)
	
	        # 执行,把所有命令一次性推送过去
	        ret = pipe.execute()
	        print(ret)
	        if len(ret) > 0:
	            print('第%s个人抢购成功' % name)
	        else:
	            print('第%s个人抢购失败' % name)
	
	
	if __name__ == '__main__':
	    conn = redis.Redis(host='10.0.0.111', port=6379,password='654321')
	    for i in range(100):
	
	        t = Thread(target=choose, args=(i, conn))
	        t.start()

七、发布订阅

	'发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)'
	
	如果是消息队列
		生产者生产了一条消息---》只会有一个消费者消费
	如果是发布定义---》发布订阅---》观察者模式
		生产者生产了一条消息---》所有订阅生产者的消费者都会收到消息
	    
	    
	    
	  
	实际操作
		-发布者发布消息
	    publish channel01 "hello world"
	    
	    -订阅者01订阅频道 channel01 
	    	subscribe channel01 
	    -订阅者02订阅频道 channel01
	    	subscribe channel01 
	        
	        
	        
	实际用途
		-只要设计到,一个人发生变化,其他人都收到通知的情况,就可以使用发布订阅
	    
	    
    -如何用,在python中?
	   	员工1 ,员工2 关注了 张三
	    张三发送一篇文章---》文章保存到数据库了---》信号绑定一个函数--》在函数中发布消息
	    	conn.publish('user_zhangsan_new_article','文章id')
        
    启动一个程序---》等待别人发布消息---》只要别人发布了消息---》取出频道--》去mysql查看哪些员工订阅了这个频道---[员工1 ,员工2]--->发邮件通知

请添加图片描述
请添加图片描述

八、bitmap位图

	操作比特位
	  c         i        g
	01100011  01101001   01100111
	set hello big #放入key位hello 值为big的字符串
	getbit hello 0 #取位图的第0个位置,返回0
	getbit hello 1 #取位图的第1个位置,返回1 如上图
	
	我们可以直接操纵位
	setbit key offset value #给位图指定索引设置值
	setbit hello 7 1 #把hello的第7个位置设为1 这样,big就变成了cig
	
	
	独立用户统计---》统计日活---》用户量足够大--》节约内存
		-10亿用户  用户id1 2  3  10亿
	    -统计日活,只要用户登录,只要用户登录,就把用户id放到集合中
	    -晚上只需要统计一下 集合大小---》就能统计出日活
	   
	    
	   
	    -使用集合存储---1--32--1亿用户 5千万左右---》需要 200MB空间
	    	int8个比特位表示范围 -128--127之间
	        int16 
	        int32 个比特位表示范围 -231次方---231次方  2147483648
	        
	    -使用位图---12.5MB空间

九、HyperLogLog

	基于HyperLogLog算法:极小的空间完成独立数量统计,极小内存实现去重
		- 爬虫去重
	    - 黑白名单
	    - 垃圾邮件过滤
    
    1.pfadd key element # 向hyperloglog添加元素,可以同时添加多个
		pfadd urls "www.baidu.com" "www.cnblogs.com" "www.lqz.com"
		pfadd uuids "uuid1" "uuid2"

	2.pfcount key # 计算hyperloglog的独立总数
		pfcount urls  # 查询统计个数
		
	3.pfadd urls 值 # 返回0表示在,返回1 表示不在
		pfadd urls "www.baidu.com"  

	4.pfmerge 新元素名 key1 key2
		pfadd uuids1 "uuid1" "uuid2" "uuid3" "uuid4"
		pfadd uuids2 "uuid3" "uuid4" "uuid5" "uuid6"
		pfmerge uuidsall uuids1 uuids2 #合并
		pfcount uuidsall #统计个数 返回6
	
	
	总结
	百万级别独立用户统计(用户id可以不是数字),万条数据只占15k
	错误率 0.81%
	无法取出单条数据,只能统计个数

在这里插入图片描述

十、GEO

测试文件准备,5个城市纬度

城市经度纬度简称
北京116.2839.55beijing
天津117.1239.08tianjin
石家庄114.2938.02shijiazhuang
唐山118.0139.38tangshan
保定115.2938.51baoding
	GEO(地理信息定位):存储经纬度,计算两地距离,范围等
	geoadd key longitude latitude member #增加地理位置信息
	geoadd cities:locations 116.28 39.55 beijing #把北京地理信息天津到cities:locations中
	geoadd cities:locations 117.12 39.08 tianjin
	geoadd cities:locations 114.29 38.02 shijiazhuang
	geoadd cities:locations 118.01 39.38 tangshan
	geoadd cities:locations 115.29 38.51 baoding

	geopos key member #获取地理位置信息
	geopos cities:locations beijing #获取北京地理信息
	
	geodist key member1 member2 [unit]#获取两个地理位置的距离 unit:m(米) km(千米) mi(英里) ft(尺)
	geodist cities:locations beijing tianjin km #北京到天津的距离,89公里
	
	georadius key logitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
	
	georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
	#获取指定位置范围内的地理位置信息集合
	'''
	withcoord:返回结果中包含经纬度
	withdist:返回结果中包含距离中心节点位置
	withhash:返回解雇中包含geohash
	COUNT count:指定返回结果的数量
	asc|desc:返回结果按照距离中心店的距离做升序/降序排列
	store key:将返回结果的地理位置信息保存到指定键
	storedist key:将返回结果距离中心点的距离保存到指定键
	'''
	georadiusbymember cities:locations beijing 150 km
	'''
	1) "beijing"
	2) "tianjin"
	3) "tangshan"
	4) "baoding"
	'''

	geo的本质是zset类型(有序集合),因此可以使用zset的删除,删除指定的geo例如 member: zerm cities:locations beijing 

在这里插入图片描述

十一、持久化

1.介绍

	'把redis数据从内存保存到硬盘上的过程称之为持久化'
		把数据保存在硬盘上永久存储 过程叫持久化
	
	'所有的数据库,持久化方案'
	    快照:某时某刻数据的一个完成备份
	     -mysql的Dump: mysqldump -uroot -p123456 -luffy >/data/mysqlDump/mydb.sql
	     -redis的RDB:
	    写日志:任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
	      -mysql的 Binlog
	      -Redis的 AOF
	    
	 'redis 主要的两种持久化方案'
		- rdb:快照方案
	    - aof:日志方案

2.RDB持久化

	'三种方案触发rdb'
	
	方案一:
		同步方案,在redis-cli中敲save命令,就会在对应的redis文件夹下的data文件夹下生成一个dump.rdb,
		这样,如果关闭redis(shutdown),后重启数据也会存在,这样就做到了持久化。
		'注意:如果数据量很大,执行save命令就会阻塞redis,会导致其他命令都执行不了'
		reids客户端敲一个命令:save
	
	方案二:
		异步方案:在redis-cli中敲bgsave命令,同上面方法一样,但是它是一个异步方式,不会阻塞redis
		'因为在执行bgsave命令的时候,它开启了一个异步线程,进行执行持久化操作,所以不会阻塞redis'
		redis客户端敲一个命令:bgsave

	方案三:
		配置文件方案,只要符合条件,会自动生成rdb文件
		save   900        1
	    save   300        10
	    save   60         10000
	    如果60s中改变了1w条数据,自动生成rdb
	    如果300s中改变了10条数据,自动生成rdb
	    如果900s中改变了1条数据,自动生成rdb
		
	'方案三演示:'
		save 60 2  # 60s内改了2条数据,就做rdb的持久化
		dbfilename dump-6379.rdb  # 以端口号作为文件名,可能一台机器上很多reids,不会乱
		dir /bigdiskpath  # 保存路径放到一个大硬盘位置目录
		stop-writes-on-bgsave-error yes  # 出现错误停止
		rdbcompression yes  # 压缩
		rdbchecksum yes  # 校验
   	'只要在工作目录下有rdb文件,redis重启,就会加载,整个load到内存中'

但是RDB做持久化,比较耗时、耗性能,并且不可控,不小心删掉就没有了,可能会丢失数据

3.AOF持久化

AOF的三种策略

命令alwayseverysecno
优点不丢失数据每秒一次fsync,丢失1秒数据不用管
缺点IO开销大,一般的sata盘只有几百TPS丢1秒数据不可控
	'AOF方案:客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复'
	-AOF的三种策略
		日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上
		always:redis–》写命令刷新的缓冲区—》每条命令fsync到硬盘—》AOF文件
		everysec(默认值):redis——》写命令刷新的缓冲区—》每秒把缓冲区fsync到硬盘–》AOF文件
		no:redis——》写命令刷新的缓冲区—》操作系统决定,缓冲区fsync到硬盘–》AOF文件

AOF重写

随着命令的逐步写入,并发量的变大, AOF文件会越来越大,通过AOF重写来解决该问题

		'未重写'						'重写'
	set hello world  
    set hello java                  set hello hehe
    set hello hehe 
    incr counter 
    incr counter   ======>>>        incryby counter 2
    rpush mylist a 
    rpush mylist b                  rpush myslist a b c
    rpush mylist c 
    过期数据

	'本质就是把过期的,无用的,重复的,可以优化的命令,来优化 这样可以减少磁盘占用量,加速恢复速度'
   	 咱们只需要做好配置,触发aof重写后,redis会自动开启aof重写,优化 日志
   	 -配置
   	 	uto-aof-rewrite-min-size	   AOF文件重写需要尺寸
    	auto-aof-rewrite-percentage	   AOF文件增长率

AOF持久化方案(配置方案)

	'aof和rdb是能够同时开启的,不会有任何影响,aof是写日志,rdb是备份文件'

	# 在redis.cconf 配置中添加
	appendonly yes #将该选项设置为yes,打开
	appendfilename "appendonly-6379.aof" #文件保存的名字
	appendfsync everysec #采用第二种策略
	no-appendfsync-on-rewrite yes #在aof重写的时候,是否要做aof的append操作,因为aof重写消耗性能,磁盘消耗,正常aof写磁盘有一定的冲突,这段期间的数据,允许丢失

	tail -f appendonly-6379.aof.1.incr.aof   # 实时查看

	'那么具体使用rdb还是aof?'
		这个取决于公司,如果对数据要求高,不允许丢失,那就必须选择AOF。如果允许丢失,就选择RDB

在这里插入图片描述

4.混合持久化

混合持久化原理:在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾。它的目的就是为了加快恢复

	'目的是为了加快恢复'
	
	配置方案:基于AOF的,所以需要开AOF
		appendonly yes
		appendfilename "appendonly-6379.aof"
		appendfsync everysec
		no-appendfsync-on-rewrite yes
		aof-use-rdb-preamble yes # 开启了混合持久化,并不需要开启rdb也会有rdb文件

5.RDB和AOF比较

命令rdbaof
启动优先级高(挂掉重启,会加载aof的数据)
体积
恢复速度
数据安全性丢数据根据策略决定
轻重
	rdb最佳策略
		rdb关掉,主从操作时
		集中管理:按天,按小时备份数据
		主从配置,从节点打开
	
	aof最佳策略
		开:缓存和存储,大部分情况都打开,
		aof重写集中管理
		everysec:通过每秒刷新的策略
	
	最佳策略
		小分片:每个redis的最大内存为4g
		缓存或存储:根据特性,使用不通策略
		时时监控硬盘,内存,负载网络等
		有足够内存
  • 25
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值