|
|
+ Q6 p* Y" o, p. s
本文主要介绍在使用阿里云Redis的开发规范,从下面几个方面进行说明。& I( e6 Z: I' [5 O
7 Q1 c/ n9 C8 E4 t/ T
- 键值设计+ E" J9 ]9 k' M
- 命令使用
; z, \) |) D P# ?5 q* `3 w' Q - 客户端使用9 ]+ q/ s5 Z7 {9 H1 X9 d
- 相关工具0 N4 _1 @5 t. ^
通过本文的介绍可以减少使用Redis过程带来的问题。
. n/ s# A' |$ E% @一、键值设计
6 E( y" w8 M9 m" {
6 P9 k/ j0 x6 a, w k5 l, W' [1、key名设计) H; K8 c4 R. f
: d0 K+ ?; D+ R$ {
可读性和可管理性0 C) j7 m O2 z- q
' \% D& j. r: R! M1 E$ A" k
以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id) U* V7 E8 n3 F; D2 {8 e! v
7 ?2 y; k( t8 S8 j. N- ugc:video:1
6 U2 L/ @$ X9 e; B1 V. T* } 简洁性3 k3 n. F4 O) o+ n' b% R
9 @! i7 @5 K9 I" a$ r保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:) t4 r- t8 Q) `
" d# |0 D# L# t
- user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}。! w0 E" F/ V9 K: Q, q
不要包含特殊字符
F, o2 A) n6 |: J; G+ v
& s# [7 A9 U" t$ X L7 @5 l4 y反例:包含空格、换行、单双引号以及其他转义字符
! j3 Y- [% o; j, T+ V/ |( \5 e2、value设计
- ]+ ^- ?1 X! r6 X0 c
- |! [( Y9 e. A& ^, q6 e拒绝bigkey
$ e5 |4 C& A! P0 }8 M7 G
/ M/ y, c. ]6 F8 D防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。
, Z* A. T( t$ x0 B8 N3 P+ t反例:一个包含200万个元素的list。
6 @( C; S' d1 o5 e4 ^非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法
) R0 c0 I; c7 g8 g& a% g8 u" t3 f选择适合的数据类型
. Y/ N; Z8 X# @: Z; l
* C- p# i! u* Y% m+ b. t例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)。了解下,Redis 为什么这么快?; L/ N' {; o: ~. e7 G2 [
反例:( u% a" ]1 A4 A" f
% [# H6 T6 e3 M6 A- set user:1:name tom' j4 L0 S4 Y" ]& m
- set user:1:age 198 l& `1 o1 t9 P$ I q
- set user:1:favor football
. m9 o# m& h5 [" n, E# `5 C; `1 t 正例:
6 `4 G, F" D# ?6 Z& T+ a) E, M, G
- hmset user:1 name tom age 19 favor football5 a1 h; B( b4 [! T( ?& x
控制key的生命周期! t: W1 A5 y! r$ G& F
( S! c' v# G. s! a
redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。
, @# q; k9 X4 y5 S二、命令使用
' h. [9 l. T1 a T1 a
: g! ]& r: e& H3 z1、O(N)命令关注N的数量% u8 ~# x# ^+ L
# }9 \: y9 E/ `& h) o3 ^例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。8 W( {: |, s1 q; U7 c
2、禁用命令+ ~4 f2 g, ^ P! m) W3 @
2 g [8 d- U3 A& a4 }禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。一个致命的 Redis 命令,导致公司损失 400 万!!关注Java技术栈微信公众号,在后台回复关键字:redis,可以获取更多栈长整理的 Redis 系列技术干货。' I3 b% J8 x8 W2 H
3、合理使用select% i0 y2 a W5 \0 ]
/ D: Q1 `# o3 q2 l1 b, A4 e: w9 F4 N6 [+ a; `; f6 V; L# X
redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。
: J6 p+ `! H- @: ?4、使用批量操作提高效率
- ?& w: B9 }/ p7 ]* |" C
6 u! f1 J. p# z1 u& h& ?0 X% z& A0 ^$ I) \
- 原生命令:例如mget、mset。
4 w& F" k n7 y+ a/ ` - 非原生命令:可以使用pipeline提高效率。
b* P# R. x- \4 N0 z p2 M: z 但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
6 T9 }" f% B7 Z/ `7 N( h注意两者不同:
" H0 R3 V, f# d4 n, W+ g5 A
. t) @4 Z4 I* C1 N# t9 O* P- 原生是原子操作,pipeline是非原子操作。
) \# Z( a- u9 N+ o7 k' j - pipeline可以打包不同的命令,原生做不到8 }! S, Z h3 U
- pipeline需要客户端和服务端同时支持。" X. r" a8 s- V; ^6 x' _' `
5、不建议过多使用Redis事务功能
$ [8 p! b6 _; C
' L5 I1 t: Q" h$ E4 r0 _Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)。分布式事务不理解?一次给你讲清楚!' p7 r( m, n1 U
6、Redis集群版本在使用Lua上有特殊要求
/ q& n5 Y7 u) Y; Y0 l6 Z3 x. l
. `% _, X- M, }+ y6 w5 s/ `1、所有key都应该由 KEYS 数组来传递,redis.call/pcall 里面调用的redis命令,key的位置,必须是KEYS array, 否则直接返回error,"-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn" 2、所有key,必须在1个slot上,否则直接返回error, "-ERR eval/evalsha command keys must in same slotrn"
2 h- p1 T! S( d" T$ d7、monitor命令) a6 [' q/ \, ]7 K: h/ i6 ~# G
) E5 K5 k' Q& c |9 a
必要情况下使用monitor命令时,要注意不要长时间使用。
* t' ^' ^* s0 ^' K$ g三、客户端使用
8 Q3 g9 D* V5 v& Q
& E# J4 U8 N" [. B9 [' f1、避免多个应用使用一个Redis实例
3 P( x2 y- D( L/ X7 p
& g3 w3 c7 O6 C, J9 A+ @不相干的业务拆分,公共数据做服务化。
# B- t# H. u6 b# M0 i2、使用连接池
( C4 D' Z# R2 _
! V* ], T ~9 p0 o3 ]可以有效控制连接,同时提高效率,标准使用方式:4 H! y: i2 y+ f, ?
Jedis jedis = null;
, |5 o) r, g+ l& q$ Ktry {" [) s1 [, y9 }$ O$ G
jedis = jedisPool.getResource;
, t4 a9 u0 n6 U. \. r$ W0 n. g //具体的命令1 a7 }5 A. p: B
jedis.executeCommand
& `( m/ V; L( q; a4 T} catch (Exception e) {
0 ?5 o- `& D7 {3 ~4 s logger.error("op key {} error: " + e.getMessage, key, e);
, q. E% m+ s( i" x& J} finally {
8 n9 C9 K% X# R( ^# O: m, Z //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。0 t6 C1 P U3 R7 J5 j' U
if (jedis != null)
- P* w0 q! Z, b jedis.close;7 |3 J9 A. v3 n! ?8 J, E. |" p4 N
}& _6 u; h$ u" ~
3、熔断功能
4 O5 P. `1 k5 U% K1 U" u1 `+ V
: t& b _, W8 d2 v* f高并发下建议客户端添加熔断功能(例如netflix hystrix)* e' ^5 n5 X" v; a6 k4 s; R$ [( a
4、合理的加密6 C+ R/ M& S( n9 O8 u. h6 n" g8 V+ ]5 M
/ _4 b& |; U3 q* S g, n
设置合理的密码,如有必要可以使用SSL加密访问(阿里云Redis支持)
, {% S$ P1 ^$ Y+ J5、淘汰策略- e& w5 O8 S- k1 t
$ t) F$ g! S3 i2 Z) F' e
根据自身业务类型,选好maxmemory-policy(最大内存淘汰策略),设置好过期时间。
' M0 q& F# d0 }, T默认策略是volatile-lru,即超过最大内存后,在过期键中使用lru算法进行key的剔除,保证不过期数据不被删除,但是可能会出现OOM问题。- `4 G! _. l0 E+ O4 L
其他策略如下:2 I* b# a" a9 P x$ D: M+ X
- q" G7 n1 E' g
- allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。* I8 f# R) h( N R: Z+ y+ V& J
- allkeys-random:随机删除所有键,直到腾出足够空间为止。
( a4 ]2 U( @" \/ q$ f1 S# Z - volatile-random:随机删除过期键,直到腾出足够空间为止。/ f, Y" U: W* [
- volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。' @- M, O/ N3 j5 P$ q
- noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。# K3 k' \7 R r b) J% V
四、相关工具; ?! `; _9 T/ T3 Q) h# Q4 a* p) w% C
f7 e* }* d8 c
1、数据同步! t% ?2 g2 a7 j1 J6 W6 I% M
8 y5 ]9 @$ a4 _5 }% m, f, g; G# ^redis间数据同步可以使用:redis-port
9 }6 I3 Y+ S; D2、big key搜索
' V" ]6 v* Q$ H2 b9 `- F9 y
5 N Q E8 z+ u4 y7 wredis大key搜索工具
, _5 k8 D; m- j ]$ a8 z5 v3、热点key寻找
8 E. B5 {) U$ ]2 t) z- r- @ @+ v" W9 O8 Y8 H& ?4 e
内部实现使用monitor,所以建议短时间使用facebook的redis-faina 阿里云Redis已经在内核层面解决热点key问题
4 a% I8 f1 W3 r* X/ X6 @3 Q五、删除bigkey
T; _0 e0 x8 y: S3 b/ d
; W) y% u/ a" W
2 n5 U+ R* k' V- 下面操作可以使用pipeline加速。& ^8 `7 j) E" X4 e" H
- redis 4.0已经支持key的异步删除,欢迎使用。5 d! F$ `6 ?) Z ^% p2 x
1、Hash删除: hscan + hdel& n @5 H1 ^( O3 {* G- s
6 ?, p% I' F* y" n2 J( n' v
public void delBigHash(String host, int port, String password, String bigHashKey) {
7 a- w- ~3 l" [( Y2 k Jedis jedis = new Jedis(host, port);) {7 c' x3 Z# H" v+ h
if (password != null && !"".equals(password)) {" K& y1 T% e/ R' |5 e+ }
jedis.auth(password);8 H% Y" {. [( l {5 q9 \8 ^
}+ q' ~- O& ]$ L
ScanParams scanParams = new ScanParams.count(100);( C/ F' z4 a$ a% _7 [" Q( ]
String cursor = "0";0 V) O# V M$ @6 Z3 v0 X
do {2 f6 E' b; c* u6 e: U; c. L
ScanResult scanResult = jedis.hscan(bigHashKey, cursor, scanParams);+ B9 w/ q1 u: S5 R( ~+ p
List entryList = scanResult.getResult;
: f U6 r3 n! o2 x/ x% I8 g if (entryList != null && !entryList.isEmpty) {
) u" q- b: Y6 y$ D6 I: i" _/ g for (Entry entry : entryList) {1 u: G7 m8 J) s8 @' e2 R7 B# }
jedis.hdel(bigHashKey, entry.getKey);- w1 `2 E3 _' A! i
}9 N! r0 a" g% \* J) L) V" ?
}
H& d) R) X& ^ cursor = scanResult.getStringCursor;/ V, G6 Q! @, @
} while (!"0".equals(cursor));
4 x$ r/ L5 Y6 R+ [( a$ \5 c
8 F9 ^8 Z! P* d" Q//删除bigkey
' H' @& d+ E2 t jedis.del(bigHashKey);
; X9 J% W3 L; a8 ]8 o! g5 e}. ^- T+ V; G3 L& H3 \
2、List删除: ltrim3 m- ]3 Z; C0 f) e( S( e
) V/ j7 P' x, s) N4 @& S+ f& p
public void delBigList(String host, int port, String password, String bigListKey) {
* f2 i% ]/ c: Z0 G4 Z: `4 }3 P Jedis jedis = new Jedis(host, port);
! K' m$ x$ b# p0 r; s1 N if (password != null && !"".equals(password)) {& d$ q/ E @6 T' i
jedis.auth(password);
6 ?3 Y# K3 E0 v, Y/ o }
0 |" D" V4 j# n: D6 {. }0 o3 _- B long llen = jedis.llen(bigListKey);
4 R' E. U( W% `0 s* z int counter = 0;/ k1 P- L. k. E g. X0 E
int left = 100;
; Q2 M2 Z! D( s$ e% l. s while (counter < llen) {
0 T7 G/ s; C+ ?/ I/ Y4 y9 e& ? //每次从左侧截掉100个- B0 z. N0 M- w0 y5 n( c: A' }
jedis.ltrim(bigListKey, left, llen);
9 |8 S/ j2 r( q8 H counter += left;
) h0 _; h+ L, H0 h3 y2 q0 H }& z- o+ i2 [) I5 _3 S9 d7 \
//最终删除key! B/ h l7 o9 G: d
jedis.del(bigListKey);
+ u- q3 f" j+ |; P! D( [' h}3、Set删除: sscan + srem6 A8 f4 q" T+ e& O) Y2 S
% F1 u! P6 M) b4 p. Ppublic void delBigSet(String host, int port, String password, String bigSetKey) {- A; T2 z! n+ [+ d8 w6 H5 K$ y
Jedis jedis = new Jedis(host, port);# w! R1 K T6 ]' o# i
if (password != null && !"".equals(password)) {
! E7 ^0 S3 O7 \9 \% A$ \2 Z jedis.auth(password);
) \9 w1 T# y/ \0 M% U }
% n3 i4 U' L- n: M2 m. ? ScanParams scanParams = new ScanParams.count(100);
/ l( w$ d3 L1 h( T String cursor = "0";
: _2 ?% O% U; u3 O9 ` do {8 [7 K' S2 S6 C' ?4 H4 Y# e
ScanResult scanResult = jedis.sscan(bigSetKey, cursor, scanParams);
: ]5 {% N8 C, ]. ] List memberList = scanResult.getResult;/ f) _4 [" b0 U8 j* l
if (memberList != null && !memberList.isEmpty) { ]; e, F' n7 F
for (String member : memberList) {" g( m' l) g' l* G0 t
jedis.srem(bigSetKey, member);# X0 i& u. x6 p) B! y: P: K
}: u3 b/ z* ^9 K# e- R
}9 R _, B' i# a0 b- n
cursor = scanResult.getStringCursor;' j: J' M8 |+ l1 ]2 O, e* V A
} while (!"0".equals(cursor));2 u* o8 J$ ]& Q
4 A/ i9 M6 v ^7 B* L* }8 I; |5 ]& M
//删除bigkey
7 T5 Y+ W, G1 M jedis.del(bigSetKey);
5 P! g9 ` I3 p3 c}
" ~% B1 ^7 |; ?9 [' y4、SortedSet删除: zscan + zrem
m+ B) y6 K! i3 e7 {
" [7 G3 Y4 @) y# a: a' xpublic void delBigZset(String host, int port, String password, String bigZsetKey) { ) Y% U, E& y: v$ _
Jedis jedis = new Jedis(host, port); # O0 I8 B1 Q3 x M
if (password != null && !"".equals(password)) {
* g p: w( a* `; _ jedis.auth(password);
) \# k, ]" c. C' I5 D- C# o }
2 B9 T- R" L9 V- j, d9 e+ \ ScanParams scanParams = new ScanParams.count(100); 8 p' w- w9 B3 ?% z% N
String cursor = "0";
t; A2 b. U9 w- T/ k' N5 d: \ do {
( Z' F6 t7 h0 Y ScanResult scanResult = jedis.zscan(bigZsetKey, cursor, scanParams); ) u# b1 ^8 Z# Y5 }7 d9 O3 C
ListtupleList = scanResult.getResult;
% Z F% j+ h1 S5 ?" t0 e3 ~5 R; V if (tupleList != null && !tupleList.isEmpty) {
+ n r1 t" D( N# k. ~ for (Tuple tuple : tupleList) {
8 F$ t$ O/ K9 ^5 p jedis.zrem(bigZsetKey, tuple.getElement); ! u2 a. J0 Z; z/ }" I6 p! v, d
} " w) L* }9 T) w+ Q. U9 o# g: X R- z
} - x/ ?' k* X. V7 D2 Q
cursor = scanResult.getStringCursor;
; I; M0 J; C4 D2 b$ X, X. r' G } while (!"0".equals(cursor));* N3 g# r# y( ^! @' \* {1 H( v
" J0 l. ^ h% b7 N//删除bigkey ; G) K# v, J- Z4 W
jedis.del(bigZsetKey); _' c, B* d/ m; x1 [5 D
} 公众号内回复“1”带你进粉丝群
1 l3 K4 U$ C4 R6 c# b0 N来源:http://www.yidianzixun.com/article/0LevQm7t, y5 h9 Q, k( }4 {( v( i; ?
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|