Redis笔记——技术点汇总

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis笔记——技术点汇总相关的知识,希望对你有一定的参考价值。

目录

· 特点

· 安装

· 数据库

· 服务器命令

· 数据类型及其操作命令

    · 数据结构

    · string

    · list

    · set

    · hash

    · zset

· 发布与订阅

· 排序

· 事务

· pipeline

· 基准测试

· 键的过期

· 持久化

    · 概况

    · snapshoting

    · AOF

· 主从复制

· HA

· Lua

· 示例:分布式日志


 

特点

1. Redis是一个开源的、C语言编写的、面向键值对类型数据的分布式NoSQL数据库系统。

2. 特点:高性能(内存数据库,随机读写非常快)、持久存储,适应高并发应用场景。

3. 对比:一些数据库和缓存服务器的特性与功能。

名称

类型

数据存储

查询类型

附加功能

Redis

使用内存存储(in-memory)的非关系数据库

字符串、列表、集合、散列、有序集合

每种数据类型都有自己的专属命令,另外还有批量操作(buld operation)和不完全(partial)事务支持

发布与订阅,主从复制(master/slave replication),持久化,脚本

memcached

使用内存存储的键值缓存

键值之间的映射

创建命令、读取命令、更新命令、删除命令以及其他几个命令

为提升性能而设的多线程服务器

mysql

关系数据库

每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间和第三方扩展

SELECTINSERTUPDATEDELETE、函数、存储过程

支持ACID(InnoDB),主从复制和主主复制

PostgreSQL

关系数据库

每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表的视图;支持空间(spatial和第三方扩展;支持定制类型

SELECTINSERTUPDATEDELETE、函数、存储过程

支持ACID(InnoDB),主从复制,由第三方支持的多主复制

MongoDB

使用硬盘存储(on-disk)的非关系数据库

每个数据库可以包含多个表,每个表可以包含多个无schema(schema-less)的BSON文档

创建命令、读取命令、更新命令、删除命令、条件查询命令等

支持map-reduce操作,主从复制,分片,空间索引(spatial index

4. 性能测试结果:set操作每秒可达110000次,get操作每秒81000次(与服务器配置有关)。

安装

1. 安装。

tar zxvf redis-3.2.0.tar.gz
cd redis-3.2.0.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:6379> select 0
OK
127.0.0.1:6379> select 15
OK
127.0.0.1:6379[15]>

服务器命令

命令

说明

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范围内所有字符组成的子串,包括startend在内

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:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> del hello
(integer) 1
127.0.0.1:6379> get hello
(nil)
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incrby num 10
(integer) 110
127.0.0.1:6379> append num abc
(integer) 6
127.0.0.1:6379> get num
"110abc"
127.0.0.1:6379> getrange num 2 4
"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:6379> rpush list-key item1
(integer) 1
127.0.0.1:6379> rpush list-key item2 item1
(integer) 3
127.0.0.1:6379> lpush list-key item0
(integer) 4
127.0.0.1:6379> lrange list-key 0 -1
1) "item0"
2) "item1"
3) "item2"
4) "item1"
127.0.0.1:6379> lindex list-key 3
"item1"
127.0.0.1:6379> lpop list-key
"item0"
127.0.0.1:6379> ltrim list-key 0 1
OK
127.0.0.1:6379> lrange list-key 0 -1
1) "item1"
2) "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:6379> sadd set-key item0
(integer) 1
127.0.0.1:6379> sadd set-key item1 item2
(integer) 2
127.0.0.1:6379> sadd set-key item0
(integer) 0
127.0.0.1:6379> smembers set-key
1) "item2"
2) "item1"
3) "item0"
127.0.0.1:6379> sismember set-key item3
(integer) 0
127.0.0.1:6379> sismember set-key item0
(integer) 1
127.0.0.1:6379> srem set-key item2
(integer) 1
127.0.0.1:6379> srem set-key item2
(integer) 0
127.0.0.1:6379> smembers set-key
1) "item1"
2) "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:6379> hset hash-key sub-key0 value0
(integer) 1
127.0.0.1:6379> hset hash-key sub-key1 value1
(integer) 1
127.0.0.1:6379> hmset hash-key sub-key2 value2 sub-key3 value3
OK
127.0.0.1:6379> hset hash-key sub-key0 value0
(integer) 0
127.0.0.1:6379> hgetall hash-key
1) "sub-key0"
2) "value0"
3) "sub-key1"
4) "value1"
5) "sub-key2"
6) "value2"
7) "sub-key3"
8) "value3"
127.0.0.1:6379> hdel hash-key sub-key3
(integer) 1
127.0.0.1:6379> hmget hash-key sub-key0 sub-key1
1) "value0"
2) "value1"
127.0.0.1:6379> hget hash-key sub-key2
"value2"
127.0.0.1:6379> hkeys hash-key
1) "sub-key0"
2) "sub-key1"
3) "sub-key2"
127.0.0.1:6379> hvals hash-key
1) "value0"
2) "value1"
3) "value2"

4. 应用:可以把hash看作关系数据库的行,hash中的key为字段名,hash中的value为字段值。以用户为例,新增/查询ID为1和2的两个用户:

127.0.0.1:6379> hmset user:1 name zhangsan age 18
OK
127.0.0.1:6379> hmset user:2 name lisi age 19
OK
127.0.0.1:6379> hgetall user:1
1) "name"
2) "zhangsan"
3) "age"
4) "18"
127.0.0.1:6379> hgetall user:2
1) "name"
2) "lisi"
3) "age"
4) "19"

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,返回分值介于minmax之间的成员数量

zrank

zrank key-name member,返回成员memberkey-name中的排名

zscore

zscore key-name member,返回成员member的分值

zrange

zrange key-name start stop [withscores],返回有序集合中排名介于startstop之间的成员,如果给定了可选的withscores选项,那么命令会将成员的分值也一并返回

zrevrank

zrevrank key-name member,返回有序集合里成员member所处的位置,成员按照分值从大到小排列

zrevrange

zrevrange key-name start stop [withscores],返回有序集合给定排名范围内的成员,成员按照分值从大到小排列

zrangebyscore

zrangebyscore key min max [withscores] [limit offset count],返回有序集合中,分值介于minmax之间的所有成员

zrevrangebyscore

zrevrangebyscore key max min [withscores] [limit offset count],获取有序集合中分值介于minmax之间的所有成员,并按照分值从大到小的顺序来返回它们

zremrangebyrank

zremrangebyrank key-name start stop,移除有序集合中排名介于startstop之间的所有成员

zremrangebyscore

zremrangebyscore key-name min max,移除有序集合中分值介于minmax之间的所有成员

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:6379> zadd zset-key 200 member1
(integer) 1
127.0.0.1:6379> zadd zset-key 300 member0 400 member2
(integer) 2
127.0.0.1:6379> zadd zset-key 100 member1
(integer) 0
127.0.0.1:6379> zcard zset-key
(integer) 3
127.0.0.1:6379> zcount zset-key 100 350
(integer) 2
127.0.0.1:6379> zrank zset-key member1
(integer) 0
127.0.0.1:6379> zrank zset-key member1
(integer) 0
127.0.0.1:6379> zscore zset-key member1
"100"
127.0.0.1:6379> zrange zset-key 1 2
1) "member0"
2) "member2"
127.0.0.1:6379> zrevrank zset-key member1
(integer) 2
127.0.0.1:6379> zrevrange zset-key 1 2
1) "member0"
2) "member1"
127.0.0.1:6379> zrangebyscore zset-key 100 350
1) "member1"
2) "member0"
127.0.0.1:6379> zrevrangebyscore zset-key 100 350
(empty list or set)
127.0.0.1:6379> zrevrangebyscore zset-key 350 100
1) "member0"
2) "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:6379> subscribe channel0 channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel0"
3) (integer) 1
1) "subscribe"
2) "channel1"
3) (integer) 2
1) "message"
2) "channel0"
3) "hello"
1) "message"
2) "channel1"
3) "world"
127.0.0.1:6379> publish channel0 hello
(integer) 1
127.0.0.1:6379> publish channel1 world
(integer) 1

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:6379> rpush sort-key v1 v0 v3 v4 v2
(integer) 5
127.0.0.1:6379> sort sort-key alpha
1) "v0"
2) "v1"
3) "v2"
4) "v3"
5) "v4"
127.0.0.1:6379> sort sort-key alpha desc
1) "v4"
2) "v3"
3) "v2"
4) "v1"
5) "v0"

事务

1. Redis的基本事务(basic transaction)可以让一个客户端在不被其他客户端打断的情况下执行多个命令。

2. 与关系数据库不同,Redis的基本事务在执行完事务内所有命令后,才会处理其他客户端的命令。

3. 命令。

命令

说明

multi

标记一个事务开始。

exec

执行所有multi之后的命令

discard

丢弃所有multi之后的命令

watch

对指定键监视,直到执行exec命令结束。如果期间其他客户端对被监视的键执行写入命令,那么当前客户端执行exec命令时将报错。相当于乐观锁

unwatch

取消监视。如果执行execdiscard命令,则无需再执行unwatch命令。

4. 举例。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k0 v0
QUEUED
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
127.0.0.1:6379> watch k0 k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k0 v0x
QUEUED
127.0.0.1:6379> set k1 v1x
QUEUED
127.0.0.1:6379> exec

5. Redis事务内有部分命令失败时,整个事务不会自动discard,导致事务内可能部分命令成功,部分失败。举例:

127.0.0.1:6379> set str-key halo
OK
127.0.0.1:6379> set num-key 100
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr str-key
QUEUED
127.0.0.1:6379> incr num-key
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) (integer) 101
127.0.0.1:6379> get str-key
"halo"
127.0.0.1:6379> get num-key
"101"

pipeline

1. 应用程序连接Redis执行事务及事务中所有命令(5个)时,一定要使用pipeline。

2. 由于pipeline会一次发送所有命令,可减少通信次数并降低延迟,在非事务时也推荐使用。

基准测试

1. Redis附带基准测试程序redis-benchmark。

2. 举例:模拟单个客户端。

src/redis-benchmark -c 1 -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 100 elements): 37271.71 requests per second
LRANGE_300 (first 300 elements): 16537.13 requests per second
LRANGE_500 (first 450 elements): 11799.41 requests per second
LRANGE_600 (first 600 elements): 9273.00 requests per second
MSET (10 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-namemilliseconds毫秒之后过期

pexpireat

pexpireat key-name timestamp-milliseconds,将一个毫秒级精度的UNIX时间戳设置为给定键的过期时间

4. 举例。

127.0.0.1:6379> set expire-key v
OK
127.0.0.1:6379> ttl expire-key
(integer) -1
127.0.0.1:6379> expire expire-key 10
(integer) 1
127.0.0.1:6379> ttl expire-key
(integer) 7
127.0.0.1:6379> get expire-key
(nil)

持久化

概况

1. 两种持久化方式:

    a) snapshoting(快照):将某一时刻内存中所有数据写入硬盘;

    b) AOF(append-only file):执行写入命令时,将命令追加到硬盘文件。

2. 两种持久化方式可同时使用,也可单独使用,某些情况下也可以两种都不使用。

3. 配置。

save 60 1000 # 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 100
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...],执行脚本scriptnumkeys表示要使用的键个数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‘" 0
"Hello World"
$ redis-cli eval "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}" 2 key1 key2 arg1 arg2
1) "key1"
2) "key2"
3) "arg1"
4) "arg2"
$ vi test.lua
local ret = redis.call("set", KEYS[1], ARGV[1])
if redis.call("exists", KEYS[2]) == 1 then
  redis.call("incr", KEYS[2])
else
  redis.call("set", KEYS[2], ARGV[2])
end
return ret
$ redis-cli script load "$(cat test.lua)"
"07aa590946287d9ae0c3df41dd9ba06a64280d85"
$ redis-cli evalsha 07aa590946287d9ae0c3df41dd9ba06a64280d85 2 mykey1 mykey2 myarg1 myarg2
OK
$ redis-cli evalsha 07aa590946287d9ae0c3df41dd9ba06a64280d85 2 mykey1 mykey2 myarg1111111 myarg2
(error) ERR Error running script (call to f_07aa590946287d9ae0c3df41dd9ba06a64280d85): @user_script:3: 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):

  1 import static gz.redis.DistributedLog.HOST;
  2 import static gz.redis.DistributedLog.LOG_QUEUE_KEY;
  3 import static gz.redis.DistributedLog.PORT;
  4 import static gz.redis.DistributedLog.connection;
  5 
  6 import java.io.BufferedWriter;
  7 import java.io.FileWriter;
  8 import java.io.IOException;
  9 import java.util.Date;
 10 import java.util.List;
 11 import java.util.UUID;
 12 import java.util.concurrent.ExecutorService;
 13 import java.util.concurrent.Executors;
 14 
 15 import redis.clients.jedis.Jedis;
 16 import redis.clients.jedis.JedisPool;
 17 import redis.clients.jedis.JedisPoolConfig;
 18 
 19 public class DistributedLog {
 20     
 21     static final String HOST = "centos1";
 22     
 23     static final int PORT = 6379;
 24     
 25     private static JedisPool jedisPool;
 26     
 27     static final String LOG_QUEUE_KEY = "log-queue";
 28     
 29     public static void main(String[] args) {
 30         initConnectionPoll();
 31         
 32         ExecutorService threadPool = Executors.newFixedThreadPool(50);
 33         for (int index = 0; index < 500000; index++) {
 34             threadPool.execute(new Writer());
 35         }
 36         new Thread(new Processor()).run();
 37         threadPool.shutdown();
 38     }
 39     
 40     private static void initConnectionPoll() {
 41         if (jedisPool == null) {
 42             JedisPoolConfig config = new JedisPoolConfig();
 43             config.setMaxTotal(51);
 44             config.setMinIdle(51);
 45             config.setMaxIdle(51);
 46             config.setMaxWaitMillis(60 * 1000);
 47             config.setTestOnCreate(true);
 48             config.setTestOnReturn(true);
 49             config.setTestOnBorrow(true);
 50             config.setTestWhileIdle(true);
 51             jedisPool = new JedisPool(config, HOST, PORT);
 52         }
 53     }
 54     
 55     static Jedis connection() {
 56         return jedisPool.getResource();
 57     }
 58     
 59 }
 60 
 61 class Writer implements Runnable {
 62 
 63     @Override
 64     public void run() {
 65         String log = new Date() + " - " + UUID.randomUUID() + "\\n";
 66         Jedis jedis = null;
 67         try {
 68             jedis = connection();
 69             // 队尾追加
 70             jedis.rpush(LOG_QUEUE_KEY, log);
 71         } finally {
 72             if (jedis != null) {
 73                 jedis.close();
 74             }
 75         }
 76     }
 77     
 78 }
 79 
 80 class Processor implements Runnable {
 81 
 82     @Override
 83     public void run() {
 84         BufferedWriter writer = null;
 85         Jedis jedis = null;
 86         try {
 87             writer = new BufferedWriter(new FileWriter("D:/movie/MyTest.log"));
 88             jedis = new Jedis(HOST, PORT);
 89             int count = 0;
 90             while (true) {
 91                 // 队头取出,无限时间阻塞,直至取出
 92                 List<String> logs = jedis.blpop(0, LOG_QUEUE_KEY);
 93                 if (logs != null && logs.size() >= 2) {
 94                     writer.write(logs.get(1));
 95                     if (++count > 100) {
 96                         writer.flush();
 97                     }
 98                 }
 99             }
100         } catch (IOException e) {
101             e.printStackTrace();
102         } finally {
103             if (jedis != null) {
104                 jedis.close();
105             }
106             if (writer != null) {
107                 try {
108                     writer.close();
109                 } catch (IOException e) {
110                     e.printStackTrace();
111                 }
112             }
113         }
114     }
115     
116 }

 

作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。

 



以上是关于Redis笔记——技术点汇总的主要内容,如果未能解决你的问题,请参考以下文章

ActiveMQ笔记——技术点汇总

Spark笔记——技术点汇总

Storm笔记——技术点汇总

JVM笔记——技术点汇总

Netty笔记——技术点汇总

ZooKeeper笔记——技术点汇总