some details about rocksdb::LRUCache

目录

最近有同事找我讨论问题,觉得挺有趣的记录一下。

rocksdb::LRUHandle::key()的历史

第一个问题是rocksdb::LRUHandle::key(), 这个函数返回该handle内部存储的key,但为啥某些情况下却返回value呢?

e>

乍看一眼,应该是某些场景下(如初始化时)图方便的产物。

另外扫了一眼codebase,确认了下next == this分支不会被调用到,凭经验盲猜是某个历史遗留的代码。由于没啥说服力,我去挖坟了一下git log,发现最新rocksdb已经把这个无用分支去掉了, 问题到这里似乎应该结了.

但由于无聊我又去check了一下leveldb在这块的实现,发现这块确实是祖传代码- -!,并且在17年左右就做了一次代码清理。

leveldb code clean at lrucache

new expression的理解

  • 问题描述

另外还有个问题,类似下面的语句中的new表达式,[]应该怎么理解,是把它理解为type的一部分呢? 还是非type的一部分?

LRUHandle** new_list = new LRUHandle*[new_length];
  • 问题讨论

我们先不去翻cpp标准,先把自己的大脑当作编译器.可以把问题抽象为如下的形式:

表达式如T** ptr = new T*[n];:

假如[]是非type的一部分的话,语义上表达的应该是new了n个连续且类型为T的对象; —A1

另一方面,假如[]是type的一部分的话,语义上表达的应该是new了一个对象,该对象的类型是长度为n/类型为T的数组. —A2

于是可以得到两个推论,可用于验证:

若A1成立的话,那么按理T** ptr = new (T*)[n];应该成立 — AA1

若A2成立的话,T** ptr = new (T*[n]);应该成立 — AA2

  • 问题解决

但编译器告诉我们括号是有问题的. AA1不成立,AA2成立.

一般写法:

[ghostv]$ g++ -std=c++11 -x c++ - -o /tmp/a << EOF
> int main() {
>   int **a = new int*[10];
>   return 0;
> }
> EOF

AA1编译器报错:

[ghostv]$ g++ -std=c++11 -x c++ - -o /tmp/a << EOF
int main() {
  int **a = new (int*)[10];
  return 0;
}
EOF

<stdin>: In function ‘int main()’:
<stdin>:2:23: error: array bound forbidden after parenthesized type-id
<stdin>:2:23: note: try removing the parentheses around the type-id

AA2编译通过:

[ghostv]$ g++ -std=c++11 -x c++ - -o /tmp/a << EOF
int main() {
  int **a = new (int*[10]);
  return 0;
}
EOF

所以应该按AA2来理解.

AA1 — Compile Error AA2 — Compile OK

  • cppreference

现在回过头来check一下标准答案,标准里对于new表达式的定义如下,标准里说的很清楚了,array就是被包含在type里的:

cpp ref of new expr

  • 总结一下为什么会有这个问题?

主要还是[]的位置引发小伙伴的难以理解. 因为这里[]是被包含在type里,那么似乎直观上又与声明一个数组类型的语句相冲突: T[10] A;的写法是编译不通过的:

[ghostv]$ g++ -std=c++11 -x c++ - -o /tmp/a << EOF
int main() {
  int* [10] A;
  return 0;
}
EOF

<stdin>: In function ‘int main()’:
<stdin>:2:8: error: expected unqualified-id before ‘[’ token

另一方面,可以使用一个typedef,但typedef本身似乎也是一样,[]在最后:

[ghostv]$ g++ -std=c++11 -x c++ - -o /tmp/a << EOF
int main() {
  typedef int* T[10];  T A;
  return 0;
}
EOF

最后,如果用template的话,就非常符合直观感受了:

#include <iostream>
struct a{};
template<typename T>
void fucktype() {
  T a;
  std::cout << sizeof(a) << std::endl; 
}
int main() {
  fucktype<a*[10]>();
  return 0;
}

个人猜测,引发不适的估计是为了兼容以前c的语法。

顺便recall一下LRUCache

  1. 关于原理,基本上不用再多说了
  2. 关于rocksdb是如何使用LRUCache
  3. 关于shard数,我们踩过的坑
  4. 原生rocksdb::LRUCache只增加元素,达到容量上限后,没有清理元素的过程(etc. GC)
  5. 哈希算法: 老版 新版
  6. Usage统计:老版 新版
  7. 自适应锁优化: