Redis 安装和五大类型常用命令
Redis
Redis 简介
1. Redis 特性
-
速度快
- 数据存在内存中
- C 语言实现 (50000 line)
- 线程模型(单线程)
-
持久化
- Redis 所有数据保持在内存中,对数据的更新将异步的保持的磁盘上
-
多种数据结构
- BitMaps: 位图
- HyperLogLog: 超小内存唯一值计数
- GEO: 地理信息定位
-
支持多种编程语言
-
功能丰富
- 发布订阅
- 事务
- Lua脚本
- pipeline
-
简单
- 23000 lines of C code
- 不依赖外部的库
- 单线程模型
-
主从复制
- 主服务 从服务器
-
高可用、分布式
- Redis-Sentinel(v2.8) 支持高可用
- Redis-Cluster(v3.0) 支持分布式
2. Redis典型应用场景
-
缓存系统
-
计数器
- 通常网站的 "转发xxx" "评论xxx" "阅读xxx" 等计算场景
-
消息队列系统
-
排行榜
-
社交网络
-
实时系统
3. Redis 的安装/启动/配置
Redis 安装
- 下载安装压缩文件:
curl -O https://download.redis.io/releases/redis-6.2.7.tar.gz
- 解压:
tar xf redis-6.2.7.tar.gz
- 创建软连接:
ln -s redis-6.2.7 redis
- 编译
cd redis && make
- 安装
cd redis && make install
Redis 可执行文件说明
- redis-server: Redis 服务器
- redis-cli: Redis命令行客户端
- redis-benchmark: Redis 性能测试工具
- redis-check-aof: AOF 文件修复工具
- redis-check-dump: RDB文件检查工具
- redis-sentinel: Sentinel服务器(2.8以后)
Redis 启动
- 最简启动
redis-server
- 动态参数启动
redis-server --port 6380
- 配置文件启动(推荐)
redis-server redis-6382.conf
- 生产环境选择配置启动
- 单机多实例配置文件可以用端口区分开
cd redis
mkdir config
# 存放配置文件mkdir data
# 存放RDB 文件cp redis.conf config/
# 复制默认配置文件cat redis.conf | grep -v "#" | grep -v "^$" > redis-6382.conf
# 去掉注释和空格
- 修改配置文件
[root@nezha-api config]# vi redis-6382.conf port 6382 daemonize yes logfile "6382.log" dir "/root/Redis/redis/data"
redis-server redis-6382.conf
# 启动redis
- 验证启动
ps -ef | grep redis
netstat -antpl| grep redis
redis-cli -h <ip> -p <port> ping
Redis 配置
daemonize
: 是否是守护进程(no|yes)port
: Redis 对外端口号(默认6379)logfile
: redis 系统日志dir
: Redis 工作目录
Redis 客户端连接
[root@api ~]# redis-cli -p 6379 -h 127.0.0.1
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
Redis 客户端返回值
- 状态回复 ping -> pong
127.0.0.1:6382> ping PONG
- 错误回复
127.0.0.1:6382> hget hello field (error) WRONGTYPE Operation against a key holding the wrong kind of value
- 整数回复 incr hello -> (integer) 1
- 字符串回复 get hello -> "world"
127.0.0.1:6382> get hello "world"
- 多行字符串回复 mget hello foo -> 1) "world" \n 2) "bar"
127.0.0.1:6382> MGET hello foo 1) "world" 2) "test
Redis API 的使用
1. 通用命令
通用命令
keys [pattern]
: 遍历所有key127.0.0.1:6382> keys * # 遍历所有key 1) "php" 2) "hello" 3) "phe" 4) "foo" 5) "hehe" 127.0.0.1:6382> keys he* # 遍历所有以he开头的key 1) "hello" 2) "hehe" 127.0.0.1:6382> keys he[h-l]* 1) "hello" 2) "hehe" 127.0.0.1:6382> keys ph? 1) "php" 2) "phe"
- keys 命令一般不在生产环境使用(redis 单线程,会被阻塞)
- 热备从节点
- scan 命令可以分步获取keys
SCAN cursor [MATCH pattern] [COUNT count]
: 初始执行scan命令例如scan 0。SCAN命令是一个基于游标的迭代器- scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是遍历的 limit hint。
第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。
127.0.0.1:6382> keys * 1) "phe" 2) "hello" 3) "set" 4) "user:2:info" 5) "myset" 6) "foo" 7) "hehe" 8) "php" 9) "set1" 10) "a" 11) "user:1:info" 12) "go" 13) "python" 127.0.0.1:6382> scan 0 MATCH * COUNT 5 1) "2" 2) 1) "php" 2) "foo" 3) "user:1:info" 4) "go" 5) "python" 127.0.0.1:6382> scan 2 MATCH * COUNT 5 1) "11" 2) 1) "set1" 2) "phe" 3) "hello" 4) "set" 5) "user:2:info" 6) "myset" 127.0.0.1:6382> scan 11 MATCH * COUNT 5 1) "0" 2) 1) "a" 2) "hehe"
- scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是遍历的 limit hint。
dbsize
: 计算key的总数127.0.0.1:6382> dbsize (integer) 5
exists key
: 检查key是否存在127.0.0.1:6382> set a b OK 127.0.0.1:6382> exists a (integer) 1 127.0.0.1:6382> del a (integer) 1 127.0.0.1:6382> exists a (integer) 0 127.0.0.1:6382>
del key [key ...]
: 删除指定的key-val127.0.0.1:6382> mset a b c d OK 127.0.0.1:6382> mget a c 1) "b" 2) "d" 127.0.0.1:6382> del a c (integer) 2 127.0.0.1:6382> mget a c 1) (nil) 2) (nil)
expire key seconds
: key 在 seconds 秒后过期ttl key
: 查看可以剩余的过期时间persist key
: 去掉key的过期时间127.0.0.1:6382> set hello world OK 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> expire hello 30 # 设置过期时间30s (integer) 1 127.0.0.1:6382> ttl hello # 剩余19s过期 (integer) 19 127.0.0.1:6382> ttl hello (integer) 14 127.0.0.1:6382> persist hello # 去掉key的过期时间 (integer) 1 127.0.0.1:6382> ttl hello (integer) -1 # (-1) 表示key存在,没有过期时间 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> expire hello 10 (integer) 1 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> ttl hello (integer) 4 127.0.0.1:6382> ttl hello (integer) -2 # (-2) 表示key已经不存在了 127.0.0.1:6382> get hello (nil)
type key
: 返回key的类型,string
hash
list
set
zset
none
127.0. A~~~~0.1:6382> set a b OK 127.0.0.1:6382> get a "b" 127.0.0.1:6382> type a string 127.0.0.1:6382> sadd myset 1 2 3 (integer) 3 127.0.0.1:6382> type myset set
数据结构和内部编码
单线程架构
单线程为什么这么快?
-
纯内存
-
非阻塞IO
-
避免线程切换和静态消耗
单线程注意什么?
- 一次只运行一条命令
- 拒绝长慢命令
keys
flushall
flushdb
slow lua script
mutil/exec
operate bug value(collection)
- 其实不是单线程
- fysnc file descriptor
- close file descriptor
2. 字符串类型
结构和命令
get set del 命令
get
o(1)
: 获取key对应的valueset
o(1)
: 设置key-valuedel
o(1)
: 删除key-value
incr decr incrby decrby
incr key
o(1)
: key自增1,如果key不存在,自增后get(key)=1decr key
o(1)
: key自减1,如果key不存在,自减后get(key)=-1incrby key k
o(1)
: key自增k,如果key不存在,自增后get(key)=kdecrby key k
o(1)
: key自减k,如果key不存在,自减后get(key)=-k
set setnx setxx
set key value
o(1)
: 不管key 是否存在都设置setnx key value
o(1)
: key 不存在才设置(相当于add操作)set key value xx
o(1)
: key 存在才会设置(相当于update操作)127.0.0.1:6382> set hello world OK 127.0.0.1:6382> exists hello (integer) 1 127.0.0.1:6382> setnx hello 你好 (integer) 0 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> set hello 世界 xx OK 127.0.0.1:6382> get hello "\xe4\xb8\x96\xe7\x95\x8c" 127.0.0.1:6382> exists go (integer) 0 127.0.0.1:6382> setnx go good (integer) 1 127.0.0.1:6382> get go "good" 127.0.0.1:6382> set go easy xx OK 127.0.0.1:6382> get go "easy" 127.0.0.1:6382> exists python (integer) 0 127.0.0.1:6382> set python good xx (nil) 127.0.0.1:6382> get python (nil)
mget mset
mget key1 key2 key3 ...
o(n)
: 批量获取key,原子操作mset key1 val1 key2 val2 ...
o(n)
: 批量设置key-value127.0.0.1:6382> mset go good python easy php ok OK 127.0.0.1:6382> mget go python php ok 1) "good" 2) "easy" 3) "ok" 4) (nil)
getset append strlen
getset key newvalue
o(1)
: 相当于 set key newvalue 并返回旧的值append key value
o(1)
: 将value追加到旧的value中strlen key
o(1)
: 返回字符串的长度(注意中文)127.0.0.1:6382> set hello world OK 127.0.0.1:6382> getset hello php "world" 127.0.0.1:6382> get hello "php" 127.0.0.1:6382> append hello ",java" (integer) 8 127.0.0.1:6382> strlen hello (integer) 8 127.0.0.1:6382> get hello "php,java" 127.0.0.1:6382> set hello "世界" OK 127.0.0.1:6382> strlen hello (integer) 6 127.0.0.1:6382> get hello "\xe4\xb8\x96\xe7\x95\x8c
incrbyfloat getrange setrange
incrbyfloat key 3.5
o(1)
: 增加key对应的值3.5, 如果要减少可以设置 -3.5getrange key start end
o(1)
: 获取字符串指定下标说有值setrange key index value
o(1)
: 设置字符串指定下标对应的值127.0.0.1:6382> set a 1 OK 127.0.0.1:6382> get a "1" 127.0.0.1:6382> incrbyfloat a 1.1 "2.1" 127.0.0.1:6382> incrbyfloat a -0.2 "1.9" 127.0.0.1:6382> get a "1.9" 127.0.0.1:6382> set hello world OK 127.0.0.1:6382> get hello "world" 127.0.0.1:6382> getrange hello 1 4 "orld" 127.0.0.1:6382> setrange hello 1 m (integer) 5 127.0.0.1:6382> get hello "wmrld"
2. 使用场景
- 缓存
- 计数器
- 分布式锁
- 等等
例子:
-
实现记录网站每个用户个人主页的访问量?
- incr userid:pageview (单线程:无竞争)
-
实现缓存视频的基本信息(数据源在mysql中)伪代码
-
实现分布式ID生成器
3. 哈希类型(H)
特点
- 相当于 Mapmap
- 也相当于 small redis
- field 不能相同,value可以相同
命令
hget hset hdel
hget key field
o(1)
: 获取hash key对应的field的valuehset key field value
o(1)
: 设置hash key 对应field的valuehdel key field
o(1)
: 删除hash key 对应filed127.0.0.1:6382> hset user:1:info age 23 name xiaoming (integer) 2 127.0.0.1:6382> hget user:1:info age "23" 127.0.0.1:6382> hget user:1:info name "xiaoming" 127.0.0.1:6382> hset user:1:info age 18 (integer) 0 127.0.0.1:6382> hget user:1:info age "18" 127.0.0.1:6382> hset user:1:info hobby pingpang (integer) 1 127.0.0.1:6382> hgetall user:1:info 1) "age" 2) "18" 3) "name" 4) "xiaoming" 5) "hobby" 6) "pingpang" 127.0.0.1:6382> hdel user:1:info age (integer) 1 127.0.0.1:6382> hgetall user:1:info 1) "name" 2) "xiaoming" 3) "hobby" 4) "pingpang"
hexists hlen
hexists key field
o(1)
: 判断hash key 是否有fieldhlen key
o(1)
: 获取hash key field 的数量127.0.0.1:6382> hgetall user:1:info 1) "name" 2) "xiaoming" 3) "hobby" 4) "pingpang" 127.0.0.1:6382> hexists user:1:info name (integer) 1 127.0.0.1:6382> hexists user:1:info age (integer) 0 127.0.0.1:6382> hlen user:1:info (integer) 2
hmget hmset hscan
hmget key field1 field2 field3 ...
o(n)
: 批量获取hash key的一批field对应的值hmset key field1 value1 field2 value2 ...
o(n)
: 批量设置hash key的一批field value127.0.0.1:6382> hmset user:2:info name baot age 18 OK 127.0.0.1:6382> hmget user:2:info name age 1) "baot" 2) "18"
hgetall hvals hkeys
hgetall key
o(n)
: 返回hash key 对应所有field和valuehvals key
o(n)
: 返回hash key 对应所有field 的 valuehkeys key
o(n)
: 返回hash key 对应所有field127.0.0.1:6382> hgetall user:2:info 1) "name" 2) "baot" 3) "age" 4) "18" 127.0.0.1:6382> hvals user:2:info 1) "baot" 2) "18" 127.0.0.1:6382> hkeys user:2:info 1) "name" 2) "age"
hsetnx hincrby hincrbyfloat
hsetnx key field value
o(1)
: 设置hash key对应field的value(如field已经存在,则失败)hincrby key field intCounter
o(1)
: hash key对应field的value自增intCounterhincrbyfloat key field floatCounter
o(1)
: hash key对应field的value自增floatCounter(hincrby 浮点数版)
应用示例
-
实现记录网站每个用户个人主页的访问量?
hincrby user:1:info pageview count
-
实现缓存视频的基本信息(数据源在mysql中)伪代码
hash vs string
用户信息存储的三种方案
-
方案1
-
方案2
-
方案3
-
方案比较
4. 列表类型(L)
特点
-
有序
-
可以重复
-
支持左右两边插入弹出
命令/API
rpush lpush
-
rpush key value1 value2 ... valueN
o(1~n)
: 从列表右端插入1~n个值 -
lpush key value1 value2 ... valueN
o(1~n)
: 从列表左端插入1~n个值
linsert
-
linsert key before|after value newValue
o(n)
: 在list指定的值 前|后 插入newValue
lpop rpop
lpop key
o(1)
: 在list的左端弹出一个元素rpop key
o(1)
: 在list的右端弹出一个元素
lrem
lrem key count value
o(n)
: 根据count值,从列表中删除与value相等的项- count > 0: 从左到右,删除最多count个与value相等的值
- count < 0: 从右到左,删除最多Math.abs(count)个与value相等的项
- count = 0: 删除所有与value相等的项
ltrim
-
ltrim key start end
o(n)
: 按照索引范围修剪列表
lrange
-
lrange key start end(包含end)
o(n)
: 获取列表指定索引范围的所有item
lindex
-
lindex key index
o(n)
: 获取列表指定索引的item
llen
-
llen key
o(1)
: 获取列表长度
lset
-
lset key index newValue
o(n)
: 设置列表指定索引值为newValue
blpop brpop
- 命令最后一个参数 timeout 是超时时间,单位是秒,如果 timeout 大于0,则到达指定的秒数即使没有弹出成功也会返回,如果 timeout 的值为0,则会一直阻塞等待其他连接向列表中插入元素, timeout 参数不允许为负数。
- BLPOP 命令上面提到可以跟多个列表参数,其顺序是从左往右依次判断的,找到一个非空的列表就会立即返回,如果所有的列表都不存在或者为空,则阻塞等待,正常返回时返回的类型是一个列表,一共包含两个值,第一个是操作弹出的列表,第二个是弹出的元素。
blpop key timeout
o(1)
: lpop 的阻塞版本,timeout是阻塞超时时间,timeout=0 表示一致不超时brpop key timeout
o(1)
: rpop 的阻塞版本,timeout是阻塞超时时间,timeout=0 表示一致不超时localhost:6382> blpop mylist 30 1) "mylist" 2) "0" localhost:6382> blpop mylist 30 1) "mylist" 2) "0" (7.84s) localhost:6382> blpop mylist 0 1) "mylist" 2) "123" (6.38s) localhost:6382> blpop mylist1 mylist 0 1) "mylist1" 2) "0" (3.98s) localhost:6382> blpop mylist1 mylist 0 1) "mylist" 2) "123" (5.81s)
使用提示
- 当做栈(Stack)来使用:
lpush + lpop = Stack
- 队列:
lpush + rpop = Queue
- 固定数量的列表:
lpush + ltrim = Capped Collection
- 实现消息队列:
lpush + brpop = Message Queue
列表命令使用示例
127.0.0.1:6382> rpush mylist a b c
(integer) 3
127.0.0.1:6382> lrange mylist 0 -1
1) "a"
2) "b"
3) "c"
127.0.0.1:6382> lpush mylist 0
(integer) 4
127.0.0.1:6382> lrange mylist 0 -1
1) "0"
2) "a"
3) "b"
4) "c"
127.0.0.1:6382> rpop mylist
"c"
127.0.0.1:6382> lrange mylist 0 -1
1) "0"
2) "a"
3) "b"
5. 集合类型(S)
特点
- 无序
- 无重复
- 集合间操作
集合内API和实战
sadd srem
sadd key element
o(1)
: 向集合key添加element(如果element已存在,添加失败)srem key element
o(1)
: 向集合key移除element(如果element不存在,移除失败)
scard sismember srandmember spop smembers sscan
scard key
: 计算集合大小sismember key member
: 判断 member 是否存在于集合 key 中srandmember key count
: 随机从集合 key 中,挑出count个元素spop key
: 随机从集合 key 中,弹出一个元素 (元素会被移除)smembers key
: 获取集合所有元素127.0.0.1:6382> sadd set a b c (integer) 3 127.0.0.1:6382> smembers set 1) "c" 2) "b" 3) "a" 127.0.0.1:6382> srandmember set 2 1) "c" 2) "b" 127.0.0.1:6382> smembers set 1) "c" 2) "b" 3) "a" 127.0.0.1:6382> spop set "a" 127.0.0.1:6382> smembers set 1) "c" 2) "b" 127.0.0.1:6382> scard set (integer) 2 127.0.0.1:6382> sismember set a (integer) 0 127.0.0.1:6382> sismember set b (integer) 1
sscan key cursor [MATCH pattern] [COUNT count]
: sscan命令是一个对set的基于游标的迭代器127.0.0.1:6382> smembers set 1) "2" 2) "b" 3) "c" 4) "5" 5) "3" 6) "4" 7) "1" 127.0.0.1:6382> sscan set 0 count 2 1) "2" 2) 1) "4" 2) "1" 127.0.0.1:6382> sscan set 2 count 2 1) "0" 2) 1) "c" 2) "2" 3) "b" 4) "5" 5) "3"
集合间API和实战
sdiff sinter sunion
sdiff set_key1 set_key2
: 计算 set_key1 - set_key2 的差集sinter set_key1 set_key2
: 计算 set_key1 和 set_key2 的交集sunion set_key1 set_key2
: 计算 set_key1 + set_key2 的并集sdiff|sinter|sunion + store destkey set_key1 set_key2
: 将set_key1 set_key2的差集、交集、并集的结果保存在 destkey中127.0.0.1:6382> smembers user:1:hobbys 1) "happy" 2) "play" 3) "drink" 4) "eat" 127.0.0.1:6382> smembers user:2:hobbys 1) "love" 2) "study" 3) "eat" 4) "play" 127.0.0.1:6382> 127.0.0.1:6382> sdiff user:1:hobbys user:2:hobbys 1) "happy" 2) "drink" 127.0.0.1:6382> sdiff user:2:hobbys user:1:hobbys 1) "study" 2) "love" 127.0.0.1:6382> sinter user:1:hobbys user:2:hobbys 1) "play" 2) "eat" 127.0.0.1:6382> sunion user:1:hobbys user:2:hobbys 1) "study" 2) "love" 3) "eat" 4) "happy" 5) "play" 6) "drink" 127.0.0.1:6382> sunionstore unionhobbys user:1:hobbys user:2:hobbys (integer) 6 127.0.0.1:6382> smembers unionhobbys 1) "study" 2) "love" 3) "eat" 4) "happy" 5) "play" 6) "drink"
使用示例
- 抽奖系统
- 创建一个集合,加入用id, 通过spop or srandmember
- 标签
- 给用户添加标签
sadd users:1:tags tag1 tag2 tag5 sadd users:2:tags tag2 tag4 sadd users:k:tags tag3 tag5 tag6
- 给标签添加用户
sadd tag1:users user:1 user:3 sadd tag2:users user:2 user:3 sadd tagk:users user:3 user:4
- 给用户添加标签
- 网站中 计算共同关注的好友、兴趣等等?
使用场景
sadd
: 用在标签等场景spop/srandmember
: 用在抽奖等随机数场景sadd + sinter
: 社交相关的应用
6. 有序集合类型(Z)
命令/API
zadd zrem
zadd key score element
o(logN)
: 添加score和elementzrem key element
o(1)
: 删除元素
zscore
zscore key element
o(1)
: 返回元素的分数
zincrby
zincrby key increScore element
o(1)
: 增加或减少元素的分数(increScore为负数表示减少)
zcard zrank
zcard key
o(1)
: 返回元素的总个数zrank key element
o(1)
: 返回 element 的排名
zrange zrangebyscore
zrange key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
-
zrange key start end withscores
o(log(n)+m)
: 返回指定索引范围内的升序元素[分值] -
zrangebyscore key minScore maxScore [WITHSCORES]
o(log(n)+m)
: 返回指定分数范围内的升序元素[分值]127.0.0.1:6382> zadd player:rank 1000 ronaldo 900 messi 800 c-ronaldo 600 kaka (integer) 4 127.0.0.1:6382> zscore player:rank ronaldo "1000" 127.0.0.1:6382> zcard player:rank (integer) 4 127.0.0.1:6382> zrank player:rank c-ronaldo (integer) 1 127.0.0.1:6382> zrank player:rank kaka (integer) 0 127.0.0.1:6382> zrank player:rank ronaldo (integer) 3 127.0.0.1:6382> zrem player:rank c-ronaldo (integer) 1 127.0.0.1:6382> zrange player:rank 0 -1 withscores 1) "kaka" 2) "600" 3) "messi" 4) "900" 5) "ronaldo" 6) "1000"
zcount
zcount key minScore maxScore
: 返回有序集合内在指定分数范围内的元素个数
zremrangebyrank zrenrangebyscore
-
zremrangebyrank key start end
o(log(n)+m)
: 删除指定排名内的升序元素 -
zrenrangebyscore key minScore maxScore
o(log(n)+m)
: 删除指定分数内的升序元素127.0.0.1:6382> zadd player:rank 1000 ronaldo 900 messi 800 c-ronaldo 600 kaka 127.0.0.1:6382> zrange player:rank 0 -1 1) "kaka" 2) "c-ronaldo" 3) "messi" 4) "ronaldo" 127.0.0.1:6382> zcount player:rank 700 901 (integer) 2 127.0.0.1:6382> zrangebyscore player:rank 700 900 withscores 1) "c-ronaldo" 2) "800" 3) "messi" 4) "900" 127.0.0.1:6382> zremrangebyrank player:rank 0 1 (integer) 2 127.0.0.1:6382> zrange player:rank 0 -1 1) "messi" 2) "ronaldo" 127.0.0.1:6382> zrange player:rank 0 -1 withscores 1) "messi" 2) "900" 3) "ronaldo" 4) "1000"
zrevrank zrevrange zrevrangebyscore
- 与 zrank zrange zrangebyscore 对应,只是排序是按照score的由高到低的降序
zdiff zinter zunion zdiffstore zinterstore zunionstore
- 参考集合命令
应用场景
- 排行榜
Redis 命令补充
dbsize
: 当前数据库的 key 的数量info memory
: 内存使用量127.0.0.1:6382> dbsize (integer) 27 127.0.0.1:6382> info memory # Memory used_memory:878576 used_memory_human:857.98K
config get appendonly
: 获取配置信息config set appendonly yes
: 修改配置信息config rewrite
: 将config信息写入到config文件中info [section]
: 查看 CPU 内存 持久化 等信息info Persistence # 查看持久化信息