Redis笔记——技术点汇总
目录
· 特点
· 安装
· 数据库
· 服务器命令
· 数据结构
· string
· list
· set
· hash
· zset
· 发布与订阅
· 排序
· 事务
· pipeline
· 基准测试
· 键的过期
· 持久化
· 概况
· AOF
· 主从复制
· HA
· Lua
· 示例:分布式日志
特点
1. Redis是一个开源的、C语言编写的、面向键值对类型数据的分布式NoSQL数据库系统。
2. 特点:高性能(内存数据库,随机读写非常快)、持久存储,适应高并发应用场景。
3. 对比:一些数据库和缓存服务器的特性与功能。
名称 |
类型 |
数据存储 |
查询类型 |
附加功能 |
Redis |
使用内存存储(in-memory)的非关系数据库 |
字符串、列表、集合、散列、有序集合 |
每种数据类型都有自己的专属命令,另外还有批量操作(buld operation)和不完全(partial)事务支持 |
发布与订阅,主从复制(master/slave replication),持久化,脚本 |
memcached |
使用内存存储的键值缓存 |
键值之间的映射 |
创建命令、读取命令、更新命令、删除命令以及其他几个命令 |
为提升性能而设的多线程服务器 |
MySQL |
关系数据库 |
每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展 |
SELECT、INSERT、UPDATE、DELETE、函数、存储过程 |
支持ACID(InnoDB),主从复制和主主复制 |
PostgreSQL |
关系数据库 |
每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间(spatial)和第三方扩展;支持定制类型 |
SELECT、INSERT、UPDATE、DELETE、函数、存储过程 |
支持ACID(InnoDB),主从复制,由第三方支持的多主复制 |
MongoDB |
使用硬盘存储(on-disk)的非关系数据库 |
每个数据库可以包含多个表,每个表可以包含多个无schema(schema-less)的BSON文档 |
创建命令、读取命令、更新命令、删除命令、条件查询命令等 |
支持map-reduce操作,主从复制,分片,空间索引(spatial index) |
4. 性能测试结果:set操作每秒可达110000次,get操作每秒81000次(与服务器配置有关)。
安装
1. 安装。
tar zxvf redis-3.2..tar.gz
cd redis-3.2..tar.gz
yum install gcc # 安装依赖
cd deps
make hiredis lua jemalloc linenoise geohash-int
cd ..
make # 编译
2. 配置。
vi redis.conf
# bind 127.0.0.1 # 不绑定表示监听所有IP
protected-mode no # 无密码
daemonize yes # 后台运行
logfile "/opt/app/redis-3.2.0/logs/redis.log" # 日志文件路径
dir "/opt/app/redis-3.2.0/data/" # 快照文件路径
appendonly yes # 开启AOF
3. 启动、关闭。
src/redis-server redis.conf # 启动
src/redis-cli # 客户端
src/redis-cli shutdown # 关闭
数据库
1. Redis默认有16个数据库。
2. 数据库个数配置项:databases。
3. 切换数据库命令:
127.0.0.1:> select
OK
127.0.0.1:> select
OK
127.0.0.1:[]>
服务器命令
命令 |
说明 |
dbsize |
获取当前数据库中键的个数 |
info |
获取服务器信息 |
select |
切换数据库 |
config get |
config get config-key,获取配置项config-key的值 |
数据类型及其操作命令
数据结构
1. 存储键与5种不同数据结构类型之间的映射。
2. 键是string类型。
3. 5种数据类型:string、list、set、hash、zset。
4. 命令:部分命令(如del、type、rename)对于5种类型通用;部分命令只能对特定的一种或者两种类型使用。另注:有很多命令尾部带“nx”表示不存在键时才执行。
5. 常用通用命令。
命令 |
说明 |
keys |
keys pattern,获取满足pattern的所有键,支持通配符星号“*” |
exists |
exists key,判断键key是否存在 |
del |
del key,删除键key |
expire |
设置键的过期时间(后面详细介绍) |
move |
move key database,将键key移动到数据库database |
rename |
rename old-key new-key,将键old-key重命名为new-key |
type |
type key,获取键的数据结构 |
string
1. 可以是字符串、整数或浮点数。
2. Redis的字符串是由字节组成的序列。
3. 对于整数、浮点数的字符串可执行自增和自减;对无法解释成整数或浮点数的字符串执行自增或自减会返回错误。
4. 常用命令。
命令 |
说明 |
get |
获取给定键的值 |
set |
设置给定键的值 |
incr |
incr key-name,将键存储的值加上1 |
decr |
decr key-name,将键存储的值减去1 |
incrby |
incrby key-name amount,将键存储的值加上整数amont |
decrby |
decrby key-name amount,将键存储的值减去整数amont |
incrbyfloat |
incrbyfloat key-name amount,将键存储的值加上浮点数amont |
append |
append key-name value,将值value追加到给定键key-name当前存储的值的末尾 |
getrange |
getrange key-name start end,获取一个偏移量start至偏移量end范围内所有字符组成的子串,包括start和end在内 |
setrange |
setrange key-name offset value,将从start偏移量开始的子串设置为给定值 |
getbit |
getbit key-name offset value,将字节串看作是二进制位串(bit string),并返回位串中偏移量为offset的二进制位的值 |
setbit |
setbit key-name offset value,将字节串看作是二进制位串,并将位串中偏移量为offset的二进制位的值设置为value |
bitcount |
bitcount key-name [start end],统计二进制位串里面值为1的二进制位的数量,如果给定了可选的start偏移量和end偏移量,那么只对偏移量指定范围内二进制位进行统计 |
bitop |
bitop operation dest-key key-name [key-name ...],对一个或多个二进制位串执行包括并and、或or、异或xor、非not在内的任意一种按位运算,并将计算结果保存在dest-key键里面 |
5. 举例。
127.0.0.1:> set hello world
OK
127.0.0.1:> get hello
"world"
127.0.0.1:> del hello
(integer)
127.0.0.1:> get hello
(nil)
127.0.0.1:> set num
OK
127.0.0.1:> incrby num
(integer)
127.0.0.1:> append num abc
(integer)
127.0.0.1:> get num
"110abc"
127.0.0.1:> getrange num
"0ab"
list
1. Redis的list是链表(linked-list)。
2. 应用:列表、栈、队列、消息队列MQ等。
3. 命令。
命令 |
说明 |
rpush |
rpush key-name value [value ...],将一个或多个值推入列表的右端 |
lpush |
lpush key-name value [value ...],将一个或多个值推入列表的左端 |
rpop |
rpop key-name,移除并返回列表最右端的元素 |
lpop |
lpop key-name,移除并返回列表最左端的元素 |
lindex |
lindex key-name offset,返回列表中偏移量为offset的元素 |
lrange |
lrange key-name start end,返回列表从start偏移量到end偏移量范围内的所有元素,其中偏移量为start和偏移量为end的元素也会包含在内 |
ltrim |
ltrim key-name start end,对列表进行修剪,只保留从start偏移量到end偏移量范围内的元素,其中偏移量为start和偏移量为end的元素也会被保留 |
blpop |
blpop key-name [key-name…] timeout,从第一个非空列表中弹出位于最左端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现 |
brpop |
brpop key-name [key-name…] timeout,从第一个非空列表中弹出位于最右端的元素,或者在timeout秒之内阻塞并等待可弹出的元素出现 |
rpoplpush |
rpoplpush source-key dest-key,从source-key列表中弹出位于最右端的元素,然后将这个元素推入dest-key列表的最左端,并向用户返回这个元素 |
brpoplpush |
brpoplpush source-key dest-key timeout,从source-key列表中弹出位于最右端的元素,然后将这个元素推入dest-key列表的最左端,并向用户返回这个元素;如果source-key为空,那么在timeout秒之内阻塞并等待可弹出的元素出现 |
4. 举例。
127.0.0.1:> rpush list-key item1
(integer)
127.0.0.1:> rpush list-key item2 item1
(integer)
127.0.0.1:> lpush list-key item0
(integer)
127.0.0.1:> lrange list-key -
) "item0"
) "item1"
) "item2"
) "item1"
127.0.0.1:> lindex list-key
"item1"
127.0.0.1:> lpop list-key
"item0"
127.0.0.1:> ltrim list-key
OK
127.0.0.1:> lrange list-key -
) "item1"
) "item2"
set
1. list允许有重复值,set不允许有重复值。
2. list是有序的,set是无序的。
3. set通过hash保证值不重复(这些hash表只有键,没有与键对应的值)。
4. 应用:去重列表、集合运算(交、并、差集)。
5. 命令。
命令 |
说明 |
sadd |
sadd key-name item [item...],将一个或多个元素添加到集合里面,并返回被添加元素当中原本并不存在于集合里面的元素数量 |
srem |
srem key-name item [item...],从集合里面移除一个或多个元素,并返回被移除元素的数量 |
sismember |
sismember key-name item,检查元素item是否存在于集合key-name里 |
scard |
scard key-anem,返回集合包含的元素数量 |
smembers |
smembers key-name,返回集合包含的所有元素 |
srandmember |
srandmember key-name [count],从集合里面随机地返回一个或多个元素。当count为正数时,命令返回的随机元素不会重复;当count为负数时,命令返回的随机元素可能会出现重复 |
spop |
spop key-name,随机地移除集合中一个元素,并返回移除的元素 |
smove |
smove source-key dest-key item,如果集合source-key包含元素item,那么从集合source-key里面移除元素item,并将元素item添加到集合dest-key中;如果item被成功移除,那么命令返回1,否则返回0 |
sdiff |
sdiff key-name [key-name…],返回那些存在于第一个集合但不存在于其他集合中的元素(数学上的差集运算) |
sdiffstore |
sdiffstore dest-key key-name [key-name…],将那些存在于第一个集合但不存在于其他集合中的元素(数学上的差集运算)存储到dest-key键里面 |
sinter |
sinter key-name [key-name…],返回那些同时存在于所有集合的元素(数学上的交集运算) |
sinterstore |
sinterstore dest-key key-name [key-name…],将那些同时存在于所有集合的元素(数学上的交集运算)存储到dest-key键里面 |
sunion |
sunion key-name [key-name…],返回那些至少存在于一个集合中的元素(数学上的并集运算) |
sunionstore |
sunionstore dest-key key-name [key-name…],将那些至少存在于一个集合中的元素(数学上的并集运算)存储到dest-key键里面 |
6. 举例。
127.0.0.1:> sadd set-key item0
(integer)
127.0.0.1:> sadd set-key item1 item2
(integer)
127.0.0.1:> sadd set-key item0
(integer)
127.0.0.1:> smembers set-key
) "item2"
) "item1"
) "item0"
127.0.0.1:> sismember set-key item3
(integer)
127.0.0.1:> sismember set-key item0
(integer)
127.0.0.1:> srem set-key item2
(integer)
127.0.0.1:> srem set-key item2
(integer)
127.0.0.1:> smembers set-key
) "item1"
) "item0"
hash
1. Redis的散列可以存储多个键值对之间的映射,在很多方面就像是一个微缩版的Redis。
2. 命令。
命令 |
说明 |
hset |
在散列里面关联起给定的键值对 |
hget |
获取指定散列键的值 |
hmget |
hmget key-name key [key...],从散列里面获取一个或多个键的值 |
hmset |
hmget key-name key value [key value...],为散列里面的一个或多个键设置值 |
hgetall |
获取散列包含的所有键值对 |
hdel |
如果给定键存在于散列里面,那么移除这个键 |
hlen |
hlen key-name,返回散列包含的键值对数量 |
hexists |
hexists key-name key,检查给定键是否存在于散列中 |
hkeys |
hkeys key-name,获取散列包含的所有键 |
hvals |
hvals key-name,获取散列包含的所有值 |
hincrby |
hincrby key-name key increment,将键key保存的值加上整数increment |
hincrbyfloat |
hincrbyfloat key-name key increment,将键key保存的值加上浮点数increment |
3. 举例。
127.0.0.1:> hset hash-key sub-key0 value0
(integer)
127.0.0.1:> hset hash-key sub-key1 value1
(integer)
127.0.0.1:> hmset hash-key sub-key2 value2 sub-key3 value3
OK
127.0.0.1:> hset hash-key sub-key0 value0
(integer)
127.0.0.1:> hgetall hash-key
) "sub-key0"
) "value0"
) "sub-key1"
) "value1"
) "sub-key2"
) "value2"
) "sub-key3"
) "value3"
127.0.0.1:> hdel hash-key sub-key3
(integer)
127.0.0.1:> hmget hash-key sub-key0 sub-key1
) "value0"
) "value1"
127.0.0.1:> hget hash-key sub-key2
"value2"
127.0.0.1:> hkeys hash-key
) "sub-key0"
) "sub-key1"
) "sub-key2"
127.0.0.1:> hvals hash-key
) "value0"
) "value1"
) "value2"
4. 应用:可以把hash看作关系数据库的行,hash中的key为字段名,hash中的value为字段值。以用户为例,新增/查询ID为1和2的两个用户:
127.0.0.1:> hmset user: name zhangsan age
OK
127.0.0.1:> hmset user: name lisi age
OK
127.0.0.1:> hgetall user:
) "name"
) "zhangsan"
) "age"
) ""
127.0.0.1:> hgetall user:
) "name"
) "lisi"
) "age"
) ""
zset
1. zset和hash一样,都用于存储键值对。
2. zset的键称为成员(member),不允许重复。
3. zset的值称为分值(score),必须是浮点数。
4. zset既可以根据member访问元素(与hash相同),也可以根据分值及分值的排序顺序来访问元素。
5. 应用:排序、去重。
6. 命令。
命令 |
说明 |
zadd |
zadd key-name score member [score member...],将带有给定分值的成员添加到有序集合里面 |
zrem |
zrem key-name member [member...],从有序集合里面移除给定的成员,并返回被移除成员的数量 |
zcard |
zcard key-name,返回有序集合包含的成员数量 |
zincrby |
zincrby key-name increment member,将member成员的分值加上increment |
zcount |
zcount key-name min max,返回分值介于min和max之间的成员数量 |
zrank |
zrank key-name member,返回成员member在key-name中的排名 |
zscore |
zscore key-name member,返回成员member的分值 |
zrange |
zrange key-name start stop [withscores],返回有序集合中排名介于start和stop之间的成员,如果给定了可选的withscores选项,那么命令会将成员的分值也一并返回 |
zrevrank |
zrevrank key-name member,返回有序集合里成员member所处的位置,成员按照分值从大到小排列 |
zrevrange |
zrevrange key-name start stop [withscores],返回有序集合给定排名范围内的成员,成员按照分值从大到小排列 |
zrangebyscore |
zrangebyscore key min max [withscores] [limit offset count],返回有序集合中,分值介于min和max之间的所有成员 |
zrevrangebyscore |
zrevrangebyscore key max min [withscores] [limit offset count],获取有序集合中分值介于min和max之间的所有成员,并按照分值从大到小的顺序来返回它们 |
zremrangebyrank |
zremrangebyrank key-name start stop,移除有序集合中排名介于start和stop之间的所有成员 |
zremrangebyscore |
zremrangebyscore key-name min max,移除有序集合中分值介于min和max之间的所有成员 |
zinterstore |
zinterstore dest-key key-count key [key...] [weights weight [weight...]] [aggregate sum|min|max],对给定的有序集合执行类似于集合的交集运算 |
zunionstore |
zunionstore dest-key key-count key [key...] [weights weight [weight...]] [aggregate sum|min|max],对给定的有序集合执行类似于集合的并集运算 |
7. 举例。
127.0.0.1:> zadd zset-key member1
(integer)
127.0.0.1:> zadd zset-key member0 member2
(integer)
127.0.0.1:> zadd zset-key member1
(integer)
127.0.0.1:> zcard zset-key
(integer)
127.0.0.1:> zcount zset-key
(integer)
127.0.0.1:> zrank zset-key member1
(integer)
127.0.0.1:> zrank zset-key member1
(integer)
127.0.0.1:> zscore zset-key member1
""
127.0.0.1:> zrange zset-key
) "member0"
) "member2"
127.0.0.1:> zrevrank zset-key member1
(integer)
127.0.0.1:> zrevrange zset-key
) "member0"
) "member1"
127.0.0.1:> zrangebyscore zset-key
) "member1"
) "member0"
127.0.0.1:> zrevrangebyscore zset-key
(empty list or set)
127.0.0.1:> zrevrangebyscore zset-key
) "member0"
) "member1"
8. zinterstore交集举例。
9. zunionstore并集举例。
发布与订阅
1. 发布与订阅(pub/sub)的特点是订阅者(listener)负责订阅频道(channel),发送者(publisher)负责向频道发送二进制字符串消息(binary string message)。
2. 当有消息被发送至给定频道时,频道的所有订阅者都会收到消息。
3. 备注:将list作为队列,同时使用阻塞命令同样可以实现发布/订阅,具体代码参见“示例:分布式日志”。
4. 命令。
命令 |
说明 |
subscribe |
subscribe channel [channel...],订阅给定的一个或多个频道 |
unsubscribe |
unsubscribe [channel [channel...]],退订给定的一个或多个频道,如果执行时没有给定任何频道,那么退订所有频道 |
psubscribe |
psubscribe pattern [pattern...],订阅与给定模式相匹配的所有频道 |
punsubscribe |
punsubscribe [pattern [pattern...]],退订给定的模式,如果执行时没有给定任何模式,那么退订所有模式 |
publish |
publish channel message,向给定频道发送消息 |
5. 举例。
127.0.0.1:> subscribe channel0 channel1
Reading messages... (press Ctrl-C to quit)
) "subscribe"
) "channel0"
) (integer)
) "subscribe"
) "channel1"
) (integer)
) "message"
) "channel0"
) "hello"
) "message"
) "channel1"
) "world"
127.0.0.1:> publish channel0 hello
(integer)
127.0.0.1:> publish channel1 world
(integer)
6. 订阅者读取速度。
a) 问题:如果订阅者读取消息速度不够快,那么不断积压的消息会使Redis输出缓冲区的体积越来越大,可能会导致Redis速度变慢,甚至崩溃。
b) 解决:自动断开不符合client-output-buffer-limit pubsub配置选项的订阅客户端。
7. 数据传输可靠性。
a) 问题:网络连接错误会使网络连接两端中的其中一端重新连接,导致客户端丢失在短线期间的所有消息。
b) 解决:TODO 第六章两个不同方法。
排序
1. 对list、set、zset排序。
2. 命令。
命令 |
说明 |
sort |
sort source-key [by pattern] [limit offset count] [get pattern [get pattern...]] [asc|desc] [alpha] [store dest-key],根据给定的选项,对输入列表、集合或者有序集合进行排序,然后返回或者存储排序的结果 |
3. 举例。
127.0.0.1:> rpush sort-key v1 v0 v3 v4 v2
(integer)
127.0.0.1:> sort sort-key alpha
) "v0"
) "v1"
) "v2"
) "v3"
) "v4"
127.0.0.1:> sort sort-key alpha desc
) "v4"
) "v3"
) "v2"
) "v1"
) "v0"
事务
1. Redis的基本事务(basic transaction)可以让一个客户端在不被其他客户端打断的情况下执行多个命令。
2. 与关系数据库不同,Redis的基本事务在执行完事务内所有命令后,才会处理其他客户端的命令。
3. 命令。
命令 |
说明 |
multi |
标记一个事务开始。 |
exec |
执行所有multi之后的命令 |
discard |
丢弃所有multi之后的命令 |
watch |
对指定键监视,直到执行exec命令结束。如果期间其他客户端对被监视的键执行写入命令,那么当前客户端执行exec命令时将报错。相当于乐观锁。 |
unwatch |
取消监视。如果执行exec或discard命令,则无需再执行unwatch命令。 |
4. 举例。
127.0.0.1:> multi
OK
127.0.0.1:> set k0 v0
QUEUED
127.0.0.1:> set k1 v1
QUEUED
127.0.0.1:> exec
) OK
) OK
127.0.0.1:> watch k0 k1
OK
127.0.0.1:> multi
OK
127.0.0.1:> set k0 v0x
QUEUED
127.0.0.1:> set k1 v1x
QUEUED
127.0.0.1:> exec
5. Redis事务内有部分命令失败时,整个事务不会自动discard,导致事务内可能部分命令成功,部分失败。举例:
127.0.0.1:> set str-key halo
OK
127.0.0.1:> set num-key
OK
127.0.0.1:> multi
OK
127.0.0.1:> incr str-key
QUEUED
127.0.0.1:> incr num-key
QUEUED
127.0.0.1:> exec
) (error) ERR value is not an integer or out of range
) (integer)
127.0.0.1:> get str-key
"halo"
127.0.0.1:> get num-key
""
pipeline
1. 应用程序连接Redis执行事务及事务中所有命令(5个)时,一定要使用pipeline。
2. 由于pipeline会一次发送所有命令,可减少通信次数并降低延迟,在非事务时也推荐使用。
基准测试
1. Redis附带基准测试程序redis-benchmark。
2. 举例:模拟单个客户端。
src/redis-benchmark -c -q
PING_INLINE: 77399.38 requests per second
PING_BULK: 81566.07 requests per second
SET: 58513.75 requests per second
GET: 80840.74 requests per second
INCR: 57208.24 requests per second
LPUSH: 54229.93 requests per second
RPUSH: 55555.56 requests per second
LPOP: 55401.66 requests per second
RPOP: 57937.43 requests per second
SADD: 77459.34 requests per second
SPOP: 79113.92 requests per second
LPUSH (needed to benchmark LRANGE): 54495.91 requests per second
LRANGE_100 (first elements): 37271.71 requests per second
LRANGE_300 (first elements): 16537.13 requests per second
LRANGE_500 (first elements): 11799.41 requests per second
LRANGE_600 (first elements): 9273.00 requests per second
MSET ( keys): 31735.96 requests per second
3. 应用程序在使用pipeline和连接池的情况下,基本与上面模拟的测试性能一致。
键的过期
1. 设置键的过期时间,让键在在给定的时限后自动被删除(相当于执行del命令)。
2. 只能设置整个键的过期时间(支持5中数据结构),无法设置list、set、hash和zset中单个元素的过期时间。
3. 命令。
命令 |
说明 |
persist |
persist key-name,移除键的过期时间 |
ttl |
ttl key-name,返回给定键距离过期还有多少秒 |
expire |
expire key-name seconds,让键key-name在给定的seconds秒之后过期 |
expireat |
expireat key-name timestamp,将给定键的过期时间设置为给定的UNIX时间戳 |
pttl |
pttl key-name,返回给定键距离过期时间还有多少毫秒,这个命令在Redis 2.6或以上版本可用 |
pexpire |
pexpire key-name milliseconds,让键key-name在milliseconds毫秒之后过期 |
pexpireat |
pexpireat key-name timestamp-milliseconds,将一个毫秒级精度的UNIX时间戳设置为给定键的过期时间 |
4. 举例。
127.0.0.1:> set expire-key v
OK
127.0.0.1:> ttl expire-key
(integer) -
127.0.0.1:> expire expire-key
(integer)
127.0.0.1:> ttl expire-key
(integer)
127.0.0.1:> get expire-key
(nil)
持久化
概况
1. 两种持久化方式:
a) snapshoting(快照):将某一时刻内存中所有数据写入硬盘;
b) AOF(append-only file):执行写入命令时,将命令追加到硬盘文件。
2. 两种持久化方式可同时使用,也可单独使用,某些情况下也可以两种都不使用。
3. 配置。
save # snapshoting配置
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
appendonly no # AOF配置
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
dir ./ # 共用配置
snapshoting
1. 创建snapshoting的方法/时机。
a) 执行bgsave命令。Redis会调用fork创建一个子进程,子进程负责将快照写入硬盘,父进程继续处理命令请求。
b) 执行save命令。Redis在创建快照完成之前不再响应任何其他命令。不常用,通常只会在内存不足时使用。
c) 设置save配置。“save 60 100000”表示当满足“60秒之内有10000次写入”条件时,自动触发bgsave命令。如果有多个save配置,那么任意一个条件满足时都会触发。
d) 执行shutdown命令或收到标准term信号时,会先触发save命令(不再响应任何客户端请求)。
e) 一个Redis服务器连接另一个Redis服务器,并向对方发送sync命令开始一次复制时,如果主服务器目前没有执行bgsave命令,或主服务器并非刚刚执行完bgsave命令,那么主服务器会执行gbsave命令。
2. snapshoting注意:如果系统真的发生崩溃,将丢失最近一次生成快照后更新的所有数据。
3. snapshoting与大数据:如果Redis内存占用高达几十GB,并且空闲内存不多,或者硬件性能较差时,执行bgsave命令可能会导致长时间停顿(几秒,甚至几十分钟),也可能引发系统大量使用虚拟内存,从而导致Redis性能降低至无法使用。
AOF
1. appendfsync同步频率。
a) always。每个写命令都同步写入硬盘。严重降低Redis性能。降低固态硬盘SSD寿命。
b) everysec。每秒同步一次,将多个写命令同步到硬盘。兼顾数据安全和写入性能。
c) no。让操作系统决定何时同步。一般不影响性能,但崩溃将导致不定数量的数据丢失。不推荐。
2. 重写AOF文件:移除AOF文件中的冗余命令,压缩AOF文件体积。
3. 重写AOF文件解决的问题。
a) 随着Redis不断运行,AOF文件不断增大,极端时甚至用完硬盘空间。
b) Redis重启后需要重新执行AOF文件中的写命令还原数据,如果AOF文件非常大,那么还原时间可能会非常长。
4. 重写AOF文件的方法/时机。
a) 执行bgrewriteaof命令。与bgsave命令相似,Redis会创建一个子进程负责AOF文件重写,也存在影响性能的问题。
b) 设置auto-aof-rewrite-percentage和auto-aof-rewrite-min-size配置。“auto-aof-rewrite-percentage 100”和“auto-aof-rewrite-min-size 64mb”表示当AOF文件大于64MB且AOF文件比上次重写后至少大一倍(100%)时,触发bgrewriteaof命令。
主从复制
1. 解决:虽然Redis性能优秀,但也会有无法快速处理请求的情况。伸缩(scalability)。
2. 客户端效果:客户端每次向主服务器执行写入命令时,从服务器都会实时更新,客户端就可以向任意一个服务器执行读取命令。
3. 配置:
a) 主服务器设置dir和dbfilename配置。保证从服务器连接主服务器时,主服务器能执行bgsave操作。
b) 从服务器设置slaveof host port配置,或执行slaveof host port命令。让从服务器复制主服务器。slaveof no one命令可终止复制。
4. 从服务器连接主服务器的过程。
步骤 |
主服务器 |
从服务器 |
1 |
(等待命令进入) |
连接(或重连)主服务器,发送sync命令 |
2 |
开始执行bgsave命令,并使用缓冲区记录bgsave之后执行的所有写命令 |
根据配置决定继续使用现有数据(如果有)来处理客户端请求,还是向发送请求的客户端返回错误 |
3 |
bgsave执行完毕,向从服务器发送快照文件,并在发送期间继续使用缓冲区记录被执行的写命令 |
丢弃所有旧数据(如果有),开始载入主服务器发来的快照文件 |
4 |
快照文件发送完毕,开始向从服务器发送缓冲区中的写命令 |
完成对快照文件的解释操作,像往常一样开始接收请求 |
5 |
缓冲区的写命令发送完毕,从此,每执行一个写命令,就向从服务器发送相同的写命令 |
执行主服务器发送来的缓冲区中的写命令,从此,接收并执行主服务器传来的每个写命令 |
5. 优化:实际中最好让主服务器只使用50%~65%的内存,剩余30%~45%内存用于执行bgsave命令和创建记录写命令的缓冲区。
6. 主从链:从服务器也可以拥有自己的从服务器,由此形成主从链(master/slave chaning)。
7. 主从链解决问题。
a) 读请求远多于写请求。
b) 负荷上升,主服务器无法快速更新所有从服务器。
8. 主从链结构:不一定是树状结构。
9. 更换故障主服务器步骤:
a) 从服务器执行save命令,生成最新快照文件;
b) 将快照文件复制到新主服务器;
c) 配置并启动新主服务器;
d) 从服务器连接新主服务器。
HA
1. Redis-Sentinel是Redis的Master-Slave高可用方案。
2. Master宕机时,自动完成主备切换。
3. 资料:http://redis.cn/topics/sentinel.html。
Lua
1. Redis中的Lua类似关系数据库中的存储过程,可封装逻辑。
2. Lua脚本跟单个Redis命令以及“multi/exec”事务一样,都是原子操作,因此可替代事务。
3. 命令。
命令 |
说明 |
eval |
eval script numkeys key [key...] arg [arg...],执行脚本script,numkeys表示要使用的键个数,key表示键,arg表示参数。脚本内部通过KEYS数组获取键,如KEYS[1]获取第1个键;通过ARGV数组获取参数,如ARGV[1]获取第1个参数 |
evalsha |
evalsha sha1 numkeys key [key...] arg [arg...],根据SHA1校验码执行脚本 |
script load |
script load script,加载脚本script,返回SHA1校验码 |
script exists |
script exists sha1,根据SHA1校验码判断脚本是否已加载 |
script flush |
清除全部脚本 |
script kill |
停止当前正在执行的脚本 |
4. 举例(第3个证明脚本中Redis命令执行失败时不会discard已执行过的命令,即“事务”提到的第5点)。
$ redis-cli eval "return 'Hello World'"
"Hello World"
$ redis-cli eval "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" key1 key2 arg1 arg2
) "key1"
) "key2"
) "arg1"
) "arg2"
$ vi test.lua
local ret = redis.call("set", KEYS[], ARGV[])
if redis.call("exists", KEYS[]) == then
redis.call("incr", KEYS[])
else
redis.call("set", KEYS[], ARGV[])
end
return ret
$ redis-cli script load "$(cat test.lua)"
"07aa590946287d9ae0c3df41dd9ba06a64280d85"
$ redis-cli evalsha 07aa590946287d9ae0c3df41dd9ba06a64280d85 mykey1 mykey2 myarg1 myarg2
OK
$ redis-cli evalsha 07aa590946287d9ae0c3df41dd9ba06a64280d85 mykey1 mykey2 myarg1111111 myarg2
(error) ERR Error running script (call to f_07aa590946287d9ae0c3df41dd9ba06a64280d85): @user_script:: ERR value is not an integer or out of range
$ redis-cli get mykey1
"myarg1111111"
$ redis-cli get mykey2
"myarg2"
示例:分布式日志
1. 生产者-消费者模式。
2. 多台机器将日志保存到Redis队列,一个线程从该队列取出日志并保存到日志文件。个数比:生产者:消费者=N:1。
3. 代码(使用Jedis API):
import static gz.redis.DistributedLog.HOST;
import static gz.redis.DistributedLog.LOG_QUEUE_KEY;
import static gz.redis.DistributedLog.PORT;
import static gz.redis.DistributedLog.connection; import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig; public class DistributedLog { static final String HOST = "centos1"; static final int PORT = 6379; private static JedisPool jedisPool; static final String LOG_QUEUE_KEY = "log-queue"; public static void main(String[] args) {
initConnectionPoll(); ExecutorService threadPool = Executors.newFixedThreadPool(50);
for (int index = 0; index < 500000; index++) {
threadPool.execute(new Writer());
}
new Thread(new Processor()).run();
threadPool.shutdown();
} private static void initConnectionPoll() {
if (jedisPool == null) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(51);
config.setMinIdle(51);
config.setMaxIdle(51);
config.setMaxWaitMillis(60 * 1000);
config.setTestOnCreate(true);
config.setTestOnReturn(true);
config.setTestOnBorrow(true);
config.setTestWhileIdle(true);
jedisPool = new JedisPool(config, HOST, PORT);
}
} static Jedis connection() {
return jedisPool.getResource();
} } class Writer implements Runnable { @Override
public void run() {
String log = new Date() + " - " + UUID.randomUUID() + "\n";
Jedis jedis = null;
try {
jedis = connection();
// 队尾追加
jedis.rpush(LOG_QUEUE_KEY, log);
} finally {
if (jedis != null) {
jedis.close();
}
}
} } class Processor implements Runnable { @Override
public void run() {
BufferedWriter writer = null;
Jedis jedis = null;
try {
writer = new BufferedWriter(new FileWriter("D:/movie/MyTest.log"));
jedis = new Jedis(HOST, PORT);
int count = 0;
while (true) {
// 队头取出,无限时间阻塞,直至取出
List<String> logs = jedis.blpop(0, LOG_QUEUE_KEY);
if (logs != null && logs.size() >= 2) {
writer.write(logs.get(1));
if (++count > 100) {
writer.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jedis != null) {
jedis.close();
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} }
作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。
Redis笔记——技术点汇总的更多相关文章
- Hadoop笔记——技术点汇总
目录 · 概况 · Hadoop · 云计算 · 大数据 · 数据挖掘 · 手工搭建集群 · 引言 · 配置机器名 · 调整时间 · 创建用户 · 安装JDK · 配置文件 · 启动与测试 · Clo ...
- Storm笔记——技术点汇总
目录 概况 手工搭建集群 引言 安装Python 配置文件 启动与测试 应用部署 参数配置 Storm命令 原理 Storm架构 Storm组件 Stream Grouping 守护进程容错性(Dae ...
- Spark SQL笔记——技术点汇总
目录 概述 原理 组成 执行流程 性能 API 应用程序模板 通用读写方法 RDD转为DataFrame Parquet文件数据源 JSON文件数据源 Hive数据源 数据库JDBC数据源 DataF ...
- JVM笔记——技术点汇总
目录 · 初步认识 · Java里程碑(关键部分) · 理解虚拟机 · Java虚拟机种类 · Java语言规范 · Java虚拟机规范 · 基本结构 · Java堆(Heap) · Java栈(St ...
- Netty笔记——技术点汇总
目录 · Linux网络IO模型 · 文件描述符 · 阻塞IO模型 · 非阻塞IO模型 · IO复用模型 · 信号驱动IO模型 · 异步IO模型 · BIO编程 · 伪异步IO编程 · NIO编程 · ...
- Java并发编程笔记——技术点汇总
目录 · 线程安全 · 线程安全的实现方法 · 互斥同步 · 非阻塞同步 · 无同步 · volatile关键字 · 线程间通信 · Object.wait()方法 · Object.notify() ...
- Kafka笔记——技术点汇总
Table of contents Table of contents Overview Introduction Use cases Manual setup Assumption Configur ...
- Spark Streaming笔记——技术点汇总
目录 目录 概况 原理 API DStream WordCount示例 Input DStream Transformation Operation Output Operation 缓存与持久化 C ...
- Spark笔记——技术点汇总
目录 概况 手工搭建集群 引言 安装Scala 配置文件 启动与测试 应用部署 部署架构 应用程序部署 核心原理 RDD概念 RDD核心组成 RDD依赖关系 DAG图 RDD故障恢复机制 Standa ...
随机推荐
- Android-重新包装Toast,自定义背景
Android-重新包装Toast,自定义背景 2016-4-27 Android L 算是包装了一个自己使用的小工具. 使用Toast的目的是弹一个提示框.先看一下Toast.makeText方法. ...
- Android - FragmentTabHost 与 Fragment 制作页面切换效果
使用 FragmentTabHost 与 Fragment 制作页面切换效果 API 19 TabHost已经不建议使用了.用 FragmentTabHost 来代替TabHost.实际上 Fragm ...
- Reverse Integer - 反转一个int,溢出时返回0
Reverse Integer Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, retur ...
- Java网络连接之HttpURLConnection、HttpsURLConnection
工具类包含两个方法: http请求.https请求 直接看代码: package com.jtools; import java.io.BufferedReader; import java.io.I ...
- link 和 @important 的区别
页面中使用CSS的方式主要有3种:行内添加定义style属性值,页面头部内嵌调用和外面链接调用,其中外面引用有两种:link和@import.外部引用CSS两种方式link和@import的方式分别是 ...
- eclipse中console的输出行数控制
eclipse中console的输出行数控制 开发中,会遇到当输出大量的sql语句或者错误的时候,往往会因为console输出的限制而不能完整显示,所以我们自己就需要迫切的增加显示的行数,这样 就可以 ...
- javascript对象的创建--相对java 怎样去创建了"类"i以及实例化对象
由于javascript没有java那么多基本类型,同时也没有提供class这个东西,那么我们想实现javascript的对象创建应该怎么办呢,我简单地从w3c提供的课件中提取了一下几种方法: 一.工 ...
- easyAR图钉功能实现相关
图钉功能总算做出来了,发现真的是因为没认真看手册导致的=.=[跪 最后的代码很简单,但是过程中看了不少camera/tracker相关的东西,感觉需要整理记录一下 图钉功能记录: 目标:实现用ARCa ...
- python 排序sorted
num = [3,2,4,6,5] anum = sorted(num) dnum = sorted(num,reverse=True) print '升序:',anum # 升序: [2, 3, 4 ...
- Java基础之接口与抽象类及多态、内部类
final关键字 被其修饰的类,不能被继承. 被其修饰的方法,不能被覆盖. 被其修饰的变量,是一个常量,不能被修改,所以定义时必须初始化(和C++的const类似). 一般有final,会搭配stat ...