Redis的特性
- 速度快:
- Redis所有的数据都存放到内存的,内存中对数据的读取速度平均为每100纳秒一次
- Redis使用的C语言加以实现,更接近于机器语言,执行速度快
- Redis使用了单线程的架构,以避免了多线程存在的竞争问题
- Redis源码精简,集性能与优雅于一身
- 基于键值对的数据结构服务器,Redis提供基于键值对方式的存储,并提供了五大基本类型,同时根据存入值的变化从而改变其内部编码,使其性能最大化,根据这些数据类型,可以根据业务需求构建更加优秀的应用。
- 丰富的功能,Redis除了提供五大基本类型外,还提供了其他的功能给开发者使用,以适应不同的业务需求:
- 提供键过期功能,实现缓存功能
- 提供发布与订阅功能,实现消息系统
- 提供Lua脚本功能,可以使用lua构建出开发者需要的命令
- 提供了简单的事务,能一定程度上保障事务的特性
- 提供了流水线功能,这样客户端能将一批命令一次性传递到Redis,减少网络的开销
- 简单稳定,Redis的源码精简,并且基本不会出现因其自身bug而出现的问题
- Redis提供了简单的TCP通信协议,使更多的编程语言能够方便的接入Redis。
- 持久化,通常将数据放入内存是不安全的,一旦发生断电或者宕机数据就可能会丢失,而Redis提供了两种持久化方式:RDB和AOF,保证了数据的可持久性
- 主从复制,实现了多个相同数据的Redis副本。成为分布式的Redis的基本条件。
- 高可用和分布式,Redis从2.8版本正式提供了高可用实现 Redis Sentinel,它能够保证 Redis节点的故障发现和故障自动转移。Redis从3.0版本正式提供了分布式实现 RedisCluster,它是 Redis 真正的分布式实现,提供了高可用、读写和容量的扩展性。
Redis基础
Redis的单线程架构
- 首先我们知道redis是单线程的,所以说你发送的命令不会并行执行,那我们先了解redis是怎样在单线程的情况下实现如此高的性能:
- 首先redis是基于内存的,在内存中响应时长约为100纳秒
- reids使用非阻塞I/O,reids使用epoll作为I/O多路复用的实现,再加上redis将epoll中的连接、读写、关闭都转换成事件,不在网络I/O中浪费时间
- 最后就是redis使用的单线程了,单线程的好处是,不必进行线程的切换,也避免了多线程情况下出现的线程资源竞争问题。
Redis五大基本类型(redis version:5.0.7)
全局API
- 遍历键:
- keys pattern,pattern使用的是glob风格的通配符来遍历键:
- * ,会输出所有的键,但是不建议使用,因为它会遍历所有的键,效率低
- ?,代表一个字符
- [],代表匹配部分字符,[a,b]匹配a和b,[a-z]匹配a到z的字母
- \,用来做转义,列如键名中存在*、?
- redis-cli -h ip - p port -a password keys ‘partten’ | xargs redis-cli -h ip - p port -a password del,可以通过条件来删除对应的键;但是不推荐这样使用,一来是可能造成阻塞,二来如果使用了主从复制会影响主从复制
- scan cursor [MATCH pattern] [COUNT count],渐进式遍历键,v2.8之后才提供,解决遍历键带来的阻塞问题,每次执行都会返回当前遍历到的游标值,当返回为0时代表遍历完成
- cursor,是一个游标参数,指定从某个游标处开始遍历,启始数从0开始
- MATCH pattern:是跟keys命令的pattern相同规则的通配符,不选中时默认为遍历所有
- COUNT count:可选项,指示当前遍历的个数,默认每次遍历10个键
- 虽然scan解决了键的阻塞问题,但在遍历途中发生了键的新增、修改、删除等操作时,可能会出现新增的键不能遍历到,以及遍历出重复的键等问题
- 扩展:redis除了提供键的渐进式遍历外还为hash、set、zset提供了渐进式遍历的命令hscan、sscan、zscan,其用法与scan相同
- keys pattern,pattern使用的是glob风格的通配符来遍历键:
- dbsize,返回当前键的总数,不会遍历所有的键,而是获取redis内置的键总数变量
- exists key,查看键是否存在,存在返回1,不存在返回0
- del key,删除键
- 键过期时间设置:
- expire key senconds,设置某个键的秒过期时间,当超过过期时间redis会自动删除键
- expireat key timestamp,设置键过期的秒级时间戳,如设置的秒级时间戳小于当前秒级时间戳,则键直接过期,否则键将会在(timestamp-当前秒级时间戳)秒后过期。
- v2.6后提供了毫秒级过期时间设置:
- pexpire key milliseconds,键在milliseconds毫秒之后过期
- pexpireat key milliseconds-timstamp,与上面的expireat命令差不多只是单位从秒级变为毫秒级
- 无论是毫秒级还是秒级,redis内部都是通过使用pexpireat命令来实现
- ttl key,返回键的剩余的秒级过期时间,大于等于0代表键的剩余过期时间,-1代表没有设置过期时间。-2则代表键不存在
- 删除键过期时间:
- persist key,删除键的过期时间,删除成功返回1
- 当键值为字符串时,使用set命令会去掉过期时间。
- 迁移键:
- move key db,适用于redis内部数据库键迁移
- dump+restore,实现不同redis实例之间的数据键迁移:
- dump key,首先将对应的键的值通过RDB格式进行序列化
- restore key ttl value [REPLACE],在另外的一台redis实例上将序列化的值获取到,通过restore命令将值保存到另一个redis实例中,key为重新设置的键名,ttl为到期时间,0为不过期,value为序列化的值,replace为可选项,针对键值存在时替换其内容
- migrate host port key | destionation-db timeout [COPY] [REPLACE] [AUTH password] [KEYS key],该命令也是用于不同redis实例之间的键迁移,并且在v3.06后支持批量的键迁移操作,其内部其实使用的dump+restore+del,但是该命令具有原子性;并且该命令不需要开启多个客户端,只需要在数据源端执行即可。
- host:目标地址
- port:目标端口
- key:要迁移的键,如果要迁移多个键则不再这里输入空
- destionation-db:为目标数据库,如果没有则填0
- timeout:为键迁移的超时时间,单位为毫秒
- COPY:可选项,如果输入则不执行del操作
- REPLACE:如果目标库中存在该键则直接覆盖值
- AUTH password:当目标库中存在密码时,进行密码验证,该项在需要在v4.0.7后才支持
- KEYS key:这个用于多个键迁移时使用
- type key,查看键的数据结构类型,键不存在则返回none
- object encoding key,查看键的内部编码
- 键重命名:
- rename key newkey,键重命名,值得注意的是在v3.2之前重命名的键名跟当前键名相等时返回错误,而在v3.2后则允许这样的操作
- renamenx key newkey,确保在newkey不存在时才会进行重命名
- randomkey,随机返回一个键名
- select index,切换数据库:
- 在redis中库名都是用数字表示,默认有0-15个数据库,通过select命令可切换数据库,连接客户端时默认都使用的是0号数据库
- 虽然这样拆分了许多的数据库,但是不推荐这样去使用,因为本来redis使用的是单线程,不管如何拆分消耗还会增大没有任何其他的意义,另外在分布式的redis中只允许使用0号数据库
- flushdb,清空当前库的所有键值对
- flushall,清空所有库的所有键值对
基础API
string
- string类型是比较常用的类型,在vlue中无论你存入的是json、xml包括二进制信息都是可以的,唯一需要注意的就是单个值的大小不能超过512M
- 存值:
- set key value [ex seconds] [px milliseconds] [nx|xx] 将值以string类型进行保存
- ex seconds 设置键的存活时间 单位为秒
- px milliseconds 设置键的存活时间 单位为毫秒
- nx 键必须在不存在的情况下才能将进行保存
- xx 与nx相反,必须在键存在的情况才能将值进行保存
- 注:除了 set 的nx外,redis额外提供了setnx命令与其相同效果的命令
- 取值:
- get key 如果要获取的键不存在,则返回nil(空)
- 批量存值:
- mset key value [key value …]
- 批量取值:
- mget key [key …]
- 计数:
- incr key 自增,如果键存在,且值为整数则返回自增后的数,否则报错,如果键不存在则从零开始自增,返回自增后的数
- decr key 自减,如果键存在,且值为整数则返回自减后的数,否则报错,如果键不存在则从零开始自减,返回自减后的数
- incrby key increment 自增指定数字,如果键存在,且值为整数则返回自增后的数,否则报错,如果键不存在则从零开始自增,返回自增后的数,increment可以为正负整数,但不能为小数
- decrby key decrement 自减指定数字,如果键存在,且值为整数则返回自减后的数,否则报错,如果键不存在则从零开始自减,返回自减后的数,decrement 可以为正负整数,但不能为小数
- incrbyfloat key increment 自增指定浮点数,如果键存在,且值为整数则返回自增后的数,否则报错,如果键不存在则从零开始自增,返回自增后的数,increment 可以为任意的整数、小数
- 追加字符串:
- append key value,如果键不存在则会创建一个键值为追加的值,并返回追加的字符串长度
- 字符串长度:
- strlen key,如果键不存在返回0,如果是中文字符串,则一个中文字符代表3字节长度
- 存入新值返回旧值:
- getset key value, 将值重置并返回原值,如果键不存在则返回nil,并且也会将值存入
- 指定位置开始替换字符串:
- setrange key offeset value ,输入几个字符就会替换几个字符,设置成功后返回字符串长度。如果之前为英文是替换为中文,则中文存储的字符长度=中文字符*3
- setrange key offeset value ,输入几个字符就会替换几个字符,设置成功后返回字符串长度。如果之前为英文是替换为中文,则中文存储的字符长度=中文字符*3
- 字符串截取:
- getrange key start end 截取从start位置到end位置的字符串,键不存在返回空字符串
- string各命令时间复杂度:
hash
- 存储结构:{{field1,value1},…{fieldN,valueN}}
- 存值:
- hset key filed value
- hsetnx key filed value 与字符串相似的功能,但是在此必须是filed不存在才会保存成功,不成功返回0
- 取值:
- hget key filed,没有返回nil(空)
- 删除filed:
- hdel kye filed [filed …]
- 某个键下{filed,value}的数量:
- hlen key ,返回filed的数量,键不存在返回0
- 批量存入{filed,value}:
- hmset key file value [filed value …]
- 批量取值:
- hmget key filed [filed …]
- 判断filed是否存在:
- hexistis key filed,存在返回1,不存在返回0
- 获取某个键下的所有filed:
- hkeys key
- 获取某个键下所有的value:
- hvals key
- 获取某个键下的所有{filed,value}:
- hgetall key,不推荐使用,如果存入的{filed,value}过多可能会造成Redis的阻塞。
- 计数:
- hincrby key filed increment,自增指定的数字,并返回自增后的数
- hincrbyfloat key filed increment,自增指定的小数或整数,并返回自增后的数
- 获取value字符串长度:
- hstrlen key len
- hash各命令时间复杂度:
list
- list类型适用来存储多个有序的字符串,一个list类型的键值对可最多存储232-1个元素;list类型的键值对可以通过索引来获取数据,且列表中的元素可以重复。
- 存值:
- rpush key value [value …],从列表右边存入元素
- lpush key value [value …],从列表的左边存入元素
- linsert key before|after piovt value ,从左往右寻找piovt元素,选择在该元素之前还是之后插入元素value,需要注意的是如果list中存在两个piovt元素,则只会在从左往右数的第一个元素的前或后插入元素,当piovt元素不存在时返回-1
- 取值:
- lrange key start stop 从strat索引开始左往右遍历获取list中的元素直到 stop为止,当stop大于等于list长度减一时(也就是包左包右),展示从start索引后的所有元素。需要值得注意的时start和stop还可以为负数,当为负数时索引为从右往左进行计算,第一个元素的索引为-1,以此类推,也就是负的list长度对应的是从左往右的第一个元素
- lindex key index,通过索引来抓取元素,同lrange中start和stop一般,index也可以为负数,且规则相同。
- lrange key start stop 从strat索引开始左往右遍历获取list中的元素直到 stop为止,当stop大于等于list长度减一时(也就是包左包右),展示从start索引后的所有元素。需要值得注意的时start和stop还可以为负数,当为负数时索引为从右往左进行计算,第一个元素的索引为-1,以此类推,也就是负的list长度对应的是从左往右的第一个元素
- 获取list元素个数:
- llen key
- 删除元素:
- lrem key count value ,该命令为指定删除某个元素,count为删除多少个指定元素,值得注意的是,当count为正数时当从左往右查找指定元素删除,当count为负数时当从右往左查找指定元素删除,当count为0时删除所有指定元素。
- 从左往右删除指定个数的元素:
- 从右往左删除指定个数的元素:
- 删除所有指定元素:
- 从左往右删除指定个数的元素:
- lrem key count value ,该命令为指定删除某个元素,count为删除多少个指定元素,值得注意的是,当count为正数时当从左往右查找指定元素删除,当count为负数时当从右往左查找指定元素删除,当count为0时删除所有指定元素。
- 按照索引范围截取列表:
- ltrim key start end,该命令可看比是字符串截取的功能一般,不同的是start和end都可以为负数且包左包右
- ltrim key start end,该命令可看比是字符串截取的功能一般,不同的是start和end都可以为负数且包左包右
- 修改元素:
- lset key index value,更改对应索引的元素,同样index可以为正数也可以为负数
- 弹出元素:
- lpop key,弹出删除左端的第一个元素
- rpop key,弹出删除右端的第一个元素
- 阻塞弹出:
- blpop key [key …] timeout,lpop的阻塞版本,但是允许多个键的弹出删除,从左往右只有某个键中存在数据则直接弹出删除该键的从左往右的第一个元素,如果没有则等待timeout秒后返回;当在阻塞途中时某个键中有添加元素的操作时也会立即弹出删除该键的从左往右的第一个元素。
- brpop key [key …] timeout,rpop的阻塞版本,但是允许多个键的弹出删除,从左往右只有某个键中存在数据则直接弹出删除该键的从右往左的第一个元素,如果没有则等待timeout秒后返回;当在阻塞途中时某个键中有添加元素的操作时也会立即弹出删除该键的从右往左的第一个元素。
- 上方两个命令效果相仿只是弹出元素的端有所不同,这里通过blpop命令来演示:
- 一个键中没有元素,另一个存在元素则立即弹出删除存在元素的键的左端第一个元素。
- 如果阻塞弹出的键为空时进入阻塞,在timeout秒后返回
- 如果阻塞弹出的键为空时进入阻塞,当阻塞时间设置为0时为一直阻塞直到键添加元素,这里用两个客户端进行演示(注:当多个客户端对同一个键进行阻塞时,当键中存在元素后,第一个阻塞的客户端优先返回)
- 一个键中没有元素,另一个存在元素则立即弹出删除存在元素的键的左端第一个元素。
- 小结:lpop和rpop可以看作是非阻塞双端队列的实现,而blpop和brpop则可以看作阻塞双端队列的实现。
- 上方两个命令效果相仿只是弹出元素的端有所不同,这里通过blpop命令来演示:
- list各命令时间复杂度:
set
- set集合中也可以存储多个字符串,其存储最大成员数为232-1,并且可以使用集合来获取交集、并集、差集;不同的是set中不允许存在多个相同的成员,且在该集合中成员是无序的,所以不能通过索引来取值。
- 存值:
- sadd key member [member …],如果在添加时集合中已存在该成员,则不会添加该成员到集合中,命令会返回添加进集合的成员数目,没有添加返回0。
- sadd key member [member …],如果在添加时集合中已存在该成员,则不会添加该成员到集合中,命令会返回添加进集合的成员数目,没有添加返回0。
- 删除成员:
- srem key member [member …]
- 获取集合中成员数:
- scard key
- 判断成员是否在集合中:
- sismember key,存在返回1,不存在返回0
- 随机返回集合中的指定数目的成员
- srandmember key [count],集合不存在返回nil(空)
- count为可选项,不写默认返回1个成员
- count > 0 && count ≤ set的集合成员总数,返回count个成员,且无重复
- count > 集合成员总数,返回集合所有成员
- count < 0,返回|count|个成员,返回的成员可能出现重复
- count为可选项,不写默认返回1个成员
- srandmember key [count],集合不存在返回nil(空)
- 从集合中随机弹出:
- spop key [count],v3.2后支持可选项count,可以指定弹出的成员数;count > 集合成员总数弹出所有成员,count不能小于0
- 获取集合所有成员:
- smembers key
- 多个集合的操作:
- sinter key [key …]
- 返回多个集合的交集
- 当只有一个集合时返回该集合的所有成员
- 多个集合取交集时当某个集合为空时,提示集合为空
- sunion key [key …]
- 获取多个集合的并集
- 两个集合存在相同的成员,则只保留一个
- 当多个集合取并集时,只要有一个集合不为空即可,否则提示集合为空。
- sdiff key [key …]
- 获取多个集合的差集
- 取交集时如果集合中没有交集提示返回的集合为空
- 当只有一个集合时返回该集合的所有成员
- 多个集合取交集时只要有一个集合不为空即可
- sinterstore destination key [key …],将返回的交集存入到自定义的集合中,如果集合为空则不创建
- sunionstore destination key [key …],将返回的并集存入到自定义的集合中,如果集合为空则不创建
- sdiffstore destination key [key …],将返回的差集存入到自定义的集合中,如果集合为空则不创建
- sinter key [key …]
- set各命令时间复杂度:
zset
- zset为有序集合,跟set一样zset不允许成员重复,但提供了对集合进行排序的功能,与list根据索引方式不同,zset除了存储成员外还给每个成员增加了一个score(分数)项,zset可依照score对集合进行排序。
- 存值:
- zadd key [NX|XX] [CH] [INCR] score member [score member …],zset为有序集合其内部会根据score进行排序,其复杂度为O(log(n)),所以在使用就需要谨慎使用。
- zset存入成员的时候提供了四个选项:nx、xx、ch、incr
- nx:必须在成员不存在情况下才能添加成功
- xx:必须在成员存在的情况下才能修改数据,需要注意的是不管更新成功与否返回值都是0,所以需要配合ch选项,查看是否操作成功
- ch:返回当次操作score,member变更的个数
- incr:每次只能对单个成员的score增加指定的增量(增量可以为正负整数、小数)
- zset存入成员的时候提供了四个选项:nx、xx、ch、incr
- zadd key [NX|XX] [CH] [INCR] score member [score member …],zset为有序集合其内部会根据score进行排序,其复杂度为O(log(n)),所以在使用就需要谨慎使用。
- 获取集合成员总数:
- zcard key
- 获取成员的分数:
- zscore key member,没有返回nil(空)
- 计算成员在集合中排位:
- zrank key member,获取成员在集合中的正序排位,排位从0开始计数
- zrevrank key member,获取成员在集合中的倒序排位,排位从0开始计数
- 删除成员:
- zrem key member [member …],返回已删除的成员个数
- 增加成员的分数:
- zincrby key increment member,与incr有相同的功能,但是如果increment为小数时会造成精度缺失
- zincrby key increment member,与incr有相同的功能,但是如果increment为小数时会造成精度缺失
- 截取集合某个排位范围的成员:
- zrange key start end [withscores],正序获取从start到end排位的成员
- zrevrange key start end [withscores],倒序获取从start到end排位的成员
- 上面的两个API一个是正序一个是倒序,除了顺序不同其他都相同:
- withscores可选项,其意义在于是否展示成员分数
- start和end的值可以为正负数
- 为正数时从0开始算起,start不得大于集合总数,而当end大于集合总数时获取从start及之后的所有成员
- 当为负数时其代表的成员则是从当前顺序的最后一个成员开始算起,但其绝对值不得大于集合成员总数(所以通过 zrange key 0 -1可以获取当前集合所有成员,zrevrange亦可)
- 其截取排位范围时的规则为包左包右
- 截取指定分数范围的成员:
- zrangebyscore key min max [withscores] [limit offset count],正序截取指定分数范围的成员
- zrevrangebyscore key max min [withscores] [limit offset count],倒序截取指定分数范围的成员
- 以上两个API分别是倒序和正序获取指定分数范围的成员,而且其指定分数范围的max和min位置有所不同,其他都是相同的:
- withscores可选项,其意义在于是否展示成员分数
- limit offset count可选项,其意义在于对指定分数范围内的成员进行限制其输出的起始位置(offset)和输出的成员个数(count)
- offset从0开始,不能为负数
- count 如果为负数或大于范围集合的总成员数,则截取从offset开始及其之后的成员
- 返回指定分数范围的成员个数:
- zcount key min max
- 删除正序排列的指定排位范围成员
- zremrangebyrank key start end,和zrange中的start和end规则相同,这里的start和end也可以为负数,删除完成后返回删除成员个数
- 删除指定分数范围的成员:
- zremrangebyscore key min max
- 多集合操作:
- 交集:zinterstore destination numkeys key [key …] [weights weight] [aggregate sum|min|max]
- 并集:zunionstore destination numkeys key [key …] [weights weight] [aggregate sum|min|max]
- zset的并集和交集除了首部的命令不同外,其选项功能都相同:
- destination,将提取出交集或并集存存入自定义键中
- numkeys,取交集或并集时的集合个数,必须与后面的key个数做对应否则报错
- key …,多集合的键名
- weights weight,该选项为可选项,指定键的权重,当设置时当根据key的顺序和个数标注每个键的权重,默认都为1。
- aggregate sum|min|max,获取出成员交集或并集后,其分数的计算方式可以使用sum,min,max三种选项,需要注意的是该选项将会在计算权重完成之后在对分数做计算。
- zset各命令时间复杂度:
文章参考书籍:
Redis开发与运维