Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/redis 5.0 #1756

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant