Redis6
一、简介
1.key
1、使用命令 select <dbid>来切换数据库。如: select 8
3、keys *查看当前库所有key (匹配:keys *1)
3、exists key判断某个key是否存在
4、type key 查看你的key是什么类型
5、del key 删除指定的key数据
6、unlink key 根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
7、expire key 10 10秒钟:为给定的key设置过期时间
8、ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
9、select 命令切换数据库
10、dbsize 查看当前数据库的key的数量
11、flushdb 清空当前库
12、flushall 通杀全部库
13、info server 查看redis版本
二、常用五大数据类型
1.String类型
1):String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M(可以是任何数据,图片等等);
2):String的数据结构是可以修改的字符串,内部结构类似于java的List,当字符串小于1M时,都是加倍扩容,超过1M每次只会扩容1M,最大为512M
常用指令
1、set <key><value>添加键值对
*NX:当数据库中key不存在时,可以将key-value添加数据库
*XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
*EX:key的超时秒数
*PX:key的超时毫秒数,与EX互斥
2、get <key>查询对应键值
3、append <key><value>将给定的<value> 追加到原值的末尾
4、strlen <key>获得值的长度
5、setnx <key><value>只有在 key 不存在时 设置 key 的值
6、incr <key>
将 key 中储存的数字值增1
只能对数字值操作,如果为空,新增值为1
7、decr <key>
将 key 中储存的数字值减1
只能对数字值操作,如果为空,新增值为-1
8、incrby / decrby <key><步长>将 key 中储存的数字值增减。自定义步长。
9、mset <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value对
10、mget <key1><key2><key3> .....同时获取一个或多个 value
11、msetnx <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
12、getrange <key><起始位置><结束位置> 获得值的范围,类似java中的substring,前包,后包
13、setrange <key><起始位置><value> 用 <value> 覆写<key>所储存的字符串值,从<起始位置>开始(索引从0开始)。
14、setex <key><过期时间><value> 设置键值的同时,设置过期时间,单位秒。
15、getset <key><value> 以新换旧,设置了新值同时获得旧值。
2.List
1):底层是个双向链表(快速链表quickList)
2):数据较少时,会使用一块连续内存,结构为ziplist(压缩列表)。当数据很多时会改成quickList
3):普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
常用指令
1、lpush/rpush <key><value1><value2><value3> .... 从左边/右边插入一个或多个值。
2、lpop/rpop <key>从左边/右边吐出一个值。值在键在,值光键亡。
3、rpoplpush <key1><key2>从<key1>列表右边吐出一个值,插到<key2>列表左边。
4、lrange <key><start><stop> 按照索引下标获得元素(从左到右)
5、lrange mylist 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
6、lindex <key><index>按照索引下标获得元素(从左到右)
7、len <key>获得列表长度
8、linsert <key> before <value><newvalue> 在<value>的后面插入<newvalue>插入值
9、lrem <key><n><value> 从左边删除n个value(从左到右)
10、lset<key><index><value> 将列表key下标为index的值替换成value
3.set
1):无序、自动去重、提供了判断某个成员是否在一个set集合内的重要接口(List不能提供)
2):底层是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。即数据增加,查找数据的时间不变
3):Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
常用指令
1、sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
2、smembers <key>取出该集合的所有值。
3、sismember <key><value>判断集合<key>是否为含有该<value>值,有1,没有0
4、scard<key>返回该集合的元素个数。
5、srem <key><value1><value2> .... 删除集合中的某个元素。
6、spop <key>随机从该集合中吐出一个值,吐完会删除。
7、srandmember <key><n>随机从该集合中取出n个值。不会从集合中删除 。
8、smove <source><destination>value把集合中一个值从一个集合移动到另一个集合
9、sinter <key1><key2>返回两个集合的交集元素。
10、sunion <key1><key2>返回两个集合的并集元素。
11、sdiff <key1><key2>返回两个集合的差集元素(key1中的,不包含key2中的)
4.Hash
1):hash是一个string类型的field和value的映射表,hash特别适合用于存储对象,类似Java里面的Map<String,Object>
2):Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
常用指令
1、hset <key><field><value>给<key>集合中的 <field>键赋值<value>
2、hget <key1><field>从<key1>集合<field>取出 value
3、hmset <key1><field1><value1><field2><value2>... 批量设置hash的值
4、hexists<key1><field>查看哈希表 key 中,给定域 field 是否存在。
5、hkeys <key>列出该hash集合的所有field
6、hvals <key>列出该hash集合的所有value
7、hincrby <key><field><increment>为哈希表 key 中的域 field 的值加上增量 1 -1
8、hsetnx <key><field><value>将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
5.Zset
1):自动去重,集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的
数据结构
SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
常用指令
1、zadd <key><score1><value1><score2><value2>… 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
2、zrange <key><start><stop> [WITHSCORES] 返回有序集 key 中,下标在<start><stop>之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。
3、zrangebyscore key minmax [withscores] [limit offset count] 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
4、zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改为从大到小排列。
5、zincrby <key><increment><value> 为元素的score加上增量
6、zrem <key><value>删除该集合下,指定值的元素
7、zcount <key><min><max>统计该集合,分数区间内的元素个数
8、zrank <key><value>返回该值在集合中的排名,从0开始。
三、配置文件详解
(1)##Units单位###,配置单位大小,只支持bytes,不支持bit,大小写不敏感
(2)###INCLUDES包含###,类似jsp中的include,多实例的情况可以把公用的配置文件提取出来(集群或者主从的时候会用到)
(3)bind,默认情况bind=127.0.0.1只能接受本机的访问请求,不写的情况下,无限制接受任何ip地址的访问
(4)protected-mode,本机访问保护模式设置,如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应
(5)Port,端口号,默认 6379
(6)tcp-backlog,设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。
在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
(7)timeout,一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
(8)tcp-keepalive,对访问客户端的一种心跳检测,每个n秒检测一次。单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
(9)daemonize,是否为后台进程,设置为yes
(10)pidfile,存放pid文件的位置,每个实例会产生一个不同的pid文件
(11)loglevel,日志级别,debug(更详细)、verbose(正常,类似info)、notice(生产级别)、warning(只显示警告),默认为notice,生产环境选择notice 或者warning
(12)logfile,设置日志输出路径
(13)databases,设定数据库数量
(14)requirepass,设置密码
(15)maxclients,设置redis同时可以与多少个客户端进行连接。默认情况下为10000个客户端。
(16)maxmemory,可使用最大内存,建议必须设置,否则,将内存占满,造成服务器宕机
(17)maxmemory-policy,内存满了的移除策略
volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用)
allkeys-lru:在所有集合key中,使用LRU算法移除key
volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
allkeys-random:在所有集合key中,移除随机的key
volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
noeviction:不进行移除。针对写操作,只是返回错误信息
(18)maxmemory-samples,设置样本数量,一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小。
三、发布订阅
1、客户端可以订阅任意数量的频道,一个频道也可以被多个客户端订阅
2、命令行实现
打开一个客户端订阅channel:SUBSCRIBE channel
打开另一个客户端,给channel发布消息hello:publish channel hello
四、Redis新数据类型
1.Bitmaps
1、Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。
2、Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量
常用指令
1、setbit
setbit<key><offset><value>设置Bitmaps中某个偏移量的值(0或1),offset:偏移量从0开始
2、getbit
getbit<key><offset>获取Bitmaps中某个偏移量的值
3、bitcount
bitcount<key>[start end] 统计字符串从start字节到end字节比特值为1的数量,-1 表示最后一个位,而 -2 表示倒数第二个位
注意:redis的setbit设置或清除的是bit位置,而bitcount计算的是byte位置
4、bitop
bitop and(or/not/xor) <destkey> [key…],是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中。
Bitmaps与set对比
1、在正常情况下,由于Bitmaps是位操作,会比set节省空间
2、假如该网站每天的独立访问用户很少, 例如只有10万(大量的僵尸用户), 这时候使用Bitmaps就不太合适了, 因为基本上大部分位都是0。
2.HyperLogLog
基数问题
1、像UV(UniqueVisitor,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。
2、解决基数问题有很多种方案:
(1)数据存储在MySQL表中,使用distinct count计算不重复个数
(2)使用Redis提供的hash、set、bitmaps等数据结构来处理
以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。
1、HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
2、降低一定的精度来平衡存储空间
常用指令
1、pfadd
pfadd <key>< element> [element ...] 添加指定元素到 HyperLogLog 中
2、pfcount
pfcount<key> [key ...] 计算HLL的近似基数,可以计算多个HLL,比如用HLL存储每天的UV,计算一周的UV可以使用7天的UV合并计算即可
3、pfmerge
pfmerge<destkey><sourcekey> [sourcekey ...] 将一个或多个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得
3.Geospatial
1、Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。
常用指令
1、geoadd
geoadd<key>< longitude><latitude><member> [longitude latitude member...] 添加地理位置(经度,纬度,名称)
2、geopos
geopos <key><member> [member...] 获得指定地区的坐标值
3、geolist
geodist<key><member1><member2> [m|km|ft|mi ] 获取两个位置之间的直线距离
单位:
m 表示单位为米[默认值]。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位
4、georadius
georadius<key>< longitude><latitude>radius m|km|ft|mi 以给定的经纬度为中心,找出某一半径内的元素
五、Redis事务、锁机制
简介
1、Redis事务是一个单独的隔离操作,事务中所有的指令都会按顺序执行,且不会被其他客户端发来的命令打断
2、Redis事务的主要作用就是串联多个命令,防止别的命令插队
实现指令
1、Redis事务分为组队阶段、执行阶段
2、输入Multi命令开始组队,输入discard命令终止组队,输入Exec命令开始执行
3、组队阶段,如果一个命令出现了错误,整个队列都会被取消。执行阶段,如果一个命令出现错误则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
没有事务引发的问题
多人共用一个账号进行抢购,如果不做事务控制,则会导致余额变成负数(单纯的指这个问题)
问题解决
1、悲观锁:每次取数的时候都认为别人会修改数据,所以每次取数据之前都会加锁,当释放锁之后别人才能继续取数据
2、乐观锁:每次取数据时都认为别人不会修改数据,再更新的时候判断一下在此期间有没有别人更新数据(比如版本号机制)。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
乐观锁实现:
1、ATCH key [key ...]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改 动,那么事务将被打断。
2、unwatch
取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。
Redis事务三特性
1、单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
2、没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
3、不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
超卖问题
1、描述:在秒杀案例中,判断是否还有库存,然后进行库存减1,会存在在同一时间,判断都有库存,但是库存被减成了负数
2、解决:使用Redis乐观锁,进行淘汰用户,这种方案还会引起下面的库存遗留问题
库存遗留问题
1、描述:由于采用了乐观锁,会存在大量用户秒杀失败,但是有可能会导致当所有用户都秒杀结束了,还有库存剩余
2、解决:使用LUA脚本,在Redis2.6以上的版本中,可以使用LUA脚本将一些指令包起来,不会被其他命令插队
连接超时问题
1、描述:由于Redis得最大连接数有限,会出现一个用户使用在等待,最后就超时了
2、解决:使用Redis连接池解决超时问问题
五、Redis持久化
1.RDB(redis database)
RDB是在指定的间隔时间内,将内存中的数据快照写入磁盘(Snapshot快照),恢复时就是直接将快照文件读到内存里
如何备份
1、Redis会单独创建(fork)一个子进程来进行持久化,先将数据写入到一个临时文件,等持久化完成了再将临时文件替换掉上次持久化的文件
相关配置
1、dbfilename:配置持久化文件名,默认叫dump.rdb
2、dir:持久化文件保存路径,默认是./
3、:save和bgsave
(1):格式:save/bgsave 秒钟 写操作次数 在指定秒内如果发生了至少指定次数的修改,则触发持久化
(2):用save,在持久化时,会阻塞其他请求,不推荐。用bgsave会在后台进行异步快照,能够相应客户端请求
4、stop-writes-on-bgsave-error rudis持久化开关,推荐yes,单纯做缓存的话建议关掉
5、rdbcompression 指定是否压缩持久化文件,用LZF算法。会占用一点CPU,推荐yes
6、rdbchecksum 用CRC64算法检查数据完整性,会增加10%左右性能消耗
优点
1、适合大规模数据恢复,对数据完整性和一致性不高更适合使用
2、节省磁盘空间
3、恢复速度快
缺点
1、由于是Fork子进程进行持久化,会有2倍的膨胀性
2、使用写时复制技术,数据庞大时还是会消耗性能
3、最后一次持久化后的数据可能会丢失
2.AOF(append only file)
以日志的形式来记录redis的所有写操作(增量保存),只允许追加文件,不能改写。恢复时将所有指令顺序执行
持久化流程
1、写命令被append追加到AOF缓冲区内
2、缓冲区根据持久化策略,将操作sync同步到磁盘的AOF文件中
3、AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量
4、Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的
相关配置
1、appendfilename:aof文件名称,默认为 appendonly.aof,AOF文件的保存路径,同RDB的路径一致。
2、appendonly:是否启用AOF,默认是no
3、appendfsync:同步频率设置
(1):always:始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
(2):everysec:每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失
(3):no:redis不主动进行同步,把同步时机交给操作系统
4、no-appendfsync-on-rewrite:是否只写入缓存不写入磁盘
(1):yes:不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)
(2):no:会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)
重写触发机制
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
可修改配置:
1、auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写。默认值是64mb
2、auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发),默认值是100
Redis会记录此时AOF大小,设为base_size,
如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。
重写流程
(1)bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。
(2)主进程fork出子进程执行重写操作,保证主进程不会阻塞。
(3)子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。
(4)1).子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
2).主进程把aof_rewrite_buf中的数据写入到新的AOF文件。
(5)使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
重写原理
当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof,redis4.0版本后的重写,是指上就是把rdb 的快照,以二级制的形式附在新的aof头部,作为已有的历史数据,替换掉原来的流水账操作
优点
1、备份机制更稳健,丢失数据概率更低
2、可读的日志文本,通过操作AOF稳健,可以处理误操作
缺点
1、比起RDB占用更多的磁盘空间。
2、恢复备份速度要慢。
3、每次读写都同步的话,有一定的性能压力。
4、存在个别Bug,造成恢复不能。
推荐使用
官方推荐两个都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用 AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用。