数据库中的冗余与代码中的效率

时间:2015-09-05 03:50:23

标签: database entity-framework database-design

我想用一个例子来解释我的问题。假设学生可以注册许多课程。每门课程都有很多讨论墙。每个讨论墙都包含问题。每个问题都可能有回复。并且可以将徽章分配给回复(或评论)。

在我的情况下,我需要知道哪个回复属于哪个回复(列出回复时),徽章也是如此。我能够使用Entity Framework执行此操作,但查询变得非常复杂并导致性能问题。

在这种情况下,在Replies(或BadgeAssignments)表中使用CourseId列会更好吗?或不?这会让我的生活在某些方面变得更容易,但从长远来看并不确定。你怎么看?有时候有一些冗余更好吗?我认为我不需要稍后更新CourseId字段。

4 个答案:

答案 0 :(得分:2)

我的烦恼是牺牲数据完整性来提高性能。更快地获得不太可靠的答案并不是一个好的解决方案。但是,改善性能而不牺牲数据完整性的更改很好。

冗余可能会牺牲数据完整性。这当然是异常数据可以开始的关键点。问题是两个"设置"数据必须严格同步,这取决于设计,可能很容易或很难做到。无论哪种方式,它都需要系统资源来维护同步,因此您将在性能上添加另一个命中。

幸运的是,性能命中将添加到DML操作中,因为这是执行同步的地方。通常,将查询到DML(通常对响应时间不太敏感)的性能时间转换是一个很好的解决方案。

然而,恶魔在细节中,并且您没有提供任何细节。如果没有冗余,性能是否可以充分改善?保持冗余数据之间同步的难度是多少?另一种询问最后一个问题的方法是:异常(未同步)数据进入系统的可能性有多大?未经同步的数据有多少问题,修复它有多难?

没有提供足够的信息来回答这些问题。但在调查解决方案时请记住这些。

答案 1 :(得分:2)

应该使用系统的每个组件,因为它的设计使其成为“最佳”组件。当他们根据他们的设计工作时,事情会更好。严格来说,这是我对你问题的回答。

关系数据库

关系数据库的目的首先是管理信息的完整性,其次是提供存储和检索系统。 RDMS管理您的真相,然后确定应该存储和检索的方式。

由于我们难以想象数字讨论墙的独特性以及问题和回复,因此我们通常会使用代理键(即自动生成的数字)来实现这些实体的主键。这意味着将课程ID添加到Questions,Replies或BadgeAssignments的决定将违反主体关系设计。在这种情况下你可能会说“没什么大不了的”,但它仍然存在违规行为,只要它持续存在(双关语),就会产生后果。

如果我们在课程,墙壁,问题,回复和徽章分配中使用自然键,那么每个表的主键都将来自这些表的复合。例如,我们会在回复的复合主键中拥有课程的主键,而不会违反任何冗余或正常化的原则,您的生活将“更容易”。

那说,这个查询有多难?

SELECT
    D.CourseId, D.CourseName
    ,A.ReplyId, A.ReplyName
FROM
    Replies A
    JOIN Questions B On A.QuestionId = B.QuestionId
    JOIN Walls C ON B.WallId = C.WallId
    JOIN Courses D ON C.CourseId = D.CourseId

实体框架

实体框架(EF)可以配置为与您的设计相匹配,无论我们将CourseId放在回复中还是依赖于连接。但是,在SQL性能方面,我们通常可以做得比EF好。

一种选择是根据您的需要制作一个具有最高优化量的SQL查询(从上面开始),并将其转换为视图。然后,将C#类映射到View(而不是表),并简化交互。我们会让EF超过提供低麻烦的数据访问,并且SQL成功地检索数据。

这是C#Linq的区别......

var replies = context.Replies
    .Where(x => x.Questions.Walls.CourseId == 1)
    .Select(x => new ReplyView
    {
        CourseId = x.Questions.Walls.Courses.CourseId,
        CourseName = x.Questions.Walls.Courses.CourseName,
        ReplyId = x.ReplyId,
        ReplyName = x.ReplyName
    }).ToList();

var replies = context.RepliesView.Where(x => x.CourseId == 1).ToList();

答案 2 :(得分:1)

由于您已使用标记了问题,因此我假设您使用的是SQL Server,在这种情况下,您可以考虑使用indexed views来“缓存”JOIN,而不必担心此缓存不同步 - DBMS会一直为你维护它。

例如,您可以在课程,学生,讨论墙,问题,回复和徽章之间缓存JOIN。因此,当您想知道哪个徽章属于哪个徽章时,您只需从索引视图中检索一行,而不是执行物理连接。

或者,考虑重新设计密钥并使用identifying relationships将密钥字段迁移到外键层次结构中,因此在查询子表时,您可以获取非直接父表的密钥,而无需在表之间加入表。 ”

最后但并非最不重要的是,我热烈建议阅读Use the Index, Luke!,了解每个开发人员对数据库性能应具备的基础知识......

答案 3 :(得分:0)

我将在这里发布一个例子:

public class SchoolEntities : DbContext 
{ 
    public DbSet<Department> Departments { get; set; } 
} 

public class Department 
{ 
    // Primary key 
    public int DepartmentID { get; set; } 
    public string Name { get; set; } 

    // Navigation property 
    public virtual ICollection<Course> Courses { get; set; } 
} 

public class Course 
{ 
    // Primary key 
    public int CourseID { get; set; } 

    public string Title { get; set; } 
    public int Credits { get; set; } 

    // Foreign key 
    public int DepartmentID { get; set; } 

    // Navigation properties 
    public virtual Department Department { get; set; } 
} 

public partial class OnlineCourse : Course 
{ 
    public string URL { get; set; } 
} 

public partial class OnsiteCourse : Course 
{ 
    public string Location { get; set; } 
    public string Days { get; set; } 
    public System.DateTime Time { get; set; } 
}

这是一个小例子......确实有这些信息吗?