【问题标题】:Secondary Index on Key/Value databaseKey/Value 数据库的二级索引
【发布时间】:2019-02-04 12:06:42
【问题描述】:

可以说,我有像

这样的数据结构
 type User struct {
      UUid string 
      Username string
      Email String 
      Password string 
      FirstName string 
      LastName string
}

我将用户 []User 存储到 levelDB 中的键/值数据库中。唯一键将是 UUId,然后用户结构将根据此 UUID 进行存储和存储。

var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network)
err := enc.Encode(user)
   if err != nil {
      log.Println("Error in encoding gob")
      return "", err
 }
err = dbSession.DBSession.Put([]byte(user.UserID), network.Bytes(), nil)

由于所有条目的键是唯一的 uuid,我想在电子邮件上建立二级索引,这样我就不必扫描数据库中存在的所有条目来找到与电子邮件对应的特定条目。

我做了什么: 我创建了一个名为 SIndex 的键,并在其中存储了一个 map[string][string] 数据结构,其中键是电子邮件,值是 uuid。每次有新条目进入时,此 Sindex 都会更新以适应新的 uuid 和电子邮件。

这是一个不好的方法: 因为随着数据的增长,需要对Sindex对应的Whole map进行获取和解码,如果email不存在,则给Sindex添加一个新的key,编码后再存储回来。

B-tree 会更合适。

我的问题:在数据库本身存储二级索引数据是否正确,如果不是我应该使用什么策略来实现二级索引,我知道二级索引的选择受数据影响很大但是有什么好的吗?除了 B-Tree、HashMaps 以外的框索引算法?

【问题讨论】:

  • LevelDB 可能已经使用一些树实现了。您应该通过将电子邮件作为键并将用户的 UUID 作为值来简单地创建二级索引。这样您可能可以快速有效地检索。
  • 这正是我所做的,我创建了一个新密钥,并针对它存储了一个电子邮件 uuid 映射,然后将其存储在同一个 leveldb 上。
  • 抱歉,我不是这个意思。给定具有 UUID abba-cafe 和电子邮件 A@example.org 的用户 A,您将 abba-cafe 存储为键 A@example.org 上的直接值。您不应该存储地图。
  • 好的,非常感谢乔纳斯 ...

标签: database go database-design leveldb


【解决方案1】:

数据库本身存储二级索引数据对吗

是的,没关系。但正如 Jonas 在评论中指出的那样,您应该将电子邮件作为键,将 UUID 作为值。另一种选择是使用电子邮件作为数据库的密钥,而不是使用 UUID。这样您就不需要使用二级索引。

另一个提高性能的策略,你可以使用内存数据库,例如 Redis(或者 LevelDB 本身可以用来将数据存储在内存中)来存储二级索引(电子邮件作为键,UUID 作为值)。

除了 B-Tree、HashMaps 之外,还有什么好的开箱即用的索引算法

无论如何,B-Tree 和 HashMap 是数据结构,而不是算法。而您实际上所做的并不是使用 HashMap 进行索引,它只是将 HashMap 存储为您的键的值。索引通常取决于 DBMS 实现(我们只能从它们提供的选项中进行选择)。

因此,关于用于索引的数据结构,它是否好用,实际上取决于用例。例如,如果您需要进行范围搜索,您可以使用 B-Tree(大多数 DBMS 默认使用)、B+ 树(MySQL InnoDB 默认使用)和跳过列表(Redis 使用此数据结构进行排序放)。你可以阅读更多关于 Redis Sorted Set here 的二级索引。

对于您的情况,您只需将电子邮件存储为键,将 UUID 存储为值。哈希表通常用于此目的。大多数 DBMS 使用这种数据结构来进行主键访问,时间复杂度仅为 O(1)。而且我相信LevelDB的实现也是基于这个数据结构的。

【讨论】: