【问题标题】:Multi index hash table implementation多索引哈希表实现
【发布时间】:2014-10-03 06:40:47
【问题描述】:

我有一个需要快速查找的记录表,因此决定进行哈希表查找。

现在,最终出现的问题是我必须根据多个键查找记录。

例如,下面的所有 4 个键都应指向同一记录。

key1 -> a,b,c,d,e
key2 -> a,b,d
key3 -> a,b,e
key4 -> c

问题 #1 然后,此模式显示出与指定多个键的数据库查找的相似性。那么,B-tree 数据结构是否比多哈希表设计更适合使用?

问题 #2 一个特殊的尝试是否更适合这个问题。默认实现需要所有键 a+b+c+d+e 作为查找键。如果我必须查找 a+b+d,那么在查找时必须从这个主密钥中跳过 c & e。但是,这个想法会奏效还是已经存在?

问题 #3 另一个想法是我是否将东西插入到我的表中,同时我建立另一个查找表,其中包含每个记录的索引。这样,我可以为每个键设置多个掩码,并扫描此查找表以查找匹配的记录。我猜类似于 CAM 表。但是如果我必须扫描整个表,性能就会下降。是否可以将哈希表 + 索引逻辑混合在一起以提供速度和最佳内存使用?

到目前为止,已经尝试使用 boost multi index、uthash、trie 等来尝试实现适合所有 4 个问题的设计,但到目前为止还没有成功。我喜欢 boost multi index,但它有自己的问题,禁止我使用。

虽然我使用 C 语言进行编程和测试设计,但我对 java、php、python 等任何其他语言都很好。

任何解决此问题的其他想法将不胜感激。

我想实现的解决方案的伪代码:

/* Keys */
struct key1_s {
int src;
int dst;
char name[10];
int t1;
int t2;
};

struct key2_s {
int src;
int dst;
char name[10];
};

struct key3_s {
int src;
int dst;
int t1;
};


struct key4_s {
int src;
int dst;
int t2;
};


/* Record */
struct record_s {
int src;
int dst;
char name[10];
int t1;
int t2;
int age;
int sex;
int mobile;
}

struct record_s record[2] = {
{1, 2, "jack", 5, 6, 50, 1, 1234567890},
{3, 4, "john", 7, 8, 60, 2, 1122334455}
};
table.insert(record[0]);
table.insert(record[1]);

/* search using key1 */
struct key1_s key1;
key1.src = 1;
key1.dst = 2;
strncpy(key1.name, "jack", 10);
key1.t1 = 5;
key1.t2 = 6;
table.find(key1); // should return pointer to record[0]

/* search using key2 */
struct key2_s key2;
key2.src = 1;
key2.dst = 2;
strncpy(key1.name, "jack", 10);
table.find(key2); // should return pointer to record[0]

/* search using key3 */
struct key3_s key3;
key3.src = 1;
key3.dst = 2;
key3.t1  = 5;
table.find(key3); // should return pointer to record[0]

如果 find() 返回一个成功的指针,那么我想更新年龄、性别、手机等记录字段。

【问题讨论】:

  • 抱歉符号混乱,为了澄清我的意思,添加一些伪代码

标签: python c++ boost data-structures


【解决方案1】:

Boost Multi Index 可以在这里提供帮助。

composite_keys.cpp 示例包含一个引人注目的示例。您只需将 ordered 全局替换为 hashed 即可获得您正在处理的内容(此外,在您的情况下,关键配置会有更多重叠)。

关于性能问题,我认为没有明确的答案;它(总是)取决于使用模式。您将需要分析和平衡优化所花费的精力。

我个人认为 Boost Multi Index 是当重点放在方便和快速结果时的最佳选择。请注意,这绝不意味着 BMI 没有优化(我相信它是高度优化的);但是它将/总是/取决于使用模式。 (考虑一个最初批量插入大量数据,然后只是读取的应用程序;这样的应用程序可以受益于一次显式地构建索引,而不是在每次插入时自动更新所有索引)。

Live On Coliru

using namespace boost::multi_index;

/* A file record maintains some info on name and size as well
 * as a pointer to the directory it belongs (null meaning the root
 * directory.)
 */

struct file_entry
{
  file_entry(
    std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_):
    name(name_),size(size_),is_dir(is_dir_),dir(dir_)
  {}

  std::string       name;
  unsigned          size;
  bool              is_dir;
  const file_entry* dir;

  friend std::ostream& operator<<(std::ostream& os,const file_entry& f)
  {
      os << f.name << "\t" << f.size;
      if (f.is_dir)os << "\t <dir>";
      return os;
  }
};

/* A file system is just a multi_index_container of entries with indices on
 * file and size (per directory). 
 */
struct dir_and_name_key:composite_key<
  file_entry,
  BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir),
  BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name)
>{};

struct dir_and_size_key:composite_key<
  file_entry,
  BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir),
  BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size)
>{};

typedef multi_index_container<
  file_entry,
  indexed_by<
    hashed_unique<dir_and_name_key>,
    hashed_non_unique<dir_and_size_key>
  >
> file_system;

/* typedef's of the two indices of file_system */
typedef nth_index<file_system,0>::type file_system_by_name;
typedef nth_index<file_system,1>::type file_system_by_size;

/* We build a rudimentary file system simulation out of some global
 * info and a map of commands provided to the user.
 */

static file_system fs;                 /* the one and only file system */
static file_system_by_name& fs_by_name=fs;         /* name index to fs */
static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */
static const file_entry* current_dir=0;            /* root directory   */

【讨论】:

  • 我使用 BMI 来解决这个问题,但发现以下用例存在一些问题:
  • 我已经使用 BMI 来解决这个问题,但在尝试解决我的问题时遇到了一些问题。我的使用模式是最初会有批量插入,之后会以高速率进行更新/删除。这就是我受到打击的地方。每当我必须更新表中的记录时,调用 table.replace(key, newrecord) 实际上会删除旧记录并插入新记录。我发现这是一个相当昂贵的操作。相反,如果我能得到指向它的指针,我希望我可以继续更新记录。
  • 除非您不更新关键字段,否则您无法避免更新索引。除非您修改关键字段,否则通过引用进行修改是可以的
  • 使用modify 而不是replace:这是最快的,不会发生删除/插入。
  • @JoaquínMLópezMuñoz 我想如果您知道您没有更新关键字段,那么直接对对象进行操作会更快吗?另外,your observation made here 不参与 OP 给出的特定场景吗?我承认在看到你的名字之前我已经忘记了:)
猜你喜欢
  • 1970-01-01
  • 2016-03-25
  • 2011-10-14
  • 2011-09-15
  • 2012-06-03
  • 2021-09-14
  • 2013-05-31
  • 1970-01-01
相关资源
最近更新 更多