redis启动方式
- 默认配置启动
- 运行启动
redis-server --configKey1 configValue1 --configKey2 configValue2
- 配置文件启动
redis-server /opt/redis/redis.conf
#配置文件以redis.conf自带的模板进行修改;当一台机器启动多个redis时,将配置文件返在指定文件夹下统一管理
redis客户端连接
- 交互方式
redis-cli -h{host} -p {port}
#host和port默认是127.0.0.1和6379
- 命令方式
redis-cli -h{host} -p {port} {command}
#e.g. redis-cli get key
redis服务停止
redis-cli shutdown #会生成持久化文件;较为优雅;可以选择是否生成持久化文件:redis-cli shutdown nosave|save
#除了shutdown命令外;kill进程号同样可以关闭redis,不会生成持久化文件,还会造成缓冲区等资源不可以优雅的被关闭,极端情况下会造成AOF和复制丢失数据的可能
redis 的数据结构与内部编码
多种内部编码可以在不同场景下发挥优势;例如:ziplist比较节省内存,但是当列表元素比较多的时候,性能会下降,redis会根据配置选项将列表类型的内部实现装换为linkedlist
object encoding key
#获取内部编码
redis的单线程架构
redis使用单线程架构和I/O多路复用模型来实现高性能的内存数据库服务;
redis使用单线程模型达到每秒万级别的处理能力的原因:
- 纯内存方位,内存的响应时长大约为100纳秒
- 非阻塞I/O;redis使用epoll作为I/O多路复用技术的实现,再加上redis自身的事件处理模型将epoll中的链接、读写、关闭都装换为事件,不在网络I/O上浪费过多的时间
- 单线程避免了线程的切换和竞态产生的消耗
redis单线程的缺陷:
因为是单线程所以对每个命令的执行时间都是有要求的,如果某个命令执行时间过长的话,会造成其他命令的阻塞;
redis全局命令
- 查看所有键
keys *
#不管存储的具体值类型,返回所有的键
#会遍历所有的键,当redis键过多时,线上环境是禁止使用
- 键总值
dbsize
#键数量redis会每次存储,所以他的时间复杂度为O(1)
- 键是否存在
exists key
#键存在返回1,不存在返回0
- 键删除
del key [key...]
#不管键值的类型,通用删除,返回删除的数量,删除的键不存在的话就返回0
- 键过期
expire key seconds
#设置键过期时间
ttl key
#ttl 返回键的剩余过期时间,-1 键没设置过期时间;-2 键不存在
- 键的数据结构类型
type key
# key不存在返回none;
# 返回redis对外五种数据结构:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)
命令
列举各个数据结构中使用较多或者开发时需要注意的一些命令
redis 字符串
string的内部编码有:
- int:8个字节长整形
- embstr:小于等于39个字节的字符串
- raw:大于39个字节的字符串
- set
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
# ex 设置秒级过期时间;同命令:setex key seconds value
# px 设置毫秒过期时间
# nx 键必须不许存在才可以设置;用于新增;同命令:setnx key value;#key 存在的话返回0
# xx 键必须存在才可以设置,用于修改;
- mset
mset key value [key value ...]
#批量设置
- get
get key
#如果key不存在,返回nil
- mget
mget key [key ...]
# 批量获取
# 比循环n次get批量获取效率高;redis可以处理每秒万级别的读写操作,但是这是服务端的处理能力,对于客户端来说,一次命令除了命令时间还有网络时间
- incr
incr key
#自增操作
#值非整数,返回错误;值是整数,返回自增结果;键不存在,按照0开始自增,返回1
incrby key
#自增指定数字
decr key
#自减
decrby key
#自减指定数字
incrbyfloat key
#自增浮点数
- strlen
strlen key
# 字符串长度
- getset
getset key value
#设置值并返回之前的值;如果第一次创建的话返回的测试nil
详见redis官方文档:http://www.redis.cn/commands.html#string
使用场景:缓存功能、计数、共享session、限速(短信验证码、IP访问限制)
redis 哈希
内部编码:
- ziplist:压缩列表;当哈希类型元素个数小于hash-max-ziplist-entries配置的512个、同时所有值都小于hash-max-ziplist-value配置的64个字节时,redis会使用ziplist作为hash的内部实现,ziplist使用更加紧凑的结构实现多个元素存储,在节省内存方面比hashtable更加优秀;
- hashtable(哈希表):当哈希类型无法满足ziplist时,使用hashtable实现哈希的内部实现,因为此时的ziplist读写效率会下降,而hashtable的读写时间复杂度为O(1);
- hset
hset key field value
#设置值
hsetnx key field value
#设置hash值,如果field不存在,才会新增,存在的话返回0
- hgetall
hgetall key
#获取所有的field-value
#当hash元素过多时,会造成阻塞;如果只是获取部分field,可以使用hmget;如果必须要获取所有的field-value时可以使用hscan,该命令会渐进式的遍历哈希类型
详见redis官方文档:http://www.redis.cn/commands.html#hash
使用场景:
- 将关系型数据库中的数据缓存下来:对比使用字符串序列化存储,hash存储更加直观,且在更新操作时更加便捷;
hash类型与关系型数据库的区别在于:1、hash类型是稀疏的,而关系型数据库是完全结构话的,比如,hash每个键可以有不同的field,但是关系型数据库添加了一列的话,所有的数据都会设置该值;2、关系型数据库可以做复杂的查询,但是redis想要模拟关系型的复杂查询的话开发困难,维护成本高;
redis列表
列表(list):用于存储多个字符串,有序的,可以存在重复的元素,可以通过下标来获取指定元素;一个列表最多可以存储2^23-1个元素
内部编码:
- ziplist:压缩列表;当列表类型元素个数小于set-max-ziplist-entries配置的512个、同时所有值都小于set-max-ziplist-value配置的64个字节时,redis会使用ziplist作为列表的内部实现,ziplist使用更加紧凑的结构实现多个元素存储,在节省内存方面比linkedklist更加优秀;
- linkedklist(链表):当列表类型无法满足ziplist时,使用linkedklist实现列表的内部实现,因为此时的ziplist读写效率会下降
- quicklist :结合了ziplist和linklinkedlist的特点,在列表较大时,需要获取列表中间元素时也可以高效的完成;(当lrange命令在获取列表两端的信息时,性能较好,但是当列表过大时,想要获取列表的中间元素,性能是有问题的)
使用场景:
- 消息队列
lpush+brpop命令组合即可实现阻塞队列
- 文章列表
使用列表存储每个用户的文章列表,每个文章信息使用hash来存储每个文章信息;
在使用列表来保存和获取文章列表时存在两个问题:第一:如果每次分页过大,需要多次执行hgetall 来拿文章信息,此时可以考虑使用Pipeline来批量获取,或者将文章信息序列化为字符串通过mget来批量获取;第二:使用lrange来获取文章列表时,在列表两端性能较好,但是如果列表过大,想要获取列表中间的信息时,性能变差,可以考虑将列表做二级拆分,或者使用列表的quicklist内部编码来实现;总结:
- lpush+lpop=stack(栈)
- lpush+rpop=queue(队列)
- lpsh+ltrim=capped collection (有限集合)
- lpush+brpop=message queue (消息队列)
redis 集合
集合(set)类型也是用来保存多个字符串的元素的,但是和列表不同在于,集合中不存在重复的元素,此外集合时无序的,不可以通过索引下标来获取元素,一个集合最多可以存储2^23-1个元素;可以实现不同集合之间的交集、并集、差集运算
内部编码:
- intset(整数集合):当集合中的元素都是整数,且元素的个数小于set-max-intset-entries配置时(默认512个);使用该编码
- hashtable:当集合元素无法满足intset时,使用hashtable来实现集合的内部编码
不同集合之间的运算,在元素比较多的时候是比较耗时的,所以redis可以通过以下三个命令将结果保存在destination key中
sinterstore destination key [key …]
suionstore destination key [key …]
sdiffstore destination key [key …]
使用场景:
- 标签:通过集合运算来计算用户共同感兴趣的标签,或者指定多种标签共同感兴趣的人
- 生成随机数
总结:- sadd=tagging(标签)
- spop/srandmember=random item(生成随机数,如果抽奖)
sadd+sinter=social graph (社交需求)
redis 有序集合
有序集合:保留了集合的元素不可以重复的特性;此外他给每个元素提供了一个score(分数)作为排序的依据;
内部编码:
- ziplist:压缩列表;当哈希类型元素个数小于zset-max-ziplist-entries配置的128个、同时所有值都小于zset-max-ziplist-value配置的64个字节时,redis会使用ziplist作为zset的内部实现,ziplist使用更加紧凑的结构实现多个元素存储,在节省内存方面更加优秀;
- skiplist(跳跃表):当无法满足ziplist条件时,使用skiplist实现有序集合的内部实现,因为此时的ziplist读写效率会下降
使用场景:
- 排行榜系统:zadd+zincreby
redis 数据迁移
redis的数据迁移即是迁移键;Redis发展历程中提供了move、dump+restore、migrate三组迁移键的方法;
- move
move命令用于在Redis内部进行数据迁移;Redis内部可以有多个数据库,彼此在数据上是相互隔离的,move key db就是把指定的键从源数据库移动到目标数据库中
move key db
- dump+restore
实现在不同的Redis实例之间进行数据迁移的功能,整个迁移的过程分为两步:
1)在源Redis上,dump命令会将键值序列化,格式采用的是RDB格式。
2)在目标Redis上,restore命令将上面序列化的值进行复原,其中ttl参数代表过期时间,如果ttl=0代表没有过期时间。
注意:
- 整个迁移过程并非原子性的,而是通过客户端分步完成的。
- 迁移过程是开启了两个客户端连接,所以dump的结果不是在源Redis和目标Redis之间进行传输
dump key
restore key ttl value
- migrate
migrate命令也是用于在Redis实例间进行数据迁移的,实际上migrate命令就是将dump、restore、del三个命令进行组合,从而简化了操作流程。migrate命令具有原子性;
migrate host port key|"" destination-db timeout [copy] [replace] [keys key [key ...]]
#host 目标Redis的IP地址
#port 目标Redis的端口
#key|"" key:迁移一个键;""迁移多个键
#destination-db:目标Redis的数据库索引,例如要迁移到0号数据库,这里就写0
#timeout:迁移的超时时间(单位为毫秒)
#[copy]:如果添加此选项,迁移后并不删除源键
#[replace]:如果添加此选项,migrate不管目标Redis是否存在该键都会正常迁移进行数据覆盖
#[keys key[key...]]:迁移多个键,例如要迁移key1、key2、key3,此处填写“keys key1 key2 key3”