简介
Redis 是一种基于键值对( key - value) 的
NoSQL
数据库,面向快速执行
场景。
Redis 的数据结构有 String
、hash
、list
、set
、zset、Bitmaps、HyperLogLog、GEO,常用的是前四种。 Redis 提供了简单的事务功能,将一组需要一起执行的命令放到 multi
和 exec
两个命令之间。multi
命令代表事务开始,exec
命令代表事务结束,它们之间的命令是原子顺序执行的 。Redis 不支持回滚,搭配 Lua
脚本才能实现相关功能;最大的特点是快,基于以下几个方面
- 所有数据都是存放在内存( 不适合存放大的数据量 )
- C 语言实现,执行速度相对会快
- 使用单线程,减少线程切换的开销( 最新版本的 redis 有些操作也用多线程)
- 源代码优雅
redis 的数据都放在内存中是不安全的,为了应对异常(断电或者机器故障),也提供持久化方式:RDB 、AOF。
RDB
:当前进程数据生成快照保存到硬盘,触发 RDB 持久化过程分为手动和自动。AOF
:以独立日志的方式记录每次写命令,重启时执行AOF 文件来恢复数据,解决了数据持久化的实时性是当前主流方式。
值得一提的是,在 redis 3.0
版本开始实现了真正的分布式。
安装
以下为源码方式安装
[hadoop@danner000 software]$ wget http://download.redis.io/releases/redis-3.0.7.tar.gz
[hadoop@danner000 software]$ tar -zxvf redis-3.0.7.tar.gz -C ../app/
[hadoop@danner000 software]$ cd ../app/
# 创建软链接
[hadoop@danner000 app]$ ln -s redis-3.0.7/ redis
[hadoop@danner000 redis]$ cd redis
[hadoop@danner000 redis]$ make
# redis 可执行文件移动到 /usr/local/bin 目录 需要 root 权限
[root@danner000 redis]# make install
[hadoop@danner000 src]$ ls -l /usr/local/bin/ | grep redis
-rwxr-xr-x. 1 root root 4589171 Oct 24 11:19 redis-benchmark
-rwxr-xr-x. 1 root root 22225 Oct 24 11:19 redis-check-aof
-rwxr-xr-x. 1 root root 45443 Oct 24 11:19 redis-check-dump
-rwxr-xr-x. 1 root root 4698386 Oct 24 11:19 redis-cli
lrwxrwxrwx. 1 root root 12 Oct 24 11:19 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 6471167 Oct 24 11:19 redis-server
# 安装成功可输出版本号
[hadoop@danner000 src]$ redis-cli -v
redis-cli 3.0.7
编译安装后会在 redis 的 src 目录多几个可执行文件,简单介绍下
可执行文件 | 作用 |
---|---|
redis-server | 启动 redis |
redis-cli | redis 命令行客户端 |
redis-benchmark | redis 基准测试工具 |
redis-check-aof | redis AOF 持久化文件检测和修复工具 |
redis-check-dump | redis RDB 持久化文件检测和修复工具 |
redis-sentinel | 启动 redis sentinel |
服务启停
# 启动服务
[hadoop@danner000 redis]$ redis-server redis.conf
# 指定配置文件启动,占用控制台窗口
# 修改配置 redis.conf,以守护进程方式启动,日志输出到 redis.log
daemonize yes
logfile "/home/hadoop/app/redis/logs/redis.log"
# 此时启动不占用控制台窗口
[hadoop@danner000 redis]$ redis-server redis.conf
# 关闭服务
[hadoop@danner000 redis]$ redis-cli shutdown
# 查看日志
86567:M 24 Oct 14:42:45.338 # User requested shutdown...
86567:M 24 Oct 14:42:45.338 * Saving the final RDB snapshot before exiting.
86567:M 24 Oct 14:42:45.352 * DB saved on disk
86567:M 24 Oct 14:42:45.352 * Removing the pid file.
86567:M 24 Oct 14:42:45.352 # Redis is now ready to exit, bye bye...
# 正常关闭会端口客户端连接并生成持久化文件
# 不要用 kill 去杀死redis 服务,这样不会生成持久化文件,还有可能造成资源不释放
客户端
客户端有两种方式连接服务:
- 交互式: redis-cli -h {host} -p {port}
[hadoop@danner000 redis]$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> keys *
(empty list or set)
- 命令式:redis-cli -h {host} -p {port} command
# 直接获取 hello 键的值
[hadoop@danner000 redis]$ redis-cli -h 127.0.0.1 -p 6379 get hello
"world"
全局命令
- 查看所有键
keys *
127.0.0.1:6379> keys *
1) "hello"
2) "myCounter"
- 键总数
dbsize
127.0.0.1:6379> dbsize
(integer) 2
dbsize
是直接获取内置的键数量,复杂度是 O(1)
;keys *
是去遍历所有键,复杂度是 O(n)
,所以线上环境禁止使用,建议是使用 scan
scan
:迭代扫描
# 给一个起点,每次扫描一部分,知道扫描结束
SCAN cursor [MATCH pattern] [COUNT count]
127.0.0.1:6379> scan 0
1) "15"
2) 1) "hello2"
2) "listkey"
3) "hello10"
4) "hello8"
5) "hello1"
6) "hello"
7) "hello9"
8) "hello5"
9) "hello4"
10) "hello3"
11) "hello7"
# 15 是上次 scan 返回的游标,0 表示扫描完毕
127.0.0.1:6379> scan 15
1) "0"
2) 1) "hello6"
hscan
、sscan
、zscan
是解决哈希、集合、有序集合遍历造成阻塞问题的命令
- 检测key 是否存在
exists key
# key 存在返回 1,不存在返回 0
127.0.0.1:6379> exists hello
(integer) 1
127.0.0.1:6379> exists hell
(integer) 0
- 删除 key
del key
127.0.0.1:6379> del hello
(integer) 1
127.0.0.1:6379> exists hello
(integer) 0
- 对key 设置生命周期
expire key
127.0.0.1:6379> set hello world
OK
# hello key 活 10 s
127.0.0.1:6379> expire hello 10
(integer) 1
# ttl 查看 key 还有多长时间,单位 s
127.0.0.1:6379> ttl hello
(integer) 6
# 返回 -2 表示 key 不存在
127.0.0.1:6379> ttl hello
(integer) -2
# 常用于超大数据集中小数据集的缓存:用到时放到 redis ,过期自动删除
- 查看key 数据结构
127.0.0.1:6379> type myCounter
string
数据结构
type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合),但这些只是 Redis 对外的数据结构 ,如下所示
每种数据结构都有自己底层的内部编码的多种实现,可以在不同的场景选择合适的编码。
# object encoding 命令可查询内部编码
127.0.0.1:6379> object encoding myCounter
"int"
字符串
字符串是 redis 中最基础的数据结构,所有的 key 都是字符串并且其他几种数据结构的都是在字符串的基础上构建的。交互模式下输入 help @string
可输出所有相关指令,下面挑三几个常用来演示
set
: 给 key 赋值,可以带生命周期SET key value [EX seconds] [PX milliseconds] [NX|XX]
SETNX key value
,key 不存在才能设置成功SETEX key seconds value
,必须带生命周期(setex = set + ex)MSET key value [key value ...]
,批量设置 key 值
# redis 生命周期 10 s
127.0.0.1:6379> set redis time ex 10
OK
127.0.0.1:6379> ttl redis
(integer) 2
127.0.0.1:6379> set hello world
OK
# 已存在的key 无法用 setnx 赋值
127.0.0.1:6379> setnx hello world1
(integer) 0
127.0.0.1:6379> setnx java ee
(integer) 1
127.0.0.1:6379>
# mset
127.0.0.1:6379> keys *
1) "java"
2) "hello"
3) "myCounter"
127.0.0.1:6379> mset hello1 world1 hello2 world2
OK
127.0.0.1:6379> keys *
1) "java"
2) "hello2"
3) "hello"
4) "hello1"
5) "myCounter"
get
:获取 key 值GET key
MGET key [key ...]
:批量获取,生产中建议使用该方法可以减少多次命令带来的网络消耗
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> mget hello hello1 hello2
1) "world"
2) "world1"
3) "world2"
incr
:计数INCR key
:自增1,若 value 不是数字类型则报错INCRBY key increment
: + incrementINCRBYFLOAT key increment
:+ float increment ,DECR key
:自减1DECRBY key decrement
:- decrement
127.0.0.1:6379> incr myCounter
(integer) 2
127.0.0.1:6379> incrby myCounter 10
(integer) 12
127.0.0.1:6379> incrbyfloat myCounter 6.0
"18"
127.0.0.1:6379> decr myCounter
(integer) 17
127.0.0.1:6379> decrby myCounter 3
(integer) 14
- 字符串相关操作
STRLEN key
:获取值长度APPEND key value
:追加字符串GETRANGE key start end
:substrSETRANGE key offset value
:替换某个位置的值
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> strlen hello
(integer) 5
127.0.0.1:6379> append hello redis
(integer) 10
127.0.0.1:6379> get hello
"worldredis
# 注意,redis 都是闭包
127.0.0.1:6379> getrange hello 2 6
"rldre"
127.0.0.1:6379> setrange hello 3 h
(integer) 10
127.0.0.1:6379> get hello
"worhdredis"
内部编码
int
:少于八个字节的数字embstr
:小于39个字节的字符串raw
:大于39个字节的字符串 or 字符串有操作之后
127.0.0.1:6379> set intkey 123
OK
127.0.0.1:6379> object encoding intkey
"int"
127.0.0.1:6379> object encoding java
"embstr"
127.0.0.1:6379> append java web
(integer) 5
# append 后自动转化为 raw
127.0.0.1:6379> object encoding java
"raw"
常用于热数据的缓存
hash
哈希类型是指键值本身又是一个键值对结构,主要是针对 field
操作
哈希的命令都是 h
开头,执行 help @hash
查看相关指令
hset
:设置 field 值HSET key field value
HMSET key field value [field value ...]
:设置多个 field
# 相当于是给用户1 设置name = john
127.0.0.1:6379> hset user:1 name john
(integer) 1
hget
:获取field 值HGET key field
HMGET key field [field ...]
:获取多个 field
# 获取用户1 的name 值
127.0.0.1:6379> hget user:1 name
"john"
# 无对应key,返回 nil
127.0.0.1:6379> hget user:100 name
(nil)
hexists
:是否存在 fieldHEXISTS key field
127.0.0.1:6379> hexists user:1 name
(integer) 1
hdel
:删除 fieldHDEL key field [field ...]
: 可删除多个 field
127.0.0.1:6379> hdel user:1 name
(integer) 1
- ` hlen`:计算 field 个数
HLEN key
127.0.0.1:6379> hset user:1 age 20
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 2
hvals
:获取所有 field 值HVALS key
127.0.0.1:6379> hvals user:1
1) "john"
2) "20"
hgetall
:获取所有 field-valueHGETALL key
127.0.0.1:6379> hgetall user:1
1) "name"
2) "john"
3) "age"
4) "20"
如果有很多 field ,不建议使用 hgetall
,可能会造成 redis 堵塞;如果是只需要部分 field ,可使用 hmget
;或者使用 hscan
内部编码
ziplist
- 哈希元素个数小于 ` hash-max-ziplist-entries `(默认 512 个)
- 所有值小于 ` hash-max-ziplist-value `(默认 64 字节)
- 结构比 hashtable 更紧凑,内存更省
hashtable
- 不满足
ziplist
情况下使用hashtable
- hashtable 读写复杂度
O(1)
- 不满足
127.0.0.1:6379> object encoding user:1
"ziplist"
# field 值大于 64字节
127.0.0.1:6379> hset user:1 name
"adasdsadasdadasdadsadasdasdasdasdasdasdasdsadasdasdasdasdasdffdsfsdf"
(integer) 0
# 变为 hashtable
127.0.0.1:6379> object encoding user:1
"hashtable"
常用于缓存表数据
List
列表类型存储多个有序的字符串,可以当成栈使用,最多存储 231
个元素。
help @list
查看相关命令,大多数命令是L
开头
- 添加操作
RPUSH key value [value ...]
:右边插入数据LPUSH key value [value ...]
:左边插入数据LINSERT key BEFORE|AFTER pivot value
:指定元素的前/后插入数据
127.0.0.1:6379> lpush listkey q
(integer) 1
127.0.0.1:6379> lpush listkey w
(integer) 2
# listkey 值为 "w,q,a"
127.0.0.1:6379> rpush listkey a
(integer) 3
# listkey 值为 "w,q,123,a"
127.0.0.1:6379> linsert listkey after q 123
(integer) 4
# linsert 只插入一次
- 查询操作
LRANGE key start stop
:查询 start - stop 的数据LINDEX key index
:获取 index 的值LLEN key
:获取 list 长度
# 此方法可以查询整个list
127.0.0.1:6379> lrange listkey 0 -1
1) "w"
2) "66"
3) "q"
4) "66"
5) "123"
6) "a"
7) "q"
127.0.0.1:6379> lindex listkey 3
"66"
127.0.0.1:6379> llen listkey
(integer) 7
- 删除操作
LPOP key
:左侧弹出一个元素RPOP key
:右侧弹出一个元素LREM key count value
:删除指定的元素LTRIM key start stop
:获取子集BLPOP key [key ...] timeout
:阻塞的方式左侧弹出元素,若无数据等待 timeout 再返回,timerout = 0 则一直等待BRPOP key [key ...] timeout
:阻塞的方式右侧弹出元素
127.0.0.1:6379> lpop listkey
"w"
127.0.0.1:6379> rpop listkey
"q"
127.0.0.1:6379> lrange listkey 0 -1
1) "66"
2) "q"
3) "66"
4) "123"
5) "a"
# 删除 3个 66,虽然list 只有2个,但也不会出错
127.0.0.1:6379> lrem listkey 3 66
(integer) 2
127.0.0.1:6379> lrange listkey 0 -1
1) "q"
2) "123"
3) "a
# 相当于是删除 第0 个元素
127.0.0.1:6379> ltrim listkey 1 -1
OK
# 返回值是 key + value
127.0.0.1:6379> blpop listkey 3
1) "listkey"
2) "q"
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "a"
- 修改操作
LSET key index value
:修改指定位置元素
127.0.0.1:6379> lset listkey 1 java
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "q"
2) "java"
3) "a"
内部编码
ziplist
- 元素个数小于 ` hash-max-ziplist-entries `(默认 512 个)
- 所有值小于 ` hash-max-ziplist-value `(默认 64 字节)
- 结构比 linkedlist 更紧凑,内存更省
- ` linkedlist `
ziplist
无法满足时使用linkedlist
127.0.0.1:6379> object encoding listkey
"ziplist"
127.0.0.1:6379> lpush listkey aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 3
127.0.0.1:6379> object encoding listkey
"linkedlist"
使用场景:
- 消息队列:lpush + brpop
- 队列: lpush+rpop
- 栈: lpush+lpop
- 有限集合:lpush+ltrim
Set
集合也可以可以保存多个元素,但不同 list
不允许重复元素且是无序的不支持下标取元素。help @set
- 元素操作
SADD key member [member ...]
:添加元素SREM key member [member ...]
:删除元素SCARD key
:获取元素个数SISMEMBER key member
:集合中元素是否存在SRANDMEMBER key [count]
:集合中随机返回几个元素SMEMBERS key
:获取几个所有元素
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> srem setkey a
(integer) 1
127.0.0.1:6379> scard setkey
(integer) 1
# 返回0 不存在
127.0.0.1:6379> sismember setkey c
(integer) 0
127.0.0.1:6379> srandmember setkey 3
1) "s"
2) "x"
3) "b"
127.0.0.1:6379> smembers setkey
1) "z"
2) "s"
3) "x"
4) "b"
5) "d"
- 集合操作
SINTER key [key ...]
:多个集合的交集SUNION key [key ...]
:多个集合并集SDIFF key [key ...]
:多个集合差集SINTERSTORE destination key [key ...]
:多个集合的交集保存SUNIONSTORE destination key [key ...]
:多个集合并集保存SDIFFSTORE destination key [key ...]
:多个集合差集保存
内部编码
intset
:整数集合- 元素个数小于 ` hash-max-ziplist-entries `(默认 512 个)
- 元素必须是整数
- 省内存
hashtable
:哈希表- 不符合
intset
的情况
- 不符合
127.0.0.1:6379> sadd setkey1 1
(integer) 1
127.0.0.1:6379> object encoding setkey1
"intset"
127.0.0.1:6379> sadd setkey1 2.9
(integer) 1
# 添加浮点数后变为 hashtable
127.0.0.1:6379> object encoding setkey1
"hashtable"
集合的典型的应用是用户标签
参考资料
Redis 开发与运维