Redis Essentials 读书笔记 - 第四章: Commands (Where the Wild Things Are)

Chapter 4. Commands (Where the Wild Things Are)

本章介绍管理Redis的一些常用命令以及Lua scripting。

Pub/Sub

Pub/Sub 表示 Publish-Subscribe(发行/订阅), 消息队列的常用模式。

Pub/Sub的使用场景包括:
* 新闻和天气公告板
* 聊天应用
* 通知发布,如列车晚点
* 远程代码执行,如SaltStack

PUBLISH将消息发布到channel, SUBSCRIBE订阅channel的消息,UNSUBSCRIBE取消订阅。
注意,在订阅之前发布的信息是无法收到的。

示例程序需要打开三个终端,前两个订阅客户端会阻塞等待,后一个发布程序是异步的,不会阻塞。

[redis@tt12c terminal1]$ node subscriber.js channel-1 
PONG
DATE 2016-05-05T06:06:53.299Z

[redis@tt12c terminal2]$  node subscriber.js channel-2
PONG
HOSTNAME tt12c

[redis@tt12c terminal3]$ node publisher.js global PING
[redis@tt12c terminal3]$ node publisher.js channel-1 DATE
[redis@tt12c terminal3]$ node publisher.js channel-2 HOSTNAME

整个监控过程如下:

[redis@tt12c ~]$ redis-cli monitor
OK
1462428299.899393 [0 127.0.0.1:63664] "info"
1462428299.903415 [0 127.0.0.1:63664] "subscribe" "global" "channel-1" <- client1订阅了两频道
1462428336.424626 [0 127.0.0.1:63665] "info"
1462428336.428984 [0 127.0.0.1:63665] "subscribe" "global" "channel-2"
1462428397.280185 [0 127.0.0.1:63668] "info"
1462428397.285357 [0 127.0.0.1:63668] "publish" "global" "PING"
1462428413.295175 [0 127.0.0.1:63670] "info"
1462428413.299221 [0 127.0.0.1:63670] "publish" "channel-1" "DATE"
1462428424.192961 [0 127.0.0.1:63671] "info"
1462428424.196800 [0 127.0.0.1:63671] "publish" "channel-2" "HOSTNAME"
^C

PUBSUB可以查看Pub/Sub系统的内部状态,PUBSUB需指定子命令,如 CHANNELS, NUMSUB
127.0.0.1:6379> PUBSUB CHANNELS
1) “global”
2) “channel-1”
127.0.0.1:6379> PUBSUB CHANNELS glo*
1) “global”

127.0.0.1:6379> PUBSUB NUMSUB global
1) “global”
2) (integer) 2
127.0.0.1:6379> PUBSUB NUMSUB global channel-1 channel-2
1) “global”
2) (integer) 2
3) “channel-1”
4) (integer) 1
5) “channel-2”
6) (integer) 1

一个使用Pub/Sub实现的聊天程序参见:https://gist.github.com/pietern/348262.

Transactions

由于Redis是单线程的,所谓交易其实就是一组按顺序成组执行的命令。MULTI标记交易的开始,EXEC标记交易的结束。其间不解释其它客户端的请求。

直到EXEC,客户端才会将所有命令送至服务器,因此可以用DISCARD代替EXEC取消交易的执行。

和传统的SQL数据库不一样,Redis中的交易不能回退。另一个不足时在交易中不能做控制,因为所有的命令都是排队顺序执行的。
所以交易的控制通常放在外面,如果条件满足才执行EXEC

交易的执行可以结合WATCH命令,WATCH监控的key只有没有被改变时,EXEC才会执行交易,否则返回NULL表示需要重试,其实是optimistic lock的一种实现。

Optimistic Locking is a strategy where you read a record, take note of a version number (other methods to do this involve dates, timestamps or checksums/hashes) and check that the version hasn’t changed before you write the record back.
Pessimistic Locking is when you lock the record for your exclusive use until you have finished with it. It has much better integrity than optimistic locking but requires you to be careful with your application design to avoid Deadlocks.

整个程序的执行过程如下(奇怪没有监控到unwatch命令):

$ node watch-transaction.js
The first president in the group is: George Washington

[redis@tt12c ~]$ redis-cli monitor
OK
1462430261.133540 [0 127.0.0.1:63679] "info"
1462430261.137652 [0 127.0.0.1:63679] "zadd" "presidents" "1732" "George Washington"
1462430261.138245 [0 127.0.0.1:63679] "zadd" "presidents" "1809" "Abraham Lincoln"
1462430261.138411 [0 127.0.0.1:63679] "zadd" "presidents" "1858" "Theodore Roosevelt"
1462430261.138548 [0 127.0.0.1:63679] "watch" "presidents"
1462430261.139920 [0 127.0.0.1:63679] "zrange" "presidents" "0" "0"
1462430261.142635 [0 127.0.0.1:63679] "multi"
1462430261.142661 [0 127.0.0.1:63679] "zrem" "presidents" "George Washington"
1462430261.142678 [0 127.0.0.1:63679] "exec"

Pipelines

Redis的Pipeline是将一组命令一次性发送到服务器,然后将所有的结果一并取回,而不是逐个的等待结果,这和通常UNIX Shell中的pipeline不一样。
Pipeline的目的是减少RTT,避免网络延迟。
Pipeline中的命令必须是不相互依赖的命令,尽管他们是按序执行的,但并非按交易执行。

其实node.js就是按Pipeline模式执行的,但不能保证所有的客户端都是如此。

Scripting

Lua脚本语言简单,小巧,功能强大,通常用于游戏开发,如Civilization V, Angry Birds, World of Warcraft都是用Lua作为脚本语言。

Redis 2.6版本后,Lua可用来扩展Redis,而之前只能改源码。

Lua 脚本执行时,Redis server会阻塞,因此缺省会设置5秒的超时。
Lua的基本语法请参见原书。
Redis client将Lua脚本作为字符串发生到服务器执行,可以通过KEYS和ARGV将key传递到脚本中。
通过EVAL和EVALSHA执行Lua脚本。

本节给出了一个用Lua脚本执行交易的例子,比之前的WATCH版本简洁,很多用户也使用Lua脚本执行交易来替代WATCH/MULTI/EXEC。

Miscellaneous commands

INFO

返回Redis服务器的状态信息,包括版本, OS, 内存用量等,可指定显示某一部分信息。

127.0.0.1:6379> info clients
# Clients
connected_clients:3
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

127.0.0.1:6379> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:ebdca053ec3876f6
redis_mode:standalone
os:Linux 3.8.13-44.1.1.el6uek.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:4649
run_id:5368932ddedc34777729830cf10fc24b44586dc7
tcp_port:6379
uptime_in_seconds:3411
uptime_in_days:0
hz:10
lru_clock:2815864
config_file:

# Clients
connected_clients:3
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:551416
used_memory_human:538.49K
used_memory_rss:2162688
used_memory_peak:551416
used_memory_peak_human:538.49K
used_memory_lua:36864
mem_fragmentation_ratio:3.92
mem_allocator:jemalloc-3.6.0

# Persistence
loading:0
rdb_changes_since_last_save:3
rdb_bgsave_in_progress:0
rdb_last_save_time:1462430245
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

# Stats
total_connections_received:5
total_commands_processed:13
instantaneous_ops_per_sec:0
total_net_input_bytes:467
total_net_output_bytes:9041
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:1
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:3.02
used_cpu_user:1.62
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=5,expires=3,avg_ttl=1757324836
127.0.0.1:6379> 

DBSIZE

当前服务器中key的数量

127.0.0.1:6379> DBSIZE
(integer) 5

DEBUG SEGFAULT

让Redis Server崩溃的命令,主要用于模拟错误和调试。

生产系统慎用,否则服务器会core dump:

Segmentation fault (core dumped)

MONITOR

对于调试和监控非常有用!前面已经用过多次了。不过在生产系统慎用,可能将服务器的吞吐量减少一半。

CLIENT LIST and CLIENT SET NAME

还是用于调试和监控。

127.0.0.1:6379> client list
id=4 addr=127.0.0.1:63693 fd=6 name= age=2 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
127.0.0.1:6379> client setname client-01
OK
127.0.0.1:6379> client list
id=4 addr=127.0.0.1:63693 fd=6 name=client-01 age=31 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
127.0.0.1:6379> 

CLIENT KILL

断掉client的连接,可以根据IP地址,ID等。

FLUSHALL

删除所有的key

127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> dbsize
(integer) 0

RANDOMKEY

随机返回key

127.0.0.1:6379> KEYS *
1) "concurrentplays:1min:60"
2) "presidents"
3) "concurrentplays:1hour:0"
4) "concurrentplays:1min:0"
5) "concurrentplays:1day:0"

127.0.0.1:6379> RANDOMKEY
"concurrentplays:1min:0"

EXPIRE and EXPIREAT

设置key的有效期,过期后会被删除。单位为秒,若为负数则马上删除,相当于DEL。

TTL and PTTL

显示key的有效期。

PERSIST

去除有效期,让可以永远有效

SETEX

同时设置key和有效期

DEL

删除一个或多个key

EXISTS

判断key是否存在

127.0.0.1:6379> SET mykey myvalue
OK
127.0.0.1:6379> EXISTS mykey
(integer) 1

PING

服务器是否可联系

127.0.0.1:6379> PING
PONG

MIGRATE

在redis服务器之间迁移key,可以是COPY或MOVE

SELECT

redis server支持多个数据库,不过不推荐罢了,更建议使用多个服务器,因为可以更好的利用CPU,毕竟Redis-Server是单线程执行。
SELECT相当于Sybase中的use database。

AUTH

认证客户端,提供口令给服务器

SCRIPT KILL

中断Lua Script

SHUTDOWN

断掉所有客户端连接,关闭服务器。可以选择进行持久化(SHUTDOWN SAVE)或放弃内存中数据(SHUTDOWN NOSAVE)

OBJECT ENCODING

显示key的内部数据结构实现,注意TYPE是数据类型,OBJECT ENCODING是具体实现。例如hash可能用hash table也可以用ziplist实现

127.0.0.1:6379> HSET math score 100
(integer) 1
127.0.0.1:6379> type math
hash
127.0.0.1:6379> OBJECT ENCODING math
"ziplist"

Data type optimizations

Redis中,所有的数据类型都可以选择不同的实现(ENCODING)来提升性能和节省内存。

这些影响到ENCODING的选项可以在配置文件redis.conf中配置,或在CLI中指定,或执行CONFIG命令设置。

[redis@tt12c ~]$ find . -name redis.conf
./redis-3.0.7/redis.conf
[redis@tt12c ~]$ cd redis-3.0.7/
[redis@tt12c redis-3.0.7]$ wc -l redis.conf 
943 redis.conf

String

对于String的ENCODING就有多种:

127.0.0.1:6379> SET str1 12345
OK
127.0.0.1:6379> OBJECT ENCODING str1
"int"
127.0.0.1:6379> SET str2 "An embstr is small"
OK
127.0.0.1:6379> OBJECT ENCODING str2
"embstr"
127.0.0.1:6379> SET str3 "A raw encoded String is anything greater than 39 bytes"
OK
127.0.0.1:6379> OBJECT ENCODING str3
"raw"

List

List有ziplist(list-max-ziplist-entries和list-max-ziplist-value参数影响)和linkedlist两种ENCODING,前者更节省内存。

Set

Set有intset(全部是整数,以及set-max-intset-entries影响)和hashtable两种ENCODING。
The following are the available encodings for Sets:

Hash

Hash 有ziplist(hash-max-ziplist-entries和hash-max-ziplist-value参数影响)和 hashtable 两种ENCODING,前者更节省内存。
The following are the available encodings for Hashes:

Sorted Set

ziplist,skiplist或hashtable三种ENCODING。

Measuring memory usage

其实就是info memory在代码前后执行两次,相减即可得到内存。

节省内存只是一方面,ziplist中,成员越多,性能越低,因此需要权衡。例如Instagram 发现Hash包含1000个成员时效果最好,参见: http://instagram-engineering.tumblr.com/post/12202313862/storing-hundreds-of-millions-of-simple-key-value

Summary

本章更偏向于系统管理员而非开发人员。
介绍了消息队列的发布/订阅,交易和pipeline。
用于系统管理和监控的命令。
通过ENCODING实现数据类型优化

你可能感兴趣的:(redis,读书笔记)