Show last authors
author | version | line-number | content |
---|---|---|---|
1 | get命令本质是调用了lookupKeyRead这个底层方法, | ||
2 | lookupKeyRead方法如下: | ||
3 | |||
4 | {{plantuml}} | ||
5 | @startuml | ||
6 | start | ||
7 | |||
8 | |||
9 | : lookupKeyReadWithFlags; | ||
10 | |||
11 | |||
12 | end | ||
13 | @enduml | ||
14 | {{/plantuml}} | ||
15 | |||
16 | |||
17 | lookupKeyReadWithFlags方法如下: | ||
18 | |||
19 | {{plantuml}} | ||
20 | @startuml | ||
21 | start | ||
22 | |||
23 | if(调用expireIfNeeded方法判断key是否过期)then(1:代表过期) | ||
24 | if(当前环境为主节点)then(yes) | ||
25 | :返回null; | ||
26 | stop | ||
27 | endif | ||
28 | |||
29 | if(当前环境为从节点,并且命令为读的时候,那说明key过期是安全的)then(yes) | ||
30 | :返回null; | ||
31 | stop | ||
32 | endif | ||
33 | endif | ||
34 | |||
35 | :调用lookupKey方法,查询value的值; | ||
36 | if(返回值为空)then(yes) | ||
37 | :缓存命中+1; | ||
38 | else(no) | ||
39 | :缓存非命中+1; | ||
40 | endif | ||
41 | |||
42 | :返回查询的结果; | ||
43 | |||
44 | end | ||
45 | @enduml | ||
46 | {{/plantuml}} | ||
47 | |||
48 | |||
49 | 先说明,redis中,过期时间等信息是单独放在一个table中存储的,因为不是所有key都设有过期时间,放在一起存储会额外增加存储成本。 | ||
50 | |||
51 | expireIfNeeded方法用来判断一个key是否过期 | ||
52 | 返回1,说明已经过期 | ||
53 | 返回0,说明数据没有过期 | ||
54 | |||
55 | {{plantuml}} | ||
56 | @startuml | ||
57 | start | ||
58 | |||
59 | |||
60 | :查询key的过期时间戳; | ||
61 | :获取当前时刻的时间戳; | ||
62 | if(过期时间<0)then(yes) | ||
63 | :说明该key不存在过期设置,返回0; | ||
64 | stop | ||
65 | endif | ||
66 | |||
67 | if(服务器正在启动中)then(yes) | ||
68 | :过期时间的table可能没完全加载,这时直接返回0; | ||
69 | stop | ||
70 | endif | ||
71 | |||
72 | if(当前环境是从服务器)then(yes) | ||
73 | :对于从服务器无需删除过期key, 直接计算过期时间和当前时间的关系并返回; | ||
74 | note left | ||
75 | 过期时间 <= 当前时间,返回1 : 已经过期 | ||
76 | 过期时间 > 当前时间,返回0 : 没有过期 | ||
77 | endnote | ||
78 | stop | ||
79 | endif | ||
80 | |||
81 | :写aof删除过期key; | ||
82 | :创建消息通知,用来回调那些监控key过期的钩子; | ||
83 | if(判断服务器是否打开惰性删除)then(yes) | ||
84 | :调用dbAsyncDelete方法异步删除; | ||
85 | else(no) | ||
86 | :调用dbSyncDelete方法同步删除; | ||
87 | endif | ||
88 | :返回删除结果; | ||
89 | note left | ||
90 | 删除方法返回值说明 | ||
91 | 1: 说明数据被删除了 | ||
92 | 0: 说明数据删除失败了,在过期表中key还存在 | ||
93 | endnote | ||
94 | |||
95 | end | ||
96 | @enduml | ||
97 | {{/plantuml}} | ||
98 | |||
99 | |||
100 | 再看同步删除dbSyncDelete的策略 | ||
101 | |||
102 | {{plantuml}} | ||
103 | @startuml | ||
104 | start | ||
105 | |||
106 | if(key过期表是否为空)then(no) | ||
107 | :删除过期表中的key; | ||
108 | endif | ||
109 | |||
110 | :执行dictDelete方法,删除dict表中具体的数据; | ||
111 | if(删除成功)then(yes) | ||
112 | :在集群模式下的redis,还需要删除cluster中的key,防止路由查询key失败; | ||
113 | :返回1; | ||
114 | else | ||
115 | :返回0; | ||
116 | endif | ||
117 | |||
118 | |||
119 | end | ||
120 | @enduml | ||
121 | {{/plantuml}} | ||
122 | |||
123 | |||
124 | 关于dictDelete这里就先不画图了,讲一下主要的实现逻辑吧。 | ||
125 | 可以大概认为,java中hashmap结构对应的就是redis中的dict结构,区别是jdk在1.8版本之后使用拉链转红黑树的方式处理哈希冲突,而redis使用的方法还是拉链存储法,对应着jdk1.7。 | ||
126 | 在redis中也存在内存常量池的概念,主要为是对应字符串/数字这种结构设计的,对于相同的字符串使用同一块内存空间。 | ||
127 | 这样的结构在删除key和value的时候就存在一个额外的问题,如果我直接free掉这块内存,那么其他key和value指向这块内存的读写就会存在异常。 | ||
128 | redis为了避免这样的问题,在dict删除一个key的过程实际是先将dict中的对应的key的指针删除,然后然后在根据情况(内存是否被共享)再决定是否去free内存空间。 | ||
129 | |||
130 | |||
131 | 再看异步删除dbAsyncDelete的策略 | ||
132 | {{plantuml}}@startuml | ||
133 | start | ||
134 | |||
135 | |||
136 | if(key过期表是否为空)then(no) | ||
137 | :删除过期表中的key; | ||
138 | endif | ||
139 | |||
140 | :调用dictUnlink方法在dict上删除key指针; | ||
141 | note left | ||
142 | 这里dictUnlink方法可以看作是上面dictDelete的一个同名方法, | ||
143 | 区别在于, | ||
144 | dictDelete会明确告诉redis尽可能free掉key和value占用的空间(如果key和value没用被共享的情况下), | ||
145 | |||
146 | 而dictUnlink则只移除dict上的key即可, | ||
147 | 一定不要free具体的内存, | ||
148 | 并把对应的entry(key->value的封装,类似于java中的map.entry对象)返回 | ||
149 | endnote | ||
150 | |||
151 | :获取entry中value占用的大小; | ||
152 | if(如果value的占用空间过多)then(yes) | ||
153 | :由于清理起来会耗用很长时间, | ||
154 | |||
155 | 这里会创建一个删除任务, | ||
156 | 本质是将要删除的entry扔到一个清理队列中, | ||
157 | 后台线程定期对这个队列中的数据清理; | ||
158 | :将entry指针指向空; | ||
159 | endif | ||
160 | |||
161 | if(entry不为空,说明没有被异步删除)then(yes) | ||
162 | :直接清理entry; | ||
163 | :返回1; | ||
164 | else(no) | ||
165 | :返回0; | ||
166 | endif | ||
167 | |||
168 | |||
169 | |||
170 | end | ||
171 | @enduml{{/plantuml}} |