You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache Ignite .NET多主键场景下KeyNotFoundException问题排查

问题描述

使用GridGain Ignite社区版8.8.31(与Apache Ignite 2.15版本一致),通过DDL创建两张仅主键名称不同的表并插入数据。在C#/.NET中调用cache.get()方法时,Person4表可正常运行,但Person3表抛出KeyNotFoundException。

DDL代码

CREATE TABLE IF NOT EXISTS Person3 (
ID int,
CITY_ID int,
NAME varchar,
AGE int,
COMPANY varchar,
PRIMARY KEY (ID, CITY_ID)
) WITH "template=REPLICATED,CACHE_NAME=Person3,KEY_TYPE=test.PersonKey3,VALUE_TYPE=test.PersonValue3";

INSERT INTO Person3 VALUES (1,1,'1',1,'1');

CREATE TABLE IF NOT EXISTS Person4 (
CITY_ID int,
CITY_ID_2 int,
NAME varchar,
AGE int,
COMPANY varchar,
PRIMARY KEY (CITY_ID, CITY_ID_2)
) WITH "template=REPLICATED,CACHE_NAME=Person4,KEY_TYPE=test.PersonKey4,VALUE_TYPE=test.PersonValue4";

INSERT INTO Person4 VALUES (1,1,'1',1,'1');

.NET代码

namespace test
{
    class PersonKey3
    {
        [QuerySqlField]
        public int ID, CITY_ID;
    }

    class PersonValue3 : PersonKey3
    {
        [QuerySqlField]
        public string NAME, COMPANY;

        [QuerySqlField]
        public int AGE;
    }

    class PersonKey4
    {
        [QuerySqlField]
        public int CITY_ID, CITY_ID_2;
    }

    class PersonValue4 : PersonKey4
    {
        [QuerySqlField]
        public string NAME, COMPANY;

        [QuerySqlField]
        public int AGE;
    }

    class IgniteTest
    {
        public static void Main()
        {
            IgniteClientConfiguration cfg = new IgniteClientConfiguration
            {
                Endpoints = new[] { "127.0.0.1:10800" }
            };

            using (var client = Ignition.StartClient(cfg))
            {
                client.GetBinary().GetBinaryType(typeof(PersonKey3));
                client.GetBinary().GetBinaryType(typeof(PersonValue3));

                client.GetBinary().GetBinaryType(typeof(PersonKey4));
                client.GetBinary().GetBinaryType(typeof(PersonValue4));

                /*runs well*/
                var cache4 = client.GetCache<PersonKey4, PersonValue4>("Person4");
                PersonKey4 tableKey4 = new PersonKey4();
                tableKey4.CITY_ID = 1;
                tableKey4.CITY_ID_2 = 1;
                Console.WriteLine(cache4.Get(tableKey4).COMPANY);

                /*error!*/
                var cache3 = client.GetCache<PersonKey3, PersonValue3>("Person3");
                PersonKey3 tableKey3 = new PersonKey3();
                tableKey3.ID = 1;
                tableKey3.CITY_ID = 1;
                Console.WriteLine(cache3.Get(tableKey3).COMPANY);
            }
        }
    }
}

错误信息

Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key was not present in the cache.
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.UnmarshalNotNull[T](ClientResponseContext ctx)
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.<Get>b__14_0(ClientResponseContext ctx)
   at Apache.Ignite.Core.Impl.Client.ClientSocket.DecodeResponse[T](BinaryHeapStream stream, Func`2 readFunc, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.ClientSocket.DoOutInOp[T](ClientOp opId, Action`1 writeAction, Func`2 readFunc, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.ClientFailoverSocket.DoOutInOpAffinity[T,TKey](ClientOp opId, Action`1 writeAction, Func`2 readFunc, Int32 cacheId, TKey key, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.DoOutInOpAffinity[T](ClientOp opId, TK key, Func`2 readFunc)
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.Get(TK key)
   at test.IgniteTest.Main() in E:\GHLee\dev\cs\GridgainTest\Program.cs:line 119
问题原因及解决方案

核心原因

问题出在自定义键类的相等性判断和哈希码计算上:

  1. C#自定义类默认使用引用相等性判断,而Ignite客户端与服务器端的键对象是不同实例,无法通过引用判断相等。
  2. 未重写Equals和GetHashCode方法,导致客户端生成的键对象哈希码与服务器端存储的键哈希码不匹配,缓存无法定位到对应条目。

Person4能正常运行属于偶然情况(字段组合的哈希码恰好匹配),但这种实现方式本身存在严重隐患。

解决方案

为所有自定义键类重写Equals和GetHashCode方法,确保基于字段值进行相等性判断和哈希码计算:

修改后的PersonKey3类

class PersonKey3
{
    [QuerySqlField]
    public int ID, CITY_ID;

    public override bool Equals(object obj)
    {
        if (obj is not PersonKey3 other)
            return false;
        
        return ID == other.ID && CITY_ID == other.CITY_ID;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(ID, CITY_ID);
    }
}

修改后的PersonKey4类

class PersonKey4
{
    [QuerySqlField]
    public int CITY_ID, CITY_ID_2;

    public override bool Equals(object obj)
    {
        if (obj is not PersonKey4 other)
            return false;
        
        return CITY_ID == other.CITY_ID && CITY_ID_2 == other.CITY_ID_2;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(CITY_ID, CITY_ID_2);
    }
}

额外注意事项

  • 确保键类的字段顺序与DDL中主键的字段顺序完全一致,Ignite的二进制序列化对字段顺序敏感,顺序不一致会导致哈希码不匹配。
  • 避免在一行声明多个字段(如public int ID, CITY_ID;),显式分行声明字段可以明确序列化顺序,减少潜在问题。

内容的提问来源于stack exchange,提问作者GH Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:46:05