本章介绍管理Redis的一些常用命令以及Lua scripting。
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.
由于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"
Redis的Pipeline是将一组命令一次性发送到服务器,然后将所有的结果一并取回,而不是逐个的等待结果,这和通常UNIX Shell中的pipeline不一样。
Pipeline的目的是减少RTT,避免网络延迟。
Pipeline中的命令必须是不相互依赖的命令,尽管他们是按序执行的,但并非按交易执行。
其实node.js就是按Pipeline模式执行的,但不能保证所有的客户端都是如此。
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。
返回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>
当前服务器中key的数量
127.0.0.1:6379> DBSIZE
(integer) 5
让Redis Server崩溃的命令,主要用于模拟错误和调试。
生产系统慎用,否则服务器会core dump:
Segmentation fault (core dumped)
对于调试和监控非常有用!前面已经用过多次了。不过在生产系统慎用,可能将服务器的吞吐量减少一半。
还是用于调试和监控。
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的连接,可以根据IP地址,ID等。
删除所有的key
127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> dbsize
(integer) 0
随机返回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"
设置key的有效期,过期后会被删除。单位为秒,若为负数则马上删除,相当于DEL。
显示key的有效期。
去除有效期,让可以永远有效
同时设置key和有效期
删除一个或多个key
判断key是否存在
127.0.0.1:6379> SET mykey myvalue
OK
127.0.0.1:6379> EXISTS mykey
(integer) 1
服务器是否可联系
127.0.0.1:6379> PING
PONG
在redis服务器之间迁移key,可以是COPY或MOVE
redis server支持多个数据库,不过不推荐罢了,更建议使用多个服务器,因为可以更好的利用CPU,毕竟Redis-Server是单线程执行。
SELECT相当于Sybase中的use database。
认证客户端,提供口令给服务器
中断Lua Script
断掉所有客户端连接,关闭服务器。可以选择进行持久化(SHUTDOWN SAVE)或放弃内存中数据(SHUTDOWN NOSAVE)
显示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"
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的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有ziplist(list-max-ziplist-entries和list-max-ziplist-value参数影响)和linkedlist两种ENCODING,前者更节省内存。
Set有intset(全部是整数,以及set-max-intset-entries影响)和hashtable两种ENCODING。
The following are the available encodings for Sets:
Hash 有ziplist(hash-max-ziplist-entries和hash-max-ziplist-value参数影响)和 hashtable 两种ENCODING,前者更节省内存。
The following are the available encodings for Hashes:
ziplist,skiplist或hashtable三种ENCODING。
其实就是info memory在代码前后执行两次,相减即可得到内存。
节省内存只是一方面,ziplist中,成员越多,性能越低,因此需要权衡。例如Instagram 发现Hash包含1000个成员时效果最好,参见: http://instagram-engineering.tumblr.com/post/12202313862/storing-hundreds-of-millions-of-simple-key-value
本章更偏向于系统管理员而非开发人员。
介绍了消息队列的发布/订阅,交易和pipeline。
用于系统管理和监控的命令。
通过ENCODING实现数据类型优化