当前位置: 首页 > 后端技术 > PHP

初学后端,如何做好表结构设计?

时间:2023-03-30 01:25:17 PHP

前言最近很多转战Go的前端和测试朋友私信我:如何设计好表结构?杨哥一定要把大家关心的问题梳理一下,希望对大家有所帮助。先说结论吧。本文介绍了数据库表结构设计应考虑的4个方面,以及优雅设计的6个原则。我举个例子来分享一下我的设计思路。为了提高性能,我们还需要从多方面考虑缓存。问题。最大的收获就是和大家的交流和讨论。总结一下:首先要先了解业务需求。比如我的例子,如果不需要灵活设置,可以写在配置文件中,不需要单独设计外键。各种筛选标签的名称直接存储在主表中(注意维护问题,必须考虑数据一致性)。数据库表结构设计必须考虑数据量和并发性。在我的例子中,如果数据量不大,可以适当做冗余设计,降低业务复杂度。4方面设计数据库表结构需要考虑以下4个方面:数据库范式:通常我们希望表中的数据符合一定的范式,这样可以保证数据的完整性和一致性。例如,第一范式要求表的每个属性都是原子的,第二范式要求每个非主键属性完全依赖于主键,第三范式要求每个非主键属性不依赖于其他非主键属性。实体-关系模型(ER模型):我们需要根据实际情况画出实体-关系模型,然后转换成数据库表结构。实体-关系模型通常包括实体、属性和关系等元素,我们需要将它们转换成表。数据库性能:我们需要考虑数据库的性能,包括表的大小、索引的使用、查询语句的优化等。数据库安全:我们需要考虑数据库安全问题,包括表权限、用户角色设置等。设计原则在设计数据库表结构时,可以参考以下优雅的设计原则:简单明了:表结构要简单清晰,避免过于复杂。一致性:表结构要一致,如命名约定、数据类型等。规范化:尽可能对表进行规范化,避免数据冗余和不一致。性能:表结构要考虑性能问题,如使用合适的索引,避免全表扫描等安全:表结构要考虑安全问题,如合理设置权限,避免SQL注入等可扩展性:表结构要有一定的可扩展性,比如保留字段、可扩展关系等。最后需要提醒的是,优雅的数据库表结构需要在实践中不断迭代优化,不断满足实际需求和新的挑战。下面举个例子,让大家更好的理解表结构怎么设计,内存怎么引入,有哪些优化思路:问题描述如上图所示,红框内的视频放映标签,如何数据库表结构应该如何设计?除了前端筛选,我还想在管理后台支持灵活配置这些过滤标签。这是一个很好的应用场景,大家可以先自己想想。别急着看我的计划。需求分析可以根据红框中的标签过滤视频。综合标签比较特殊,区别于类型、地区、年份、演员等,综合值基于业务逻辑,不需要存储。需要录入类型、地区、年份、演员等在设计图书馆的表结构时,需要考虑:方便获取标签信息,方便缓存和处理标签信息,方便根据标签过滤视频,方便我们编写后续的业务逻辑设计思路。综合标签可以写在配置文件中(或者写在前端),这些信息不需要灵活配置,所以不需要保存在数据库中。为类型、地区、年份和演员设计一个单独的表。在视频表中设计标签表的外键,方便视频列表过滤并将值标签信息写入缓存,完善接口。响应速度类型、地区、年份、演员列表也要支持数据排序,方便后期管理和维护。表结构设计视频表字段评论id视频主键idtype_id类型表外键idarea_id地区表外键idyear_id年份外键idactor_id演员外键id其他和视频直接相关的字段(比如名字)我就省略不写类型表fieldcommentidtypeprimarykeyidnametypenamesortsortfieldregiontablefieldcommentidtypeprimarykeyidnametypenamesortfieldyeartablefieldcommentidtypeprimarykeyidnametypenamesort排序字段本来以为year字段不需要要排序的,要么是年份正序,要么是年份倒序,所以不需要sort字段。仔细看需求,“10s”还是要灵活配置的~castfieldcommentidtypeprimarykeyidnametypenamesortsort字段表结构设计完了,别忘了缓存缓存策略首先这些都不会经常更新过滤条件推荐使用缓存:比较常用的是redis缓存,比较高级。如果使用docker,则可以将这些配置信息写入docker容器所在物理机的内存中,而不是向其他节点请求redis,进一步减少了网络传输对于过滤条件等配置信息的耗时,客户端和服务器端可以约定一种更新缓存的机制,客户端直接缓存配置信息,进一步提高性能。列表数据自动缓存。目前很多框架都支持自动缓存处理,比如goframe和go-zerogaframe可以使用ORM链式操作-查询缓存示例代码:packagemainimport("time""github.com/gogf/gf/v2/database/gdb""github.com/gogf/gf/v2/frame/g""github.com/gogf/gf/v2/os/gctx")funcmain(){var(db=g.DB()ctx=gctx.New())//为To启用调试模式记录所有执行过的SQLdb.SetDebug(true)//写入测试数据_,err:=g.Model("user").Ctx(ctx).Data(g.Map{"name":"xxx","site":"https://xxx.org",}).Insert()//执行2次查询并缓存查询结果1小时,执行缓存名称(可选)fori:=0;我<2;i++{r,_:=g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{持续时间:time.Hour,Name:"vip-user",Force:false,}).Where("uid",1).One()g.Log().Debug(ctx,r.Map())}//执行更新操作并清除指定name_的查询缓存,err=g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{Duration:-1,Name:"vip-user",Force:false,}).Data(gdb.Map{"name":"smith"}).Where("uid",1).Update()iferr!=nil{g.Log().Fatal(ctx,err)}//再次执行查询并启用查询缓存特性r,_:=g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{Duration:time.Hour,Name:"vip-user",Force:false,}).Where("uid",1).One()g.Log().Debug(ctx,r.Map())}go-zeroDB缓存机制go-zero缓存设计持久层缓存官方有详细介绍,不是本文重点。本文首发于我的公众号《如何做好表结构设计?》,引起大家的讨论。也想跟大家分享一下:Q1冗余设计和一致性问题:一张表有这么多外键,如果要查看它们各自的名字,势必要关联4张表。对于这种多外键关联,是否要对这种表进行冗余(直接在主表中对各自的name字段进行冗余)?如果保证一致性,必然会影响性能。如果使用冗余,则无法保证一致性。答:你应该知道文章的上下文。文章要解决的是视频列表筛选的问题。你提到的场景在视频的详细信息中。如果要显示这些外键的名称,如何设计比较好。我的建议是:大家可以根据自己的需要进行适当的冗余。比如你的主表信息量小,配置信息修改后同步修改冗余字段成本不高。或者不做我文章中写的冗余设计,而是将外键信息缓存起来,业务查询从缓存中获取值。或者将视频详情的查询结果整体缓存起来,看具体需求。如果筛选信息不发生变化或者不需要人工管理,甚至设计表,都可以直接写在代码的配置文件中。进一步降低DB压力,提升性能。Q2为什么要设计外键?问题:为什么要设计外键关联?直接写视频表不就可以了吗?这样的设计有什么意义呢?答:关键是要解决管理后台的灵活配置。如果没有这样的需求,我们可以直接将过滤条件以配置文件的形式写在程序中,以降低复杂度。从我的角度来看:这个函数的过滤条件不会有太大的变化,所以我明白你的意思。也建议按照我2.的方案去做,和产品经理聊聊~总结本文介绍了设计数据库表结构应该考虑的4个方面,以及优雅设计的6个原则。举个例子分享下,按照我的设计思路,要想提高性能,还必须从多方面考虑缓存问题。最大的收获就是和大家的交流和讨论。总结一下:首先要先了解业务需求。比如我的例子,如果不需要灵活设置,可以写在配置文件中,不需要单独设计外键。各种筛选标签的名称直接存储在主表中(注意维护问题,必须考虑数据一致性)。数据库表结构设计必须考虑数据量和并发性。在我的例子中,如果数据量较小,可以适当做冗余设计,降低业务复杂度本文是介绍,欢迎大家留言交流。