|
* m/ I. O2 N' n" B" R
本文主要介绍在使用阿里云Redis的开发规范,从下面几个方面进行说明。
; ?+ [) a, m. b$ ?: k# {- [ b# ^0 F# k8 v {$ h* n
- 键值设计
' F% {; V8 n# T7 \ - 命令使用0 t5 |" z2 V! v! O( C+ m
- 客户端使用
, z: L- X. e8 V/ t' n9 x7 ^ - 相关工具
1 N+ e* r" u% ]1 M- p 通过本文的介绍可以减少使用Redis过程带来的问题。
* o1 S7 F" o3 j: V一、键值设计0 ?# F* d$ K: p$ q* k* N7 j* b
' G I9 t9 @, L% R6 C# G
1、key名设计2 l) n# ~# U- X' W/ S. m
8 }- p9 H4 }# v5 {: ^可读性和可管理性
1 R* v+ }0 i+ O* M1 Z/ Z' u: K# o. P' }9 h* Y p7 @( e/ S" H4 c
以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
6 g% P3 B0 f* u; E, Z5 S; ]' B7 ?* v, {
- ugc:video:1; j/ O9 y& ]& V
简洁性
$ D7 y9 J& o. W) J9 q d' D8 l z; C( e% v" f; q5 d! n. \) E
保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:& f0 v n3 s2 g6 d0 n
2 ^3 n2 v' t) {; V- user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}。
5 q; w$ m+ w. r 不要包含特殊字符. r8 F/ u; E' k3 V& n$ n9 P8 p
7 K& m9 Q+ Q d* H4 N反例:包含空格、换行、单双引号以及其他转义字符
% ?9 p1 I. Q, l/ f5 z4 L2、value设计! M% b. _1 W: m$ N2 X. I/ e6 g
; a: V$ G7 M( N
拒绝bigkey% O I0 B0 E$ |$ E) y
2 u$ z* z0 r' x- y防止网卡流量、慢查询,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。# k( ?0 D; w) V# _
反例:一个包含200万个元素的list。* Q! L1 b& z6 S
非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法9 D1 Q- I+ E, n/ ?# A
选择适合的数据类型
( V! S3 P r- H G- u% _( D! l, ]/ G! y5 v, ^5 v
例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)。了解下,Redis 为什么这么快?. b* f$ s* Y3 E! T
反例:
# o9 N; _ Z7 Z4 X6 g7 G, }0 j
y2 q! l1 k& f- set user:1:name tom
+ u# z8 {8 ?0 Y3 u1 n3 O" X" f - set user:1:age 19
4 B& ~; y4 @% ~1 n x( E/ z - set user:1:favor football L; a H0 _0 g. p5 H
正例:
a! d9 Z8 d0 M: u, P
) E H# Z0 b8 ?! m- hmset user:1 name tom age 19 favor football
0 C2 y. {! w" O3 w; |3 M# g 控制key的生命周期) s" b: x% h* l& I
1 F: [! I% `! j1 ^
redis不是垃圾桶,建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注idletime。! L; r4 w; E: I- J9 v
二、命令使用2 R2 h* f9 z8 j1 g6 @
, s6 Z/ B& d0 r% M9 w. B3 ^) W1、O(N)命令关注N的数量
1 o2 O, I \+ R& b5 R" U0 ~' z7 v8 l
例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscan、sscan、zscan代替。. H$ C! m4 \) X* C
2、禁用命令( U! F( J& k9 t' i
% h7 k8 u- c3 e$ D6 s6 H7 g
禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。一个致命的 Redis 命令,导致公司损失 400 万!!关注Java技术栈微信公众号,在后台回复关键字:redis,可以获取更多栈长整理的 Redis 系列技术干货。
! U' y8 t. `; \9 c8 q/ J7 B( J) k3、合理使用select
/ |% M! S; l! J: k# f9 O7 W: @; N X' t
+ h) x3 N* X9 P3 z5 i- `# ^7 g. O/ J
redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。9 A l1 u( K5 v7 x
4、使用批量操作提高效率, V( K/ }* r: P0 t
; Z1 q) j( _( v( r- q" L- p& b# Y4 m2 p7 |
- 原生命令:例如mget、mset。
) F" e( Y6 w; X1 a6 | - 非原生命令:可以使用pipeline提高效率。
' b7 M) V6 {$ X M! Y% ? 但要注意控制一次批量操作的元素个数(例如500以内,实际也和元素字节数有关)。
8 m2 z9 `! D r9 z2 d注意两者不同:
$ Y, K# p9 i) Q
) Z8 H* o4 I* \6 P% S- 原生是原子操作,pipeline是非原子操作。7 j2 L2 B7 ]7 G+ v+ x# M* V
- pipeline可以打包不同的命令,原生做不到, T5 O) J' T5 ]4 Y6 v; h" v0 S
- pipeline需要客户端和服务端同时支持。) ]8 m6 L7 Z' c0 [8 N) p! f" @
5、不建议过多使用Redis事务功能
# e+ }/ ~; R/ @$ P: s+ s, W" @* h& Z# N9 f) i4 r9 A
Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)。分布式事务不理解?一次给你讲清楚!4 q5 d( P7 S/ E, Y' a
6、Redis集群版本在使用Lua上有特殊要求
( K1 G' z! n) z% @' [2 |% K3 b5 ~! V3 e/ F( a
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"- W3 ~% p% P% U( }8 l! M( N, H' M O
7、monitor命令5 K/ p% a% I9 l0 } _
# }/ z9 \% n- Z- ?
必要情况下使用monitor命令时,要注意不要长时间使用。
( Z8 Z% @3 v$ ^( w5 D% `" }( T2 c三、客户端使用
w; e; B o! l+ Z/ N& H" e) s5 y' x4 k* A" p
1、避免多个应用使用一个Redis实例2 q: S: t2 s0 o9 r$ h
, \4 z5 y; C9 R: V m
不相干的业务拆分,公共数据做服务化。0 n8 v \, k+ @& u# I
2、使用连接池# [! K/ \5 l; ^/ T7 X
1 L _; D& O$ Q
可以有效控制连接,同时提高效率,标准使用方式:9 j4 z, t4 R+ m+ w$ H+ Z9 {
Jedis jedis = null;
b4 r, Y: y, D4 etry {
7 p9 h/ e p8 ]0 U jedis = jedisPool.getResource;
1 G; i# w" J5 ^% b //具体的命令+ w- E8 D8 j( U9 m9 m7 W
jedis.executeCommand9 |9 P' r5 Z! s1 M. w% O
} catch (Exception e) {
2 \/ _; t' M+ a logger.error("op key {} error: " + e.getMessage, key, e);
) l* O; H. x- p( s9 L/ N: P* X} finally {% c9 O) ?) t% g% O0 E! b: ^% R
//注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
% c! D9 l5 v/ I# ^ a& H* f4 ~ if (jedis != null) 1 k4 H E$ Z1 ^) \1 a
jedis.close;
7 T& M0 q3 q) N( Y8 n" F, `}
" r# S w" P2 X$ _- R" B' {3、熔断功能
6 O2 k1 A$ \& f5 V6 V7 Q* |. }& [- \& v7 |2 R3 \0 N7 X
高并发下建议客户端添加熔断功能(例如netflix hystrix)
6 m s% F+ { ^6 r9 N4、合理的加密2 T+ {1 w0 ], t
! E6 i" M* r* J, ]3 i @
设置合理的密码,如有必要可以使用SSL加密访问(阿里云Redis支持)6 w! d+ w/ S R- m% e5 w Q! }
5、淘汰策略# P7 E7 i7 X8 j1 Z P. M8 ~0 j
9 ]" O7 p; I4 L( h$ ~9 ^* t; _+ w根据自身业务类型,选好maxmemory-policy(最大内存淘汰策略),设置好过期时间。
8 W% e& D- P3 P. D, Q默认策略是volatile-lru,即超过最大内存后,在过期键中使用lru算法进行key的剔除,保证不过期数据不被删除,但是可能会出现OOM问题。: ^$ y/ b v/ R8 |
其他策略如下:
6 Q0 U7 B/ I0 U' l8 [% H$ p3 l* R# v! d
- allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
! M4 c5 A+ f( ~/ R - allkeys-random:随机删除所有键,直到腾出足够空间为止。
0 [% O; s2 H0 K4 m - volatile-random:随机删除过期键,直到腾出足够空间为止。- ^! ^7 ?: v+ a# [& Y! I
- volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。1 o0 W: |$ ?1 H' K4 Y, t# Z$ W
- noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。
1 F6 a' D1 O8 G; \4 x# h' c$ H 四、相关工具2 w4 o; u+ v/ `6 A7 m& d+ g
# Z7 ~" \1 c7 R: e$ G
1、数据同步5 {# V/ T: U# e7 |
& i' C( A8 F, e7 Tredis间数据同步可以使用:redis-port" j/ ]7 ]$ L* [& R. V- |
2、big key搜索2 ~8 p) `8 R6 P( y
* W+ Z( c! d" z4 f) P: a; W% A
redis大key搜索工具
( g! c& K$ x$ \: k2 R3、热点key寻找0 u! ?9 F8 ], O" ^6 s' q# Y& |
* i( {3 O. J$ U8 o! a( L
内部实现使用monitor,所以建议短时间使用facebook的redis-faina 阿里云Redis已经在内核层面解决热点key问题
p/ r& u2 }" b/ `8 u# f8 V9 V& L2 |五、删除bigkey
6 j. v& M$ f8 y8 e& a3 o# Q \/ K5 i N+ l- I
+ E' C; E3 @* ?7 W. k% c/ l
- 下面操作可以使用pipeline加速。) t. s& o- m% M: ]; G
- redis 4.0已经支持key的异步删除,欢迎使用。
$ G8 R, G5 K& }. t) K: {9 k 1、Hash删除: hscan + hdel2 t* Y. @7 \8 v0 `4 s3 n/ J
9 `* ]8 o: o, x* `& n! Z) ypublic void delBigHash(String host, int port, String password, String bigHashKey) {
) b2 v6 o' S9 m3 R7 E! t' }& E v Jedis jedis = new Jedis(host, port);
/ |6 o! n( i- X if (password != null && !"".equals(password)) {
/ K: N1 }. ^) D, a9 l jedis.auth(password);
! ~) ]! l. F" e/ i( u& a# a' R }% y4 \9 s' q7 p% o. p
ScanParams scanParams = new ScanParams.count(100);2 C( C) y; ]$ Z: `9 F, J
String cursor = "0";: X, s/ f+ a7 p7 Q! f
do {8 e' t" X* S, C Q4 ]2 Z2 \) W
ScanResult scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
0 {) {0 ~* ^) E6 [: @; ` List entryList = scanResult.getResult;
/ {& R U; r& h; Z3 m if (entryList != null && !entryList.isEmpty) {- k6 S; D/ N: O6 u/ ]! Q5 s
for (Entry entry : entryList) {
' a0 V2 j8 i0 u2 ?# r3 u jedis.hdel(bigHashKey, entry.getKey);& q( G3 Q* B4 L8 g8 ?
}* B) @3 W! M( }& {0 d6 z3 K
}
2 H2 u) W) e! o8 Q2 N cursor = scanResult.getStringCursor;* c1 m% P: p% b
} while (!"0".equals(cursor));
" n; Y+ x9 a) M4 Y" {% _+ X
/ x" d- H5 Y4 ~# L/ X( S7 k+ q//删除bigkey# p3 M2 m. r! Y }& I1 b; ~! t% J1 j
jedis.del(bigHashKey);$ S# N- Q5 Q& P2 J- f" ?; r
}
( K- ~- r6 Q H2、List删除: ltrim
- y$ t& Q' p4 r8 H( s0 c: K+ G( v+ `& W$ P# ^; x
public void delBigList(String host, int port, String password, String bigListKey) {. j3 U/ W* p+ d# [$ K0 d @
Jedis jedis = new Jedis(host, port);4 _" C* b4 r: B- Z* P9 `
if (password != null && !"".equals(password)) {& K, Y2 g g* A, t$ M8 J, c" ?
jedis.auth(password);
8 e9 ?" v8 D% P* G0 D3 M }
7 u- F9 f0 a( I* J5 L1 _6 {: o long llen = jedis.llen(bigListKey);$ u6 h1 J; M1 S G
int counter = 0;
+ |5 J0 l, K0 z4 |; c. f% G int left = 100;8 U) t6 P5 Q) Y' q# B; z
while (counter < llen) {
1 }9 N" U j9 `" { //每次从左侧截掉100个
& r8 L( H. \- T& e jedis.ltrim(bigListKey, left, llen);$ X y8 h/ q0 K; ^ @& p
counter += left;
* O" _: G) u: b1 m }
; {) d6 F, J* w6 @ //最终删除key
* Z( c0 Z$ g; Y/ B3 o8 Z jedis.del(bigListKey);/ _' p9 @ Q1 @+ h j
}3、Set删除: sscan + srem, ?4 i) }1 r" ?) i, \
5 V# z/ m2 z( W% X
public void delBigSet(String host, int port, String password, String bigSetKey) {8 Q$ X6 @( `$ ~* q& ^& E% p7 q" d
Jedis jedis = new Jedis(host, port);% \% Q6 g7 r5 N1 G0 V; q' q
if (password != null && !"".equals(password)) {
, F+ L9 j- \/ ?( f7 { j jedis.auth(password);3 U" \& r8 o7 u- `1 W1 }. J* a
}
y# X2 U& q" Y ScanParams scanParams = new ScanParams.count(100);% L+ |) j% p) p4 S8 F* V d
String cursor = "0";
$ ]1 D0 ~2 b$ w6 Y do {7 _) i: c. C( m, o) l* e( j
ScanResult scanResult = jedis.sscan(bigSetKey, cursor, scanParams);+ o/ T1 H& ]- [6 p
List memberList = scanResult.getResult;+ _# |" Q1 Z) E3 u. A
if (memberList != null && !memberList.isEmpty) {" J! i' x# N( G" l
for (String member : memberList) {- z& ]" Y- Q; d/ B
jedis.srem(bigSetKey, member);# e7 ~! L4 |5 }% Q3 [9 j
}
3 G6 [4 q- h8 a$ U& n. E }
) d% a$ d& I7 Y0 R5 z9 q cursor = scanResult.getStringCursor;
! C' B5 R$ u# _7 V } while (!"0".equals(cursor));
7 ?' m) X0 q3 x. O. z: T" C$ X, o u4 H! @
//删除bigkey) \( J/ x4 p$ }
jedis.del(bigSetKey);1 I7 V G; v1 @% h% C
}" }/ G9 ^$ n* G
4、SortedSet删除: zscan + zrem
( |1 w8 o* f6 j7 r" u4 i
2 Z3 O) ]* H x% W" `+ Jpublic void delBigZset(String host, int port, String password, String bigZsetKey) {
* H8 _$ f# A; R Jedis jedis = new Jedis(host, port); : R# P) \" n* T6 j' R
if (password != null && !"".equals(password)) {
8 h" D" t: m$ y1 P A jedis.auth(password); - v* _5 _5 Q! @5 s- S
} $ h" P* R% C3 U1 F U0 }! x
ScanParams scanParams = new ScanParams.count(100);
, N& H3 |5 L) x7 o String cursor = "0"; & z* P4 j9 ^9 L
do {
8 y* e* U7 a/ n" o2 E$ _ ScanResult scanResult = jedis.zscan(bigZsetKey, cursor, scanParams); 1 @' {/ F" c" R) X& G. G
ListtupleList = scanResult.getResult; W2 m; e! T g7 g& O
if (tupleList != null && !tupleList.isEmpty) { 2 b, U- W6 X$ h6 i7 P6 v% M
for (Tuple tuple : tupleList) {
/ z3 X% S, F- v( ?' d/ W, F jedis.zrem(bigZsetKey, tuple.getElement);
( J9 L' q; F/ d } / s0 @% k0 F' z
}
& g; O S+ e1 b8 d a. \2 k3 i cursor = scanResult.getStringCursor; # p9 H6 A% [8 f1 a+ R7 A
} while (!"0".equals(cursor));
/ S4 ]/ b: h& G K' a9 ~
" Z1 Z* z0 W. g6 P6 m# Y8 k//删除bigkey 5 Q" u- Q" e/ d% T `8 C( E) e
jedis.del(bigZsetKey); ( N& I5 j! \% J* G
} 公众号内回复“1”带你进粉丝群
% d+ i5 H9 E7 S. S) N k8 ?5 l7 M& P来源:http://www.yidianzixun.com/article/0LevQm7t
! j: I& n; A/ @* _& A免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|