【问题标题】:Redis Secondary Indicing maintenance add,remove are fine what about updatesRedis Secondary Indicing 维护添加,删除都可以,更新呢
【发布时间】:2022-01-08 04:20:01
【问题描述】:

我读过所有关于插入的文章和文章,但我遇到了问题。

我已经使用事务(MULTI)和集合实现了二级索引机制,以便在查找实体时节省时间,索引由属性名称和值的集合名称保存。 说我们有一个人

Person Jack = new Person() { Id = 1, Name = Jack, Age = 30 }
Person Jena = new Person() { Id = 2, Name = Jena, Age = 30 }

当我选择索引 Age 属性并插入两者时,我会查找 1 和 2 的 Age 准备它们并将它们插入相应的集合并在同一事务中更新索引。 age_30_index 持有 ids 1,2

删除 Jack 时,我准备 Jacks 年龄 30,并从 age_30_index 中删除 id 1 并在一次事务中再次将 Jack 从其集合中删除,一切都很好,嗯.. 几乎。

问题从我想改变Age和更新缓存开始,看下面的场景:

var p = GetEntity<Person>(id: 1)
p.Age = 31
UpdateEntity(p)

现在有了上面的概念,我将拥有age_30_index -&gt; 1,2age_31_index -&gt; 1 那是因为在更新缓存中的实体时,我不知道缓存中存储的属性的值是什么。因此无法将其从索引中删除。

另一个问题是按 ID 删除,或者像这样删除:

var p = GetEntity<Person>(id: 1)
p.Age = 31
DeleteEntity(p)

一个简单的解决方案是使用分布式锁并按实体名称锁定,从缓存中获取实体,删除索引并继续,但我运行的测试,性能不足。

我想到的任何其他选项都不是线程安全的,因为它不是原子的。

还有其他方法可以实现我想做的事情吗?

该项目是 c# .net 框架,在 windows 上带有 redis,redisearch.io 看起来不错,但不幸的是它超出了范围。

【问题讨论】:

  • 您是否考虑改用 RediSearch 模块?
  • @GuyKorland 正如我所写,目前这不是一个真正的选择,我想避免它,

标签: redis


【解决方案1】:

艰难的道路

如果由于您在 Windows 上运行 Redis 而确实不想使用 RediSearch - 我的主要意见是您使用了错误的数据结构来存储年龄,数字通常应该存储在排序集中,因此您可以更轻松地查询它们

因此,对于您的年龄,您将拥有排序集 Person:Age,并且当您添加 Jack(假设 Jack 的 ID 是 1 并且年龄是 30 就像您的示例一样)时,您将像这样设置索引:

ZADD Person:Age 30 1

那么当您需要将 Jack 的年龄更新为 31 岁时,您所需要做的就是更新排序集中的成员:

ZADD Person:Age 31 1

然后您就可以使用 ZRANGE 查询所有 31 岁的人,在 StackExchange.Redis 中如下所示:

db.SortedSetRangeByScore("Person:Age", 31, 31, Exclude.None);
  • 就是这样,您无需在较旧的集合和较新的集合中查找它。

如果您需要横向扩展 redis,那么您对原子性的更大关注应该是围绕。如果您需要这样做,那么没有 RediSearch 就没有好的原子方式来更新分布式索引。那是因为多键操作或事务中的所有键都必须在同一个槽中,否则redis将不接受该操作。因为您在 Windows 上,所以我假设您没有使用集群多分片环境,并且您是独立的或哨兵。

假设您正在运行独立或哨兵,您可以在 lua 脚本中运行更新,这将允许您按顺序运行所有命令,基本上您的脚本将更新您的实体,然后更新所有随附的索引,所以如果您在您的示例Person:Age 中有一个年龄排序集以更新您的脚本,如下所示:

redis.call('HSET', KEYS[1], ARGV[1])
redis.call('ZADD', 'Person:Age', ARGV[1], KEYS[1])

自然,您必须根据索引的实际情况更改此脚本,因此您可能想要进行某种动态脚本生成。我会在 MULTI 上使用脚本,因为 StackExchange.Redis 上的 MULTIslightly different behavior 比 redis 预期的要多,因为它的架构。

RediSearch 和 Redis.OM 的最佳方式

处理此问题的最佳方法是使用 RediSearch(我将这部分放在第二位,尽管它确实是正确的答案,因为这听起来像是您想要避免的事情,考虑到您的环境)。使用 RediSearch + Redis.OM .NET,您只需更新有问题的项目并调用 collection.Save(),因此对于您的示例 - 以下是您要查找的所有内容(插入、检索、更新、删除)

using Redis.OM;
using TestRedisOmUpdate;

var provider = new RedisConnectionProvider("redis://localhost:6379");
provider.Connection.CreateIndex(typeof(Person));
var collection = provider.RedisCollection<Person>();

//insert
var id = await collection.InsertAsync(new() {Name = "Jack", Age = 30});

//update
foreach (var person in collection.Where(p => p.Name == "Jack"))
{
    person.Age = 31;
}

await collection.SaveAsync();

//check
foreach (var person in collection.Where(p => p.Name == "Jack"))
{
    Console.WriteLine($"{person.Name} {person.Age}");
}

//delete
provider.Connection.Unlink(id);

一切都可以脱离模型:

[Document]
public class Person
{
    [Indexed]
    public string Name { get; set; }
    [Indexed]
    public int Age { get; set; }
}

而且您不必为自己维护索引而头疼或麻烦。

【讨论】:

  • 这不是我所希望的,试图避免重新搜索但感谢您提供的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-07
  • 1970-01-01
  • 2020-02-06
  • 2020-12-21
  • 1970-01-01
相关资源
最近更新 更多