这篇我们来看看Redis五大类型中的第四大类型:“集合类型”,集合类型还是蛮有意思的,第一个是因为它算是只使用key的Dictionary简易版,

这样说来的话,它就比Dictionary节省很多内存消耗,第二个是因为它和C#中的HashSet是一个等同类型,废话不多说,先看redis手册,如下:

上面就是redis中的set类型使用到的所有方法,还是老话,常用的方法也就那么四个(CURD)。。。

一: 常用方法

1. SAdd

  这个方法毫无疑问,就是向集合里面添加数据,比如下面这样,我往fruits集合里面添加喜爱的水果。

  1. 127.0.0.1:> sadd fruits apple
  2. (integer)
  3. 127.0.0.1:> sadd fruits banana
  4. (integer)
  5. 127.0.0.1:> smembers fruits
  6. ) "banana"
  7. ) "apple"
  8. 127.0.0.1:>

上面这个sadd你也看到了,我往集合里面成功添加了两个元素,现在你可能不满足这么简单的添加,你或许想知道set这个集合在redis底层是使用

什么来实现的,你可以用object encoding查看一下便知:

  1. 127.0.0.1:> object encoding fruits
  2. "hashtable"
  3. 127.0.0.1:>

看到了吧,是hashtable这个吊毛,现在闭上眼睛都能想到,肯定就是只用key的dictionary啦,对不对,如果你还有疑问的话,我还可以找到底层

代码给你看,好不啦???

有没有看到dictAdd方法,而其中的第三个参数正好是Null。。。对应着*val形参,你看牛叉不牛叉。。。然后我再带你看看dictAdd方法的定义。

好了,关于hashtable的实现理论,我在上一篇文章中也已经说过了,这里就不再赘叙了。

2. SPOP,SMEMBERS

既然元素进来了,总不能不出来吧,这里的第一个SPOP:移除并返回集合中的一个随机元素,有一点奇怪的是,这种奇怪的方法其实在我们

C#中的HashSet并没有好办法解决,就比如”这个随机“就有点烦人了,下面这是我能想到的方法。

刚才随便插了一句话,下面我们继续SAdd,再SPop出来。

  1. 127.0.0.1:> sadd fruits pear
  2. (integer)
  3. 127.0.0.1:> sadd fruits grape
  4. (integer)
  5. 127.0.0.1:> sadd fruits chestnut
  6. (integer)
  7. 127.0.0.1:> smembers fruits
  8. ) "grape"
  9. ) "pear"
  10. ) "banana"
  11. ) "apple"
  12. ) "chestnut"
  13. 127.0.0.1:> spop fruits
  14. "apple"
  15. 127.0.0.1:> spop fruits
  16. "chestnut"
  17. 127.0.0.1:> smembers fruits
  18. ) "grape"
  19. ) "pear"
  20. ) "banana"
  21. 127.0.0.1:>

这个方法确实还是蛮好的,起码它是原子性操作,如果要我自己实现的话,起码还是要10行左右代码的。

3. SREM

既然说到了CURD,那怎么能少了D呢,它的功能定义就是:移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略,

下面我随便举个例子,删除fruits中的pear。

  1. 127.0.0.1:> smembers fruits
  2. ) "grape"
  3. ) "pear"
  4. ) "banana"
  5. 127.0.0.1:> srem fruits pear
  6. (integer)
  7. 127.0.0.1:> smembers fruits
  8. ) "grape"
  9. ) "banana"
  10. 127.0.0.1:>

好了,常用的操作就那么几个,是不是觉得好傻瓜哦。。。傻瓜就对了,方法是简单的,关键你需要了解这个方法底层是如何实现的,这样才能做到

心里有数,就比如Set函数,它的源代码全部都在 “t.set.c” 中。

  1. /*
  2. * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * * Neither the name of Redis nor the names of its contributors may be used
  14. * to endorse or promote products derived from this software without
  15. * specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. * POSSIBILITY OF SUCH DAMAGE.
  28. */
  29.  
  30. #include "redis.h"
  31.  
  32. /*-----------------------------------------------------------------------------
  33. * Set Commands
  34. *----------------------------------------------------------------------------*/
  35.  
  36. void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op);
  37.  
  38. /* Factory method to return a set that *can* hold "value". When the object has
  39. * an integer-encodable value, an intset will be returned. Otherwise a regular
  40. * hash table. */
  41. robj *setTypeCreate(robj *value) {
  42. if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
  43. return createIntsetObject();
  44. return createSetObject();
  45. }
  46.  
  47. int setTypeAdd(robj *subject, robj *value) {
  48. long long llval;
  49. if (subject->encoding == REDIS_ENCODING_HT) {
  50. if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
  51. incrRefCount(value);
  52. return ;
  53. }
  54. } else if (subject->encoding == REDIS_ENCODING_INTSET) {
  55. if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
  56. uint8_t success = ;
  57. subject->ptr = intsetAdd(subject->ptr,llval,&success);
  58. if (success) {
  59. /* Convert to regular set when the intset contains
  60. * too many entries. */
  61. if (intsetLen(subject->ptr) > server.set_max_intset_entries)
  62. setTypeConvert(subject,REDIS_ENCODING_HT);
  63. return ;
  64. }
  65. } else {
  66. /* Failed to get integer from object, convert to regular set. */
  67. setTypeConvert(subject,REDIS_ENCODING_HT);
  68.  
  69. /* The set *was* an intset and this value is not integer
  70. * encodable, so dictAdd should always work. */
  71. redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK);
  72. incrRefCount(value);
  73. return ;
  74. }
  75. } else {
  76. redisPanic("Unknown set encoding");
  77. }
  78. return ;
  79. }
  80.  
  81. int setTypeRemove(robj *setobj, robj *value) {
  82. long long llval;
  83. if (setobj->encoding == REDIS_ENCODING_HT) {
  84. if (dictDelete(setobj->ptr,value) == DICT_OK) {
  85. if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);
  86. return ;
  87. }
  88. } else if (setobj->encoding == REDIS_ENCODING_INTSET) {
  89. if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
  90. int success;
  91. setobj->ptr = intsetRemove(setobj->ptr,llval,&success);
  92. if (success) return ;
  93. }
  94. } else {
  95. redisPanic("Unknown set encoding");
  96. }
  97. return ;
  98. }
  99.  
  100. int setTypeIsMember(robj *subject, robj *value) {
  101. long long llval;
  102. if (subject->encoding == REDIS_ENCODING_HT) {
  103. return dictFind((dict*)subject->ptr,value) != NULL;
  104. } else if (subject->encoding == REDIS_ENCODING_INTSET) {
  105. if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
  106. return intsetFind((intset*)subject->ptr,llval);
  107. }
  108. } else {
  109. redisPanic("Unknown set encoding");
  110. }
  111. return ;
  112. }
  113.  
  114. setTypeIterator *setTypeInitIterator(robj *subject) {
  115. setTypeIterator *si = zmalloc(sizeof(setTypeIterator));
  116. si->subject = subject;
  117. si->encoding = subject->encoding;
  118. if (si->encoding == REDIS_ENCODING_HT) {
  119. si->di = dictGetIterator(subject->ptr);
  120. } else if (si->encoding == REDIS_ENCODING_INTSET) {
  121. si->ii = ;
  122. } else {
  123. redisPanic("Unknown set encoding");
  124. }
  125. return si;
  126. }
  127.  
  128. void setTypeReleaseIterator(setTypeIterator *si) {
  129. if (si->encoding == REDIS_ENCODING_HT)
  130. dictReleaseIterator(si->di);
  131. zfree(si);
  132. }
  133.  
  134. /* Move to the next entry in the set. Returns the object at the current
  135. * position.
  136. *
  137. * Since set elements can be internally be stored as redis objects or
  138. * simple arrays of integers, setTypeNext returns the encoding of the
  139. * set object you are iterating, and will populate the appropriate pointer
  140. * (eobj) or (llobj) accordingly.
  141. *
  142. * When there are no longer elements -1 is returned.
  143. * Returned objects ref count is not incremented, so this function is
  144. * copy on write friendly. */
  145. int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {
  146. if (si->encoding == REDIS_ENCODING_HT) {
  147. dictEntry *de = dictNext(si->di);
  148. if (de == NULL) return -;
  149. *objele = dictGetKey(de);
  150. } else if (si->encoding == REDIS_ENCODING_INTSET) {
  151. if (!intsetGet(si->subject->ptr,si->ii++,llele))
  152. return -;
  153. }
  154. return si->encoding;
  155. }
  156.  
  157. /* The not copy on write friendly version but easy to use version
  158. * of setTypeNext() is setTypeNextObject(), returning new objects
  159. * or incrementing the ref count of returned objects. So if you don't
  160. * retain a pointer to this object you should call decrRefCount() against it.
  161. *
  162. * This function is the way to go for write operations where COW is not
  163. * an issue as the result will be anyway of incrementing the ref count. */
  164. robj *setTypeNextObject(setTypeIterator *si) {
  165. int64_t intele;
  166. robj *objele;
  167. int encoding;
  168.  
  169. encoding = setTypeNext(si,&objele,&intele);
  170. switch(encoding) {
  171. case -: return NULL;
  172. case REDIS_ENCODING_INTSET:
  173. return createStringObjectFromLongLong(intele);
  174. case REDIS_ENCODING_HT:
  175. incrRefCount(objele);
  176. return objele;
  177. default:
  178. redisPanic("Unsupported encoding");
  179. }
  180. return NULL; /* just to suppress warnings */
  181. }
  182.  
  183. /* Return random element from a non empty set.
  184. * The returned element can be a int64_t value if the set is encoded
  185. * as an "intset" blob of integers, or a redis object if the set
  186. * is a regular set.
  187. *
  188. * The caller provides both pointers to be populated with the right
  189. * object. The return value of the function is the object->encoding
  190. * field of the object and is used by the caller to check if the
  191. * int64_t pointer or the redis object pointer was populated.
  192. *
  193. * When an object is returned (the set was a real set) the ref count
  194. * of the object is not incremented so this function can be considered
  195. * copy on write friendly. */
  196. int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
  197. if (setobj->encoding == REDIS_ENCODING_HT) {
  198. dictEntry *de = dictGetRandomKey(setobj->ptr);
  199. *objele = dictGetKey(de);
  200. } else if (setobj->encoding == REDIS_ENCODING_INTSET) {
  201. *llele = intsetRandom(setobj->ptr);
  202. } else {
  203. redisPanic("Unknown set encoding");
  204. }
  205. return setobj->encoding;
  206. }
  207.  
  208. unsigned long setTypeSize(robj *subject) {
  209. if (subject->encoding == REDIS_ENCODING_HT) {
  210. return dictSize((dict*)subject->ptr);
  211. } else if (subject->encoding == REDIS_ENCODING_INTSET) {
  212. return intsetLen((intset*)subject->ptr);
  213. } else {
  214. redisPanic("Unknown set encoding");
  215. }
  216. }
  217.  
  218. /* Convert the set to specified encoding. The resulting dict (when converting
  219. * to a hash table) is presized to hold the number of elements in the original
  220. * set. */
  221. void setTypeConvert(robj *setobj, int enc) {
  222. setTypeIterator *si;
  223. redisAssertWithInfo(NULL,setobj,setobj->type == REDIS_SET &&
  224. setobj->encoding == REDIS_ENCODING_INTSET);
  225.  
  226. if (enc == REDIS_ENCODING_HT) {
  227. int64_t intele;
  228. dict *d = dictCreate(&setDictType,NULL);
  229. robj *element;
  230.  
  231. /* Presize the dict to avoid rehashing */
  232. dictExpand(d,intsetLen(setobj->ptr));
  233.  
  234. /* To add the elements we extract integers and create redis objects */
  235. si = setTypeInitIterator(setobj);
  236. while (setTypeNext(si,NULL,&intele) != -) {
  237. element = createStringObjectFromLongLong(intele);
  238. redisAssertWithInfo(NULL,element,dictAdd(d,element,NULL) == DICT_OK);
  239. }
  240. setTypeReleaseIterator(si);
  241.  
  242. setobj->encoding = REDIS_ENCODING_HT;
  243. zfree(setobj->ptr);
  244. setobj->ptr = d;
  245. } else {
  246. redisPanic("Unsupported set conversion");
  247. }
  248. }
  249.  
  250. void saddCommand(redisClient *c) {
  251. robj *set;
  252. int j, added = ;
  253.  
  254. set = lookupKeyWrite(c->db,c->argv[]);
  255. if (set == NULL) {
  256. set = setTypeCreate(c->argv[]);
  257. dbAdd(c->db,c->argv[],set);
  258. } else {
  259. if (set->type != REDIS_SET) {
  260. addReply(c,shared.wrongtypeerr);
  261. return;
  262. }
  263. }
  264.  
  265. for (j = ; j < c->argc; j++) {
  266. c->argv[j] = tryObjectEncoding(c->argv[j]);
  267. if (setTypeAdd(set,c->argv[j])) added++;
  268. }
  269. if (added) {
  270. signalModifiedKey(c->db,c->argv[]);
  271. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[],c->db->id);
  272. }
  273. server.dirty += added;
  274. addReplyLongLong(c,added);
  275. }
  276.  
  277. void sremCommand(redisClient *c) {
  278. robj *set;
  279. int j, deleted = , keyremoved = ;
  280.  
  281. if ((set = lookupKeyWriteOrReply(c,c->argv[],shared.czero)) == NULL ||
  282. checkType(c,set,REDIS_SET)) return;
  283.  
  284. for (j = ; j < c->argc; j++) {
  285. if (setTypeRemove(set,c->argv[j])) {
  286. deleted++;
  287. if (setTypeSize(set) == ) {
  288. dbDelete(c->db,c->argv[]);
  289. keyremoved = ;
  290. break;
  291. }
  292. }
  293. }
  294. if (deleted) {
  295. signalModifiedKey(c->db,c->argv[]);
  296. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[],c->db->id);
  297. if (keyremoved)
  298. notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[],
  299. c->db->id);
  300. server.dirty += deleted;
  301. }
  302. addReplyLongLong(c,deleted);
  303. }
  304.  
  305. void smoveCommand(redisClient *c) {
  306. robj *srcset, *dstset, *ele;
  307. srcset = lookupKeyWrite(c->db,c->argv[]);
  308. dstset = lookupKeyWrite(c->db,c->argv[]);
  309. ele = c->argv[] = tryObjectEncoding(c->argv[]);
  310.  
  311. /* If the source key does not exist return 0 */
  312. if (srcset == NULL) {
  313. addReply(c,shared.czero);
  314. return;
  315. }
  316.  
  317. /* If the source key has the wrong type, or the destination key
  318. * is set and has the wrong type, return with an error. */
  319. if (checkType(c,srcset,REDIS_SET) ||
  320. (dstset && checkType(c,dstset,REDIS_SET))) return;
  321.  
  322. /* If srcset and dstset are equal, SMOVE is a no-op */
  323. if (srcset == dstset) {
  324. addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero);
  325. return;
  326. }
  327.  
  328. /* If the element cannot be removed from the src set, return 0. */
  329. if (!setTypeRemove(srcset,ele)) {
  330. addReply(c,shared.czero);
  331. return;
  332. }
  333. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[],c->db->id);
  334.  
  335. /* Remove the src set from the database when empty */
  336. if (setTypeSize(srcset) == ) {
  337. dbDelete(c->db,c->argv[]);
  338. notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[],c->db->id);
  339. }
  340. signalModifiedKey(c->db,c->argv[]);
  341. signalModifiedKey(c->db,c->argv[]);
  342. server.dirty++;
  343.  
  344. /* Create the destination set when it doesn't exist */
  345. if (!dstset) {
  346. dstset = setTypeCreate(ele);
  347. dbAdd(c->db,c->argv[],dstset);
  348. }
  349.  
  350. /* An extra key has changed when ele was successfully added to dstset */
  351. if (setTypeAdd(dstset,ele)) {
  352. server.dirty++;
  353. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[],c->db->id);
  354. }
  355. addReply(c,shared.cone);
  356. }
  357.  
  358. void sismemberCommand(redisClient *c) {
  359. robj *set;
  360.  
  361. if ((set = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
  362. checkType(c,set,REDIS_SET)) return;
  363.  
  364. c->argv[] = tryObjectEncoding(c->argv[]);
  365. if (setTypeIsMember(set,c->argv[]))
  366. addReply(c,shared.cone);
  367. else
  368. addReply(c,shared.czero);
  369. }
  370.  
  371. void scardCommand(redisClient *c) {
  372. robj *o;
  373.  
  374. if ((o = lookupKeyReadOrReply(c,c->argv[],shared.czero)) == NULL ||
  375. checkType(c,o,REDIS_SET)) return;
  376.  
  377. addReplyLongLong(c,setTypeSize(o));
  378. }
  379.  
  380. void spopCommand(redisClient *c) {
  381. robj *set, *ele, *aux;
  382. int64_t llele;
  383. int encoding;
  384.  
  385. if ((set = lookupKeyWriteOrReply(c,c->argv[],shared.nullbulk)) == NULL ||
  386. checkType(c,set,REDIS_SET)) return;
  387.  
  388. encoding = setTypeRandomElement(set,&ele,&llele);
  389. if (encoding == REDIS_ENCODING_INTSET) {
  390. ele = createStringObjectFromLongLong(llele);
  391. set->ptr = intsetRemove(set->ptr,llele,NULL);
  392. } else {
  393. incrRefCount(ele);
  394. setTypeRemove(set,ele);
  395. }
  396. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[],c->db->id);
  397.  
  398. /* Replicate/AOF this command as an SREM operation */
  399. aux = createStringObject("SREM",);
  400. rewriteClientCommandVector(c,,aux,c->argv[],ele);
  401. decrRefCount(ele);
  402. decrRefCount(aux);
  403.  
  404. addReplyBulk(c,ele);
  405. if (setTypeSize(set) == ) {
  406. dbDelete(c->db,c->argv[]);
  407. notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[],c->db->id);
  408. }
  409. signalModifiedKey(c->db,c->argv[]);
  410. server.dirty++;
  411. }
  412.  
  413. /* handle the "SRANDMEMBER key <count>" variant. The normal version of the
  414. * command is handled by the srandmemberCommand() function itself. */
  415.  
  416. /* How many times bigger should be the set compared to the requested size
  417. * for us to don't use the "remove elements" strategy? Read later in the
  418. * implementation for more info. */
  419. #define SRANDMEMBER_SUB_STRATEGY_MUL 3
  420.  
  421. void srandmemberWithCountCommand(redisClient *c) {
  422. long l;
  423. unsigned long count, size;
  424. int uniq = ;
  425. robj *set, *ele;
  426. int64_t llele;
  427. int encoding;
  428.  
  429. dict *d;
  430.  
  431. if (getLongFromObjectOrReply(c,c->argv[],&l,NULL) != REDIS_OK) return;
  432. if (l >= ) {
  433. count = (unsigned) l;
  434. } else {
  435. /* A negative count means: return the same elements multiple times
  436. * (i.e. don't remove the extracted element after every extraction). */
  437. count = -l;
  438. uniq = ;
  439. }
  440.  
  441. if ((set = lookupKeyReadOrReply(c,c->argv[],shared.emptymultibulk))
  442. == NULL || checkType(c,set,REDIS_SET)) return;
  443. size = setTypeSize(set);
  444.  
  445. /* If count is zero, serve it ASAP to avoid special cases later. */
  446. if (count == ) {
  447. addReply(c,shared.emptymultibulk);
  448. return;
  449. }
  450.  
  451. /* CASE 1: The count was negative, so the extraction method is just:
  452. * "return N random elements" sampling the whole set every time.
  453. * This case is trivial and can be served without auxiliary data
  454. * structures. */
  455. if (!uniq) {
  456. addReplyMultiBulkLen(c,count);
  457. while(count--) {
  458. encoding = setTypeRandomElement(set,&ele,&llele);
  459. if (encoding == REDIS_ENCODING_INTSET) {
  460. addReplyBulkLongLong(c,llele);
  461. } else {
  462. addReplyBulk(c,ele);
  463. }
  464. }
  465. return;
  466. }
  467.  
  468. /* CASE 2:
  469. * The number of requested elements is greater than the number of
  470. * elements inside the set: simply return the whole set. */
  471. if (count >= size) {
  472. sunionDiffGenericCommand(c,c->argv+,,NULL,REDIS_OP_UNION);
  473. return;
  474. }
  475.  
  476. /* For CASE 3 and CASE 4 we need an auxiliary dictionary. */
  477. d = dictCreate(&setDictType,NULL);
  478.  
  479. /* CASE 3:
  480. * The number of elements inside the set is not greater than
  481. * SRANDMEMBER_SUB_STRATEGY_MUL times the number of requested elements.
  482. * In this case we create a set from scratch with all the elements, and
  483. * subtract random elements to reach the requested number of elements.
  484. *
  485. * This is done because if the number of requsted elements is just
  486. * a bit less than the number of elements in the set, the natural approach
  487. * used into CASE 3 is highly inefficient. */
  488. if (count*SRANDMEMBER_SUB_STRATEGY_MUL > size) {
  489. setTypeIterator *si;
  490.  
  491. /* Add all the elements into the temporary dictionary. */
  492. si = setTypeInitIterator(set);
  493. while((encoding = setTypeNext(si,&ele,&llele)) != -) {
  494. int retval = DICT_ERR;
  495.  
  496. if (encoding == REDIS_ENCODING_INTSET) {
  497. retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);
  498. } else {
  499. retval = dictAdd(d,dupStringObject(ele),NULL);
  500. }
  501. redisAssert(retval == DICT_OK);
  502. }
  503. setTypeReleaseIterator(si);
  504. redisAssert(dictSize(d) == size);
  505.  
  506. /* Remove random elements to reach the right count. */
  507. while(size > count) {
  508. dictEntry *de;
  509.  
  510. de = dictGetRandomKey(d);
  511. dictDelete(d,dictGetKey(de));
  512. size--;
  513. }
  514. }
  515.  
  516. /* CASE 4: We have a big set compared to the requested number of elements.
  517. * In this case we can simply get random elements from the set and add
  518. * to the temporary set, trying to eventually get enough unique elements
  519. * to reach the specified count. */
  520. else {
  521. unsigned long added = ;
  522.  
  523. while(added < count) {
  524. encoding = setTypeRandomElement(set,&ele,&llele);
  525. if (encoding == REDIS_ENCODING_INTSET) {
  526. ele = createStringObjectFromLongLong(llele);
  527. } else {
  528. ele = dupStringObject(ele);
  529. }
  530. /* Try to add the object to the dictionary. If it already exists
  531. * free it, otherwise increment the number of objects we have
  532. * in the result dictionary. */
  533. if (dictAdd(d,ele,NULL) == DICT_OK)
  534. added++;
  535. else
  536. decrRefCount(ele);
  537. }
  538. }
  539.  
  540. /* CASE 3 & 4: send the result to the user. */
  541. {
  542. dictIterator *di;
  543. dictEntry *de;
  544.  
  545. addReplyMultiBulkLen(c,count);
  546. di = dictGetIterator(d);
  547. while((de = dictNext(di)) != NULL)
  548. addReplyBulk(c,dictGetKey(de));
  549. dictReleaseIterator(di);
  550. dictRelease(d);
  551. }
  552. }
  553.  
  554. void srandmemberCommand(redisClient *c) {
  555. robj *set, *ele;
  556. int64_t llele;
  557. int encoding;
  558.  
  559. if (c->argc == ) {
  560. srandmemberWithCountCommand(c);
  561. return;
  562. } else if (c->argc > ) {
  563. addReply(c,shared.syntaxerr);
  564. return;
  565. }
  566.  
  567. if ((set = lookupKeyReadOrReply(c,c->argv[],shared.nullbulk)) == NULL ||
  568. checkType(c,set,REDIS_SET)) return;
  569.  
  570. encoding = setTypeRandomElement(set,&ele,&llele);
  571. if (encoding == REDIS_ENCODING_INTSET) {
  572. addReplyBulkLongLong(c,llele);
  573. } else {
  574. addReplyBulk(c,ele);
  575. }
  576. }
  577.  
  578. int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
  579. return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);
  580. }
  581.  
  582. /* This is used by SDIFF and in this case we can receive NULL that should
  583. * be handled as empty sets. */
  584. int qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {
  585. robj *o1 = *(robj**)s1, *o2 = *(robj**)s2;
  586.  
  587. return (o2 ? setTypeSize(o2) : ) - (o1 ? setTypeSize(o1) : );
  588. }
  589.  
  590. void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
  591. robj **sets = zmalloc(sizeof(robj*)*setnum);
  592. setTypeIterator *si;
  593. robj *eleobj, *dstset = NULL;
  594. int64_t intobj;
  595. void *replylen = NULL;
  596. unsigned long j, cardinality = ;
  597. int encoding;
  598.  
  599. for (j = ; j < setnum; j++) {
  600. robj *setobj = dstkey ?
  601. lookupKeyWrite(c->db,setkeys[j]) :
  602. lookupKeyRead(c->db,setkeys[j]);
  603. if (!setobj) {
  604. zfree(sets);
  605. if (dstkey) {
  606. if (dbDelete(c->db,dstkey)) {
  607. signalModifiedKey(c->db,dstkey);
  608. server.dirty++;
  609. }
  610. addReply(c,shared.czero);
  611. } else {
  612. addReply(c,shared.emptymultibulk);
  613. }
  614. return;
  615. }
  616. if (checkType(c,setobj,REDIS_SET)) {
  617. zfree(sets);
  618. return;
  619. }
  620. sets[j] = setobj;
  621. }
  622. /* Sort sets from the smallest to largest, this will improve our
  623. * algorithm's performance */
  624. qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
  625.  
  626. /* The first thing we should output is the total number of elements...
  627. * since this is a multi-bulk write, but at this stage we don't know
  628. * the intersection set size, so we use a trick, append an empty object
  629. * to the output list and save the pointer to later modify it with the
  630. * right length */
  631. if (!dstkey) {
  632. replylen = addDeferredMultiBulkLength(c);
  633. } else {
  634. /* If we have a target key where to store the resulting set
  635. * create this key with an empty set inside */
  636. dstset = createIntsetObject();
  637. }
  638.  
  639. /* Iterate all the elements of the first (smallest) set, and test
  640. * the element against all the other sets, if at least one set does
  641. * not include the element it is discarded */
  642. si = setTypeInitIterator(sets[]);
  643. while((encoding = setTypeNext(si,&eleobj,&intobj)) != -) {
  644. for (j = ; j < setnum; j++) {
  645. if (sets[j] == sets[]) continue;
  646. if (encoding == REDIS_ENCODING_INTSET) {
  647. /* intset with intset is simple... and fast */
  648. if (sets[j]->encoding == REDIS_ENCODING_INTSET &&
  649. !intsetFind((intset*)sets[j]->ptr,intobj))
  650. {
  651. break;
  652. /* in order to compare an integer with an object we
  653. * have to use the generic function, creating an object
  654. * for this */
  655. } else if (sets[j]->encoding == REDIS_ENCODING_HT) {
  656. eleobj = createStringObjectFromLongLong(intobj);
  657. if (!setTypeIsMember(sets[j],eleobj)) {
  658. decrRefCount(eleobj);
  659. break;
  660. }
  661. decrRefCount(eleobj);
  662. }
  663. } else if (encoding == REDIS_ENCODING_HT) {
  664. /* Optimization... if the source object is integer
  665. * encoded AND the target set is an intset, we can get
  666. * a much faster path. */
  667. if (eleobj->encoding == REDIS_ENCODING_INT &&
  668. sets[j]->encoding == REDIS_ENCODING_INTSET &&
  669. !intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr))
  670. {
  671. break;
  672. /* else... object to object check is easy as we use the
  673. * type agnostic API here. */
  674. } else if (!setTypeIsMember(sets[j],eleobj)) {
  675. break;
  676. }
  677. }
  678. }
  679.  
  680. /* Only take action when all sets contain the member */
  681. if (j == setnum) {
  682. if (!dstkey) {
  683. if (encoding == REDIS_ENCODING_HT)
  684. addReplyBulk(c,eleobj);
  685. else
  686. addReplyBulkLongLong(c,intobj);
  687. cardinality++;
  688. } else {
  689. if (encoding == REDIS_ENCODING_INTSET) {
  690. eleobj = createStringObjectFromLongLong(intobj);
  691. setTypeAdd(dstset,eleobj);
  692. decrRefCount(eleobj);
  693. } else {
  694. setTypeAdd(dstset,eleobj);
  695. }
  696. }
  697. }
  698. }
  699. setTypeReleaseIterator(si);
  700.  
  701. if (dstkey) {
  702. /* Store the resulting set into the target, if the intersection
  703. * is not an empty set. */
  704. int deleted = dbDelete(c->db,dstkey);
  705. if (setTypeSize(dstset) > ) {
  706. dbAdd(c->db,dstkey,dstset);
  707. addReplyLongLong(c,setTypeSize(dstset));
  708. notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sinterstore",
  709. dstkey,c->db->id);
  710. } else {
  711. decrRefCount(dstset);
  712. addReply(c,shared.czero);
  713. if (deleted)
  714. notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
  715. dstkey,c->db->id);
  716. }
  717. signalModifiedKey(c->db,dstkey);
  718. server.dirty++;
  719. } else {
  720. setDeferredMultiBulkLength(c,replylen,cardinality);
  721. }
  722. zfree(sets);
  723. }
  724.  
  725. void sinterCommand(redisClient *c) {
  726. sinterGenericCommand(c,c->argv+,c->argc-,NULL);
  727. }
  728.  
  729. void sinterstoreCommand(redisClient *c) {
  730. sinterGenericCommand(c,c->argv+,c->argc-,c->argv[]);
  731. }
  732.  
  733. #define REDIS_OP_UNION 0
  734. #define REDIS_OP_DIFF 1
  735. #define REDIS_OP_INTER 2
  736.  
  737. void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) {
  738. robj **sets = zmalloc(sizeof(robj*)*setnum);
  739. setTypeIterator *si;
  740. robj *ele, *dstset = NULL;
  741. int j, cardinality = ;
  742. int diff_algo = ;
  743.  
  744. for (j = ; j < setnum; j++) {
  745. robj *setobj = dstkey ?
  746. lookupKeyWrite(c->db,setkeys[j]) :
  747. lookupKeyRead(c->db,setkeys[j]);
  748. if (!setobj) {
  749. sets[j] = NULL;
  750. continue;
  751. }
  752. if (checkType(c,setobj,REDIS_SET)) {
  753. zfree(sets);
  754. return;
  755. }
  756. sets[j] = setobj;
  757. }
  758.  
  759. /* Select what DIFF algorithm to use.
  760. *
  761. * Algorithm 1 is O(N*M) where N is the size of the element first set
  762. * and M the total number of sets.
  763. *
  764. * Algorithm 2 is O(N) where N is the total number of elements in all
  765. * the sets.
  766. *
  767. * We compute what is the best bet with the current input here. */
  768. if (op == REDIS_OP_DIFF && sets[]) {
  769. long long algo_one_work = , algo_two_work = ;
  770.  
  771. for (j = ; j < setnum; j++) {
  772. if (sets[j] == NULL) continue;
  773.  
  774. algo_one_work += setTypeSize(sets[]);
  775. algo_two_work += setTypeSize(sets[j]);
  776. }
  777.  
  778. /* Algorithm 1 has better constant times and performs less operations
  779. * if there are elements in common. Give it some advantage. */
  780. algo_one_work /= ;
  781. diff_algo = (algo_one_work <= algo_two_work) ? : ;
  782.  
  783. if (diff_algo == && setnum > ) {
  784. /* With algorithm 1 it is better to order the sets to subtract
  785. * by decreasing size, so that we are more likely to find
  786. * duplicated elements ASAP. */
  787. qsort(sets+,setnum-,sizeof(robj*),
  788. qsortCompareSetsByRevCardinality);
  789. }
  790. }
  791.  
  792. /* We need a temp set object to store our union. If the dstkey
  793. * is not NULL (that is, we are inside an SUNIONSTORE operation) then
  794. * this set object will be the resulting object to set into the target key*/
  795. dstset = createIntsetObject();
  796.  
  797. if (op == REDIS_OP_UNION) {
  798. /* Union is trivial, just add every element of every set to the
  799. * temporary set. */
  800. for (j = ; j < setnum; j++) {
  801. if (!sets[j]) continue; /* non existing keys are like empty sets */
  802.  
  803. si = setTypeInitIterator(sets[j]);
  804. while((ele = setTypeNextObject(si)) != NULL) {
  805. if (setTypeAdd(dstset,ele)) cardinality++;
  806. decrRefCount(ele);
  807. }
  808. setTypeReleaseIterator(si);
  809. }
  810. } else if (op == REDIS_OP_DIFF && sets[] && diff_algo == ) {
  811. /* DIFF Algorithm 1:
  812. *
  813. * We perform the diff by iterating all the elements of the first set,
  814. * and only adding it to the target set if the element does not exist
  815. * into all the other sets.
  816. *
  817. * This way we perform at max N*M operations, where N is the size of
  818. * the first set, and M the number of sets. */
  819. si = setTypeInitIterator(sets[]);
  820. while((ele = setTypeNextObject(si)) != NULL) {
  821. for (j = ; j < setnum; j++) {
  822. if (!sets[j]) continue; /* no key is an empty set. */
  823. if (sets[j] == sets[]) break; /* same set! */
  824. if (setTypeIsMember(sets[j],ele)) break;
  825. }
  826. if (j == setnum) {
  827. /* There is no other set with this element. Add it. */
  828. setTypeAdd(dstset,ele);
  829. cardinality++;
  830. }
  831. decrRefCount(ele);
  832. }
  833. setTypeReleaseIterator(si);
  834. } else if (op == REDIS_OP_DIFF && sets[] && diff_algo == ) {
  835. /* DIFF Algorithm 2:
  836. *
  837. * Add all the elements of the first set to the auxiliary set.
  838. * Then remove all the elements of all the next sets from it.
  839. *
  840. * This is O(N) where N is the sum of all the elements in every
  841. * set. */
  842. for (j = ; j < setnum; j++) {
  843. if (!sets[j]) continue; /* non existing keys are like empty sets */
  844.  
  845. si = setTypeInitIterator(sets[j]);
  846. while((ele = setTypeNextObject(si)) != NULL) {
  847. if (j == ) {
  848. if (setTypeAdd(dstset,ele)) cardinality++;
  849. } else {
  850. if (setTypeRemove(dstset,ele)) cardinality--;
  851. }
  852. decrRefCount(ele);
  853. }
  854. setTypeReleaseIterator(si);
  855.  
  856. /* Exit if result set is empty as any additional removal
  857. * of elements will have no effect. */
  858. if (cardinality == ) break;
  859. }
  860. }
  861.  
  862. /* Output the content of the resulting set, if not in STORE mode */
  863. if (!dstkey) {
  864. addReplyMultiBulkLen(c,cardinality);
  865. si = setTypeInitIterator(dstset);
  866. while((ele = setTypeNextObject(si)) != NULL) {
  867. addReplyBulk(c,ele);
  868. decrRefCount(ele);
  869. }
  870. setTypeReleaseIterator(si);
  871. decrRefCount(dstset);
  872. } else {
  873. /* If we have a target key where to store the resulting set
  874. * create this key with the result set inside */
  875. int deleted = dbDelete(c->db,dstkey);
  876. if (setTypeSize(dstset) > ) {
  877. dbAdd(c->db,dstkey,dstset);
  878. addReplyLongLong(c,setTypeSize(dstset));
  879. notifyKeyspaceEvent(REDIS_NOTIFY_SET,
  880. op == REDIS_OP_UNION ? "sunionstore" : "sdiffstore",
  881. dstkey,c->db->id);
  882. } else {
  883. decrRefCount(dstset);
  884. addReply(c,shared.czero);
  885. if (deleted)
  886. notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",
  887. dstkey,c->db->id);
  888. }
  889. signalModifiedKey(c->db,dstkey);
  890. server.dirty++;
  891. }
  892. zfree(sets);
  893. }
  894.  
  895. void sunionCommand(redisClient *c) {
  896. sunionDiffGenericCommand(c,c->argv+,c->argc-,NULL,REDIS_OP_UNION);
  897. }
  898.  
  899. void sunionstoreCommand(redisClient *c) {
  900. sunionDiffGenericCommand(c,c->argv+,c->argc-,c->argv[],REDIS_OP_UNION);
  901. }
  902.  
  903. void sdiffCommand(redisClient *c) {
  904. sunionDiffGenericCommand(c,c->argv+,c->argc-,NULL,REDIS_OP_DIFF);
  905. }
  906.  
  907. void sdiffstoreCommand(redisClient *c) {
  908. sunionDiffGenericCommand(c,c->argv+,c->argc-,c->argv[],REDIS_OP_DIFF);
  909. }
  910.  
  911. void sscanCommand(redisClient *c) {
  912. robj *set;
  913. unsigned long cursor;
  914.  
  915. if (parseScanCursorOrReply(c,c->argv[],&cursor) == REDIS_ERR) return;
  916. if ((set = lookupKeyReadOrReply(c,c->argv[],shared.emptyscan)) == NULL ||
  917. checkType(c,set,REDIS_SET)) return;
  918. scanGenericCommand(c,set,cursor);
  919. }

15天玩转redis —— 第五篇 集合对象类型的更多相关文章

  1. 15天玩转redis —— 第四篇 哈希对象类型

    redis中的hash也是我们使用中的高频数据结构,它的构造基本上和编程语言中的HashTable,Dictionary大同小异,如果大家往后有什么逻辑需要用 Dictionary存放的话,可以根据场 ...

  2. 15天玩转redis —— 第十一篇 让你彻底了解RDB存储结构

    接着上一篇说,这里我们来继续分析一下RDB文件存储结构,首先大家都知道RDB文件是在redis的“快照”的模式下才会产生,那么如果 我们理解了RDB文件的结构,是不是让我们对“快照”模式能做到一个心中 ...

  3. 15天玩转redis —— 第十篇 对快照模式的深入分析

    我们知道redis是带有持久化这个能力了,那到底持久化成到哪里,持久化成啥样呢???这篇我们一起来寻求答案. 一:快照模式 或许在用Redis之初的时候,就听说过redis有两种持久化模式,第一种是S ...

  4. 15天玩转redis —— 第八篇 你不得不会的事务玩法

    我们都知道redis追求的是简单,快速,高效,在这种情况下也就拒绝了支持window平台,学sqlserver的时候,我们知道事务还算是个比较复杂的东西, 所以这吊毛要是照搬到redis中去,理所当然 ...

  5. 15天玩转redis —— 第六篇 有序集合类型

    今天我们说一下Redis中最后一个数据类型 “有序集合类型”,回首之前学过的几个数据结构,不知道你会不会由衷感叹,开源的世界真好,写这 些代码的好心人真的要一生平安哈,不管我们想没想的到的东西,在这个 ...

  6. 15天玩转redis —— 第三篇 无敌的列表类型

     据说60%的人使用redis看重的是redis中的list类型,那这个list有什么用呢???不用我说大家都明白,做队列使用呗,为什么用它呢,很简单呗, 因为有了它我就不需要专门的MQ产品啦,比如说 ...

  7. 15天玩转redis —— 第七篇 同事的一次缓存操作引起对慢查询的认识

    上个星期同事做一个业务模块,需要将一个80M的数据存入到redis缓存中,想法总是好的,真操作的时候遇到了HSet超时,我们使用的是C#的 StackExchange.Redis驱动. <red ...

  8. 从Redis中删除大集合对象的方法

    Redis中的大集合对象,如set.zset等,如果有上千万个元素,一般是不能直接用del命令来删除的,因为del命令可能会耗时几秒钟,而redis本身是单线程的,在高并发的情况下会阻塞大量的请求,严 ...

  9. 15天玩转redis —— 第二篇 基础的字符串类型

    我们都知道redis是采用C语言开发,那么在C语言中表示string都是采用char[]数组的,然后你可能会想,那还不简单,当我执行如下命令,肯定是直 接塞给char[]数组的. 如果你真的这么想的话 ...

随机推荐

  1. js模版引擎handlebars.js实用教程——each-基本循环使用方法

    返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...

  2. Log4Net指南

    英文好的直接看这里:http://www.codeproject.com/Articles/140911/log4net-Tutorial 介绍 log4net.是.NET下面最伟大的日志工具之一.简 ...

  3. Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明

    一.基础说明 这里说的“通用接口(CommonAPIs)”是使用微信公众账号一系列高级功能的必备验证功能(应用于开发模式). 我们通过微信后台唯一的凭证,向通用接口发出请求,得到访问令牌(Access ...

  4. loglikelihood ratio 相似度

    摘要: 在机器学习中常用到各种距离或者相似度,今天在看美团推荐系统重排序的文章时看到了loglikelihood ratio 相似度,特总结起来.以后有时间再把常用的相似度或者距离梳理到一篇文章. 背 ...

  5. iOS-应用上架

    其实挺讨厌苹果这一套上架机制,审核周期长,而且网速又慢,但又没办法,只能上架到AppStore,无奈. 上架前需要确保应用图标齐全,并在Info.plist中的Icon files添加了png图片,不 ...

  6. javascript_basic_01之概述

    1.javascript组成: ①核心ECMAScript:②文档对象模型DOM(Document Object Model):③浏览器对象模型BOM(Browser Object Model): 2 ...

  7. Liferay7 BPM门户开发之43: Gradle依赖管理

    进入liferay v7.0,官方推荐使用Gradle进行依赖管理和发布,所以必须知道Gradle的用法,网上资料很多,不赘述 只写依赖管理的分类 一般用外部仓库依赖,也可以用本地文件依赖(依赖本地j ...

  8. js高程读书笔记(1-3章)

    一.js简介 js是一种专为与网页交互而设计的脚本语言,由以下三个不同的部分组成: 1.ECMAScript,由ECMA-262(它规定了语言的这些组成部分:语法,类型,语句,关键字,保留字,操作符, ...

  9. ELCImagePickerController 的集成

    1,  将文件夹ELCImagePicker拷贝到工程中 2,添加系统库AssetsLibrary.framework 3,  添加系统库MobileCoreServices 4,在头文件中加入引用# ...

  10. EntityFramework系列:MySql的RowVersion

    无需修改实体和配置,在MySql中使用和SqlServer一致的并发控制.修改RowVersion类型不可取,修改为Timestamp更不可行.Sql Server的RowVersion生成一串唯一的 ...