调查
我在调试代码时在数据库上运行 SQL Profiler。它表明第一次调用 GetById() 函数时,执行了一个从数据库表中选择所有记录的查询。主键不传递给数据库。下次执行 GetById() 函数时,不再查询数据库。结果从缓存值返回。
因此,从 GetById() 函数返回的单个记录必须是对表中所有记录的结果集进行某种搜索的结果。这个函数如何操作和检索记录的细节在编译代码中(这个编译代码的源可能是可用的,但我还没有调查过)。
我查看了执行此代码期间的内存使用情况,以确保 IIS 有足够的数量。IIS 中的设置被设置为允许应用程序池使用可用内存。在调试 w3wp.exe 进程的内存使用情况时,内存使用量在 0.75 和 1.3 GB 之间波动。有大量额外的 RAM 可用,因此网站可以在需要时使用更多。
这些实验的亮点是:
- GetById() 函数第一次访问数据库表时,它将选择表中的所有记录。
- 缓存按预期运行,因此重复查询不会命中数据库。
- 无论 IIS 有多少内存可用,从缓存中检索结果都需要几秒钟的时间。
解决方案
为了解决这个问题,我改变了我的方法。在 SELECT 过程中直接使用主键查询数据库时,查询返回很快。这导致我使用 DAL2 存储库的 Find() 函数,这样我就可以自己指定 SELECT 的条件。
我创建的函数如下所示:
public MyThing Get(string primaryKey)
{
MyThing myThing = (MyThing)DataCache.GetCache(primaryKey);
if (myThing != null)
return myThing;
IList<MyThing> myThings;
using (IDataContext ctx = DataContext.Instance(“DATABASECONN”))
{
var rep = ctx.GetRepository<MyThing>();
myThings = (IList<MyThing>)rep.Find("WHERE myPrimaryKeyFieldName = @0", primaryKey);
if (myThings.Count > 0)
DataCache.SetCache(primaryKey, myThings[0]);
}
return myThings.Count > 0 ? myThings[0] : null;
}
上面的函数使用 Find() 函数,因此 DAL2 没有实现缓存。为了解决这个问题,我包含了一种自定义缓存技术。使用 SQL Profiler 调查此函数表明,使用相同的 primaryKey 值重复调用该函数并没有命中数据库,因此缓存的行为符合预期。
以这种方式使用的缓存不会像使用 GetById() 时那样减慢执行时间。DateTimes 放置在函数中,用作计时器,显示以下执行时间。当数据库被命中时,00:00:00.1 或更小。当缓存被命中时,执行时间为 0。这没有进行大规模测试,但执行时间在我运行的测试中是一致的。
结论
上述测试和解决方案表明,应谨慎使用 DAL2 中的 GetById()。根据数据库的大小检索记录不一定是最佳解决方案。如果数据检索执行缓慢,请探索替代方法,例如解决方案中建议的方法。
找出表中 GetById() 不再是最佳解决方案的确切记录数会很有帮助。此数字可能会因对象中数据的大小而异。它可能是可计算的,但目前没有等式,因此需要根据具体情况来计算。
如果其他人对此主题有额外的信息,请将其添加到讨论中。