关键词搜索

源码搜索 ×
×

漫话Redis源码之十四

发布2021-11-28浏览1450次

详情内容

SORT命令在Redis中算是比较复杂的,该函数的可读性挺差,主要是考虑了性能优化的因素。有得有失:对机器友好,但对人不友好。

  1. /* The SORT command is the most complex command in Redis. Warning: this code
  2. * is optimized for speed and a bit less for readability */
  3. void sortCommand(client *c) {
  4. list *operations;
  5. unsigned int outputlen = 0;
  6. int desc = 0, alpha = 0;
  7. long limit_start = 0, limit_count = -1, start, end;
  8. int j, dontsort = 0, vectorlen;
  9. int getop = 0; /* GET operation counter */
  10. int int_conversion_error = 0;
  11. int syntax_error = 0;
  12. robj *sortval, *sortby = NULL, *storekey = NULL;
  13. redisSortObject *vector; /* Resulting vector to sort */
  14. /* Create a list of operations to perform for every sorted element.
  15. * Operations can be GET */
  16. operations = listCreate();
  17. listSetFreeMethod(operations,zfree);
  18. j = 2; /* options start at argv[2] */
  19. /* The SORT command has an SQL-alike syntax, parse it */
  20. while(j < c->argc) {
  21. int leftargs = c->argc-j-1;
  22. if (!strcasecmp(c->argv[j]->ptr,"asc")) {
  23. desc = 0;
  24. } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {
  25. desc = 1;
  26. } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {
  27. alpha = 1;
  28. } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {
  29. if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)
  30. != C_OK) ||
  31. (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)
  32. != C_OK))
  33. {
  34. syntax_error++;
  35. break;
  36. }
  37. j+=2;
  38. } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) {
  39. storekey = c->argv[j+1];
  40. j++;
  41. } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {
  42. sortby = c->argv[j+1];
  43. /* If the BY pattern does not contain '*', i.e. it is constant,
  44. * we don't need to sort nor to lookup the weight keys. */
  45. if (strchr(c->argv[j+1]->ptr,'*') == NULL) {
  46. dontsort = 1;
  47. } else {
  48. /* If BY is specified with a real patter, we can't accept
  49. * it in cluster mode. */
  50. if (server.cluster_enabled) {
  51. addReplyError(c,"BY option of SORT denied in Cluster mode.");
  52. syntax_error++;
  53. break;
  54. }
  55. }
  56. j++;
  57. } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
  58. if (server.cluster_enabled) {
  59. addReplyError(c,"GET option of SORT denied in Cluster mode.");
  60. syntax_error++;
  61. break;
  62. }
  63. listAddNodeTail(operations,createSortOperation(
  64. SORT_OP_GET,c->argv[j+1]));
  65. getop++;
  66. j++;
  67. } else {
  68. addReplyErrorObject(c,shared.syntaxerr);
  69. syntax_error++;
  70. break;
  71. }
  72. j++;
  73. }
  74. /* Handle syntax errors set during options parsing. */
  75. if (syntax_error) {
  76. listRelease(operations);
  77. return;
  78. }
  79. /* Lookup the key to sort. It must be of the right types */
  80. if (!storekey)
  81. sortval = lookupKeyRead(c->db,c->argv[1]);
  82. else
  83. sortval = lookupKeyWrite(c->db,c->argv[1]);
  84. if (sortval && sortval->type != OBJ_SET &&
  85. sortval->type != OBJ_LIST &&
  86. sortval->type != OBJ_ZSET)
  87. {
  88. listRelease(operations);
  89. addReplyErrorObject(c,shared.wrongtypeerr);
  90. return;
  91. }
  92. /* Now we need to protect sortval incrementing its count, in the future
  93. * SORT may have options able to overwrite/delete keys during the sorting
  94. * and the sorted key itself may get destroyed */
  95. if (sortval)
  96. incrRefCount(sortval);
  97. else
  98. sortval = createQuicklistObject();
  99. /* When sorting a set with no sort specified, we must sort the output
  100. * so the result is consistent across scripting and replication.
  101. *
  102. * The other types (list, sorted set) will retain their native order
  103. * even if no sort order is requested, so they remain stable across
  104. * scripting and replication. */
  105. if (dontsort &&
  106. sortval->type == OBJ_SET &&
  107. (storekey || c->flags & CLIENT_LUA))
  108. {
  109. /* Force ALPHA sorting */
  110. dontsort = 0;
  111. alpha = 1;
  112. sortby = NULL;
  113. }
  114. /* Destructively convert encoded sorted sets for SORT. */
  115. if (sortval->type == OBJ_ZSET)
  116. zsetConvert(sortval, OBJ_ENCODING_SKIPLIST);
  117. /* Objtain the length of the object to sort. */
  118. switch(sortval->type) {
  119. case OBJ_LIST: vectorlen = listTypeLength(sortval); break;
  120. case OBJ_SET: vectorlen = setTypeSize(sortval); break;
  121. case OBJ_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
  122. default: vectorlen = 0; serverPanic("Bad SORT type"); /* Avoid GCC warning */
  123. }
  124. /* Perform LIMIT start,count sanity checking. */
  125. start = (limit_start < 0) ? 0 : limit_start;
  126. end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
  127. if (start >= vectorlen) {
  128. start = vectorlen-1;
  129. end = vectorlen-2;
  130. }
  131. if (end >= vectorlen) end = vectorlen-1;
  132. /* Whenever possible, we load elements into the output array in a more
  133. * direct way. This is possible if:
  134. *
  135. * 1) The object to sort is a sorted set or a list (internally sorted).
  136. * 2) There is nothing to sort as dontsort is true (BY <constant string>).
  137. *
  138. * In this special case, if we have a LIMIT option that actually reduces
  139. * the number of elements to fetch, we also optimize to just load the
  140. * range we are interested in and allocating a vector that is big enough
  141. * for the selected range length. */
  142. if ((sortval->type == OBJ_ZSET || sortval->type == OBJ_LIST) &&
  143. dontsort &&
  144. (start != 0 || end != vectorlen-1))
  145. {
  146. vectorlen = end-start+1;
  147. }
  148. /* Load the sorting vector with all the objects to sort */
  149. vector = zmalloc(sizeof(redisSortObject)*vectorlen);
  150. j = 0;
  151. if (sortval->type == OBJ_LIST && dontsort) {
  152. /* Special handling for a list, if 'dontsort' is true.
  153. * This makes sure we return elements in the list original
  154. * ordering, accordingly to DESC / ASC options.
  155. *
  156. * Note that in this case we also handle LIMIT here in a direct
  157. * way, just getting the required range, as an optimization. */
  158. if (end >= start) {
  159. listTypeIterator *li;
  160. listTypeEntry entry;
  161. li = listTypeInitIterator(sortval,
  162. desc ? (long)(listTypeLength(sortval) - start - 1) : start,
  163. desc ? LIST_HEAD : LIST_TAIL);
  164. while(j < vectorlen && listTypeNext(li,&entry)) {
  165. vector[j].obj = listTypeGet(&entry);
  166. vector[j].u.score = 0;
  167. vector[j].u.cmpobj = NULL;
  168. j++;
  169. }
  170. listTypeReleaseIterator(li);
  171. /* Fix start/end: output code is not aware of this optimization. */
  172. end -= start;
  173. start = 0;
  174. }
  175. } else if (sortval->type == OBJ_LIST) {
  176. listTypeIterator *li = listTypeInitIterator(sortval,0,LIST_TAIL);
  177. listTypeEntry entry;
  178. while(listTypeNext(li,&entry)) {
  179. vector[j].obj = listTypeGet(&entry);
  180. vector[j].u.score = 0;
  181. vector[j].u.cmpobj = NULL;
  182. j++;
  183. }
  184. listTypeReleaseIterator(li);
  185. } else if (sortval->type == OBJ_SET) {
  186. setTypeIterator *si = setTypeInitIterator(sortval);
  187. sds sdsele;
  188. while((sdsele = setTypeNextObject(si)) != NULL) {
  189. vector[j].obj = createObject(OBJ_STRING,sdsele);
  190. vector[j].u.score = 0;
  191. vector[j].u.cmpobj = NULL;
  192. j++;
  193. }
  194. setTypeReleaseIterator(si);
  195. } else if (sortval->type == OBJ_ZSET && dontsort) {
  196. /* Special handling for a sorted set, if 'dontsort' is true.
  197. * This makes sure we return elements in the sorted set original
  198. * ordering, accordingly to DESC / ASC options.
  199. *
  200. * Note that in this case we also handle LIMIT here in a direct
  201. * way, just getting the required range, as an optimization. */
  202. zset *zs = sortval->ptr;
  203. zskiplist *zsl = zs->zsl;
  204. zskiplistNode *ln;
  205. sds sdsele;
  206. int rangelen = vectorlen;
  207. /* Check if starting point is trivial, before doing log(N) lookup. */
  208. if (desc) {
  209. long zsetlen = dictSize(((zset*)sortval->ptr)->dict);
  210. ln = zsl->tail;
  211. if (start > 0)
  212. ln = zslGetElementByRank(zsl,zsetlen-start);
  213. } else {
  214. ln = zsl->header->level[0].forward;
  215. if (start > 0)
  216. ln = zslGetElementByRank(zsl,start+1);
  217. }
  218. while(rangelen--) {
  219. serverAssertWithInfo(c,sortval,ln != NULL);
  220. sdsele = ln->ele;
  221. vector[j].obj = createStringObject(sdsele,sdslen(sdsele));
  222. vector[j].u.score = 0;
  223. vector[j].u.cmpobj = NULL;
  224. j++;
  225. ln = desc ? ln->backward : ln->level[0].forward;
  226. }
  227. /* Fix start/end: output code is not aware of this optimization. */
  228. end -= start;
  229. start = 0;
  230. } else if (sortval->type == OBJ_ZSET) {
  231. dict *set = ((zset*)sortval->ptr)->dict;
  232. dictIterator *di;
  233. dictEntry *setele;
  234. sds sdsele;
  235. di = dictGetIterator(set);
  236. while((setele = dictNext(di)) != NULL) {
  237. sdsele = dictGetKey(setele);
  238. vector[j].obj = createStringObject(sdsele,sdslen(sdsele));
  239. vector[j].u.score = 0;
  240. vector[j].u.cmpobj = NULL;
  241. j++;
  242. }
  243. dictReleaseIterator(di);
  244. } else {
  245. serverPanic("Unknown type");
  246. }
  247. serverAssertWithInfo(c,sortval,j == vectorlen);
  248. /* Now it's time to load the right scores in the sorting vector */
  249. if (!dontsort) {
  250. for (j = 0; j < vectorlen; j++) {
  251. robj *byval;
  252. if (sortby) {
  253. /* lookup value to sort by */
  254. byval = lookupKeyByPattern(c->db,sortby,vector[j].obj,storekey!=NULL);
  255. if (!byval) continue;
  256. } else {
  257. /* use object itself to sort by */
  258. byval = vector[j].obj;
  259. }
  260. if (alpha) {
  261. if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
  262. } else {
  263. if (sdsEncodedObject(byval)) {
  264. char *eptr;
  265. vector[j].u.score = strtod(byval->ptr,&eptr);
  266. if (eptr[0] != '\0' || errno == ERANGE ||
  267. isnan(vector[j].u.score))
  268. {
  269. int_conversion_error = 1;
  270. }
  271. } else if (byval->encoding == OBJ_ENCODING_INT) {
  272. /* Don't need to decode the object if it's
  273. * integer-encoded (the only encoding supported) so
  274. * far. We can just cast it */
  275. vector[j].u.score = (long)byval->ptr;
  276. } else {
  277. serverAssertWithInfo(c,sortval,1 != 1);
  278. }
  279. }
  280. /* when the object was retrieved using lookupKeyByPattern,
  281. * its refcount needs to be decreased. */
  282. if (sortby) {
  283. decrRefCount(byval);
  284. }
  285. }
  286. server.sort_desc = desc;
  287. server.sort_alpha = alpha;
  288. server.sort_bypattern = sortby ? 1 : 0;
  289. server.sort_store = storekey ? 1 : 0;
  290. if (sortby && (start != 0 || end != vectorlen-1))
  291. pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
  292. else
  293. qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
  294. }

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载