Skip to content

Conversation

@pingfan108
Copy link

  1. 基于Redis5.0.10移植了codis-server的相关改动,使得codis可以支持Redis5.0;
  2. 基于Redis本身的测试框架为codis-server相关改动增加了相应的测试用例。
…lots

主要参照codis源码里的extern/redis-3.2.11/patch/codis/0000-redis.patch,
同时做了如下修改:
1. 为适配lazyfree的改动, 在dbAsyncDelete()中补充了hash_slots和tagged_keys的
   删key逻辑;
2. 为适配flushdb()与flushall()的统一, hash_slots和tagged_keys的清理逻辑统一
   在emptyDb()内实现;
3. 为适配zset的元素类型由robj*改为SDS, 调zskiplist相关接口时做相应类型转换;
4. 为适配dictScan()/dbLoadObject()/setExpire()/zslDelete()等函数的签名变化,
   调用时增加相应的新参数;
5. 因.o文件的依赖关系已经可以自动推导生成, 不再修改Makefile.dep;
6. 调整slots.c和crc32.c两个新文件的编码风格, 以便和redis的原生代码风格一致;
   在slots.c中增加了一些注释并调整了部分函数的顺序以提升代码的可读性.
* db->hash_slots[]是CodisServer用来维护hash slot与key的子集映射关系的一个
  dict数组, 为了减少内存占用, 它和db->dict复用了key的name sds.
* 自4.0版本开始, redis新增了主动整理内存碎片的功能, 范围包括key的name sds,
  value object, 以及db dict entry. 在整理过程中如果对某个key的name sds做了
  内存重新分配,那么复用这个name sds的db->expires和db->hash_slots[i]中相应
  的指针就会失效,需要一并做修改.
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0002-rehash.patch,
* 背景: incrementallyRehash()本身是一个渐进式的设计实现, 预期对其的每一次
  调用都是轻量的. 但codis之前的patch中将新增结构db->hash_slots的rehash也
  放入了其中, 每次调用都需要对1024个slot逐个调用dictRehashMilliseconds(),
  这样的操作过重, 容易阻塞对外服务.
* 优化方式为, 在遍历1024个slot的过程中增加耗时的检查, 当耗时超过1ms后随时
  break, 以此控制整个调用的耗时量级; 同时, 为避免slot之间的rehash进度严重
  不均衡, 每次调用时基于时间戳动态生成slot遍历的起始索引.
此patch是将codis3.2中原本的0000-basic.patch和0002-rehash.patch针对redis5.0
做了相应适配性修改之后合并生成。
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0001-slotsscan.patch,
同时做了如下修改:
1. 为适配dictScan()函数的签名变化, 调用时增加相应的新参数;
2. 为新命令SLOTSSCAN增加相应的UT覆盖;
3. 将高频出现的的"wrong number of arguments"错误信息抽离为公共的宏.
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0003-slots_async1.patch,
同时做了如下修改:
1. 适配zset的元素类型由robj*改为SDS,
   * 在slotsrestoreAsyncHandle()的实现中, 删除zset结构val对象中冲突的元素时
     原patch中是先zsl后dict, 原因是3.2版本中元素对象为robj, 通过引用计数来
     控制元素对象的释放, 不用考虑zsl和dict删除的先后顺序; 但在5.0版本中元素
     对象为SDS, 通过将dict的keyDestructor置为NULL来将元素对象释放的责任完全
     交付给zsl, 所以此处需改为先dict后zsl的顺序以避免对无效指针的引用;
   * 在slotsrestoreAsyncHandle()的实现中, 不再用tryObjectEncoding()对elem做
     编码优化; 同时, 调用zslInsert()和dictAdd()前需用sdsdup()做字符串拷贝;
   * 在lazyReleaseIteratorNext()/singleObjectIteratorNext()的实现中遍历zset
     结构val对象或chunked编码时, 以及在batchedObjectIteratorAddKey()的实现
     中遍历tagged_keys时, 原patch中均直接操作node->obj, 而在5.0版本中需先将
     node->ele转换为robj再来操作;
2. 适配set/hash的元素类型由robj*改为SDS,
   * 在batchedObjectIterator的实现中, 迁移的key/val均采用robj类型表示,并用
     字典keys做key的去重; 因3.2版中set结构的元素类型也为robj, 原patch中直接
     复用了setDictType作为keys字典的类型; 但在5.0版中set结构元素对象为SDS,
     setDictType中绑定的相关函数也改为针对SDS实现, keys字典的类型需改为专门
     适配robj的objectKeyPointerValueDictType类型;
   * 在dictScan()的callback函数中, 将遍历到的元素塞进list的时候, 原patch中
     直接将dictGetKey()/dictGetVal()获取的结果AddTail, 而在5.0版本中需先将
     结果转换为robj再添加到list; 相应的, callback函数所填充list中节点的val
     在使用完后, 也需要手动调用decrRefCount()来主动释放;
   * 在slotsrestoreAsyncHandle()的实现中, 不再用hashTypeTryObjectEncoding()
     对field和value做编码优化; 同时, 调用hashTypeSet()时需做相应类型转换;
   * 在slotsrestoreAsyncHandle()的实现中, 不再用tryObjectEncoding()对elem做
     编码优化; 同时, 调用setTypeAdd()时需做相应类型转换.
3. 适配dictScan()/dbLoadObject()/setExpire()等函数的签名变化, 调用时需增加
   相应的新参数.
4. 在slotsrestoreAsyncHandle()的实现中, 对zset内dict的删除操作还有一个优化:
   原patch中是dictFind() => dictDelete(), 但dictDelete()内部会对待删除元素
   重复做一次查找, 本次将其优化为dictUnlink() => dictFreeUnlinkedEntry().
5. Makefile中, 当MALLOC为jemalloc时, 原patch特别地在FINAL_LIBS后附加了-lrt;
   而5.0版本在linux环境下编译时FINAL_LIBS本身已经附加了-lrt, 无需特殊处理.
6. 对于新文件slots_async.c, 调整其编码风格以便redis的原生代码保持一致.
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0004-slots_async2.patch,
本次移植的主要是异步迁移的状态统计与查询相关功能.
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0005-slots_async3_lazyfree.patch,
1. 背景介绍
   * 之前迁移过程中产生的所有chunked objects的释放虽然也是lazyfree的, 但是
     释放object的调用仍然放在redis的主线程里面, 由时间事件周期触发; 这一定
     程度上还是会使主线程负担过重, 影响正常业务请求的延迟.
2. 改动说明
   * 这个patch中创建了单独的lazyfree线程, 迁移中产生的所有chunked objects
     都交由这个lazyfree线程逐步的释放, 减小了主线程的负担;
   * 和原始patch不同的一点在于, 原patch基于redis3.2修改, 为了实现lazyfree
     的特性移植了redis4.0中shared object部分的代码; 而现在是基于redis5.0做
     修改, 已有lazyfree这部分代码, 无需再做此移植;
   * 另外, codis源码里的0007-slots_async5_cleanup.patch也和此改动直接相关,
     将改动一并做了merge.
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0006-slots_async4_bugfix_1184.patch,
1. 背景介绍
   * 当前迁移slot时, 会在scan hash_slot的过程中直接将扫到key通过回调函数加
     将入待迁移列表; 这个过程中会调用lookupKeyWrite()获取每个key的元数据来
     估算当前批次的迁移代价, lookup又会调用expireIfNeeded()来触发已过期key
     的删除; 这个删除op发生在dictScan()迭代的过程中是非常危险的, 容易触发
     server crash. 详参, CodisLabs#1184
2. 改动说明
   * 这个patch减少了扫描hash_slot的callback函数流程, 函数只负责将扫到的key
     临时记录到一个list, dictScan()退出后再遍历list获取实际要迁移的key。
主要参照codis源码里的extern/redis-3.2.11/patch/codis/0008-slots_async6_select.patch,
本次的主要改动是将SLOTSRESTORE-ASYNC命令中的SELECT子命令拆分成一个独立命令
1. 背景介绍
   * codis3.2的异步迁移实现中, 为避免迁移完成后删除大value对源端正常的业务
     访问造成阻塞, 会将迁移过程中遇到的大value全部记录下来, 交给自己创建的
     后台线程lazy release; 但在redis5.0中原本就有完整的lazy free机制, 可以
     通过dbAsyncDelete()将大value的删除交由统一的BIO线程在后台处理, 没必要
     在迁移模块里再自己创建线程, 重复实现lazy释放大对象的逻辑.
2. 改动说明
   * 移除异步迁移模块中自实现的lazy release逻辑, 改为在删除已迁移key的时候
     直接调用dbAsyncDelete()来实现大对象的lazy释放.
此patch是将codis源码中slots_async相关的所有patch针对redis5.0做了相应适配性
修改之后合并生成, 它实际包含了以下原始patch:
* 0003-slots_async1.patch
* 0004-slots_async2.patch
* 0005-slots_async3_lazyfree.patch
* 0006-slots_async4_bugfix_1184.patch
* 0007-slots_async5_cleanup.patch
* 0008-slots_async6_select.patch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant