【问题标题】:Out of Process in memory database table that supports queries for high speed caching支持高速缓存查询的内存数据库表中的进程不足
【发布时间】:2018-02-10 02:57:42
【问题描述】:

我有一个不断访问但很少更改的 SQL 表。

表按UserID分区,每个用户在表中有很多条记录。

我想节省数据库资源,并在某种内存缓存中将此表移动到更靠近应用程序的位置。

进程内缓存过于占用内存,因此需要在应用程序外部进行。

事实证明,像 Redis 这样的键值存储效率很低,因为在 Redis 之间对表进行序列化和反序列化的开销很大。

我正在寻找可以将此表(或数据分区)存储在内存中的东西,但让我只查询我需要的信息,而无需为每次读取序列化和反序列化大块数据。

是否有任何东西可以提供内存数据库表中的 Out of Process 以支持高速缓存查询?

搜索表明 Apache Ignite 可能是一个可能的选择,但我正在寻找更明智的建议。

【问题讨论】:

  • “内存数据库表中的进程外”是什么意思?
  • 你的前端是什么?一些托管环境(如 Asp.net)具有输出缓存或中间件缓存,如果它符合您的需求,您也可以查看 Elastic Search。

标签: sql caching redis in-memory-database in-memory-tables


【解决方案1】:

既然是进程外的,就得做序列化和反序列化。您关心的问题是如何减少序列化/反序列化工作。如果你使用 Redis 的 STRING 类型,你不能减少这些工作。

不过,您可以使用HASH 来解决问题:将您的SQL 表映射到HASH

假设你有如下表:person: id(varchar), name(varchar), age(int),你可以将id作为key,以nameage作为字段。当你想搜索某人的名字时,你只需要获取名字字段(HGET person-id name),其他字段不会被反序列化。

【讨论】:

  • 对于我存储的数据,每个用户都会有很多行需要缓存。理想情况下,我想使用 UserId 和 TagId 进行查询。每个用户将有许多不同的行,其中 TagId 不同。如果我可以从内存表中进行 SQL 类型查询并说出 UserId =y 和 TagId = x 的位置,并且只取回包含数据的一行,那将是理想的。这就是我们使用 SQL 存储和检索当前数据的方式。 SQL hekaton 听起来有可能,但成本很高。
【解决方案2】:

Ignite 确实是您可能的解决方案,因为您可以通过使用内部二进制表示来访问对象的字段来优化序列化/反序列化开销。您可以参考此文档页面了解更多信息:https://apacheignite.readme.io/docs/binary-marshaller

还可以通过禁用读取时复制选项来优化访问开销https://apacheignite.readme.io/docs/performance-tips#section-do-not-copy-value-on-read

Ignite 也可以按用户 ID 配置数据:https://apacheignite.readme.io/docs/affinity-collocation

【讨论】:

    【解决方案3】:

    正如@for_stack 所说,Hash 将非常适合您的情况。

    您说每个用户在 db 中有很多行,由 user_idtag_id 索引。所以它是 (user_id, tag_id) 唯一指定一行。每一行的功能取决于这个元组,你可以使用元组作为 HASH KEY。

    例如,如果您想将值为 ("123456", "FDSA", "gsz", 20) 的行 (user_id, tag_id, username, age) 保存到 redis 中,您可以这样做:

    HMSET 123456:FDSA username "gsz" age 30
    

    当你想用 user_id 和 tag_id 查询用户名时,你可以这样做:

    HGET 123456:FDSA username
    

    所以每个哈希键都将是user_idtag_id 的组合,如果您希望键更易于阅读,可以添加前缀字符串,例如“USERINFO”。例如:USERINFO:123456:FDSA.

    但是如果你想只用一个user_id查询并得到这个user_id的所有行,上面的这个方法是不够的。

    您可以在 redis 中为您的 HASH 构建 secondary indexes

    如上所述,我们使用user_id:tag_id 作为HASH 键。因为它可以唯一点到一行。如果我们要查询关于一个 user_id 的所有行。

    我们可以使用sorted set 建立二级索引来索引哪些哈希存储有关此用户ID 的信息。

    我们可以在 SortedSet 中添加这个:

    ZADD user_index 0 123456:FDSA
    

    如上所述,我们将member设置为string of HASH key,并将score设置为0。规则是我们应该将此zset中的所有分数设置为0,然后我们可以使用字典顺序做范围查询。参考zrangebylex

    例如我们想要获取关于 user_id 123456 的所有行,

    ZRANGEBYLEX user_index [123456 (123457
    

    它会返回所有前缀为123456的HASH key,然后我们使用这个字符串作为HASH key,然后hget或hmget来获取我们想要的信息。

    [ 表示包含,( 表示排除。为什么我们使用123457?这很明显。所以当我们想要获取所有带有 user_id 的行时,我们应该指定上限,使 user_id 字符串的最左边 char 的 ascii 值加 1。

    更多关于 lex 索引的内容可以参考我上面提到的那篇文章。

    【讨论】:

      【解决方案4】:

      你可以试试intel 启动的apache mnemonic。链接-http://incubator.apache.org/projects/mnemonic.html。它支持无序列化功能

      【讨论】:

        【解决方案5】:

        对于以读取为主的工作负载,MySQL MEMORY engine 应该可以正常工作(编写 DML 锁定整个表)。这样您就无需更改数据检索逻辑。

        或者,如果您可以更改数据检索逻辑,那么 Redis 也是一种选择。为了补充@GuangshengZuo 所描述的内容,ReJSON Redis 动态可加载模块(适用于 Redis 4+)在 Redis 之上实现了文档存储。它可以进一步放宽在网络上来回编组大型结构的要求。

        【讨论】:

          【解决方案6】:

          只需 6 条原则(我收集了 here),一个有 SQL 头脑的人很容易让自己适应 Redis 方法。简而言之,它们是:

          1. 最重要的是,不要害怕生成大量的键值对。因此,您可以随意将表格的每一行存储在不同的键中。
          2. 使用 Redis 的哈希映射数据类型
          3. 用分隔符(如“:”)从表的主键值中形成键名
          4. 将剩余字段存储为哈希
          5. 当你要查询单行时,直接形成键并检索其结果
          6. 当您要查询范围时,请使用通配符“*”作为您的键。但请注意,扫描键会中断其他 Redis 进程。因此,如果您确实需要,请使用此方法。

          该链接仅提供了一个简单的表格示例以及如何在 Redis 中对其进行建模。遵循这 6 条原则,您可以继续像对待普通桌子一样思考。 (当然没有一些不那么相关的概念,如 CRUD、约束、关系等)

          【讨论】:

            【解决方案7】:

            想到在 MYSQL 之上使用 Memcache 和 REDIS 组合。

            【讨论】:

              猜你喜欢
              • 2012-05-03
              • 2016-07-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-09-01
              • 2017-09-26
              • 1970-01-01
              相关资源
              最近更新 更多