[转]后端开发如何设计数据库
设计传统系统表结构(Java开发)
以前经常能够看到,数据库范式,现在说数据库三大范式的少了。
三大范式我以前也很严格的弄过,但是后来发现,还是灵活好啊,为什么,业务变动太快了啊,按照范式来,结构变更顶不住。
下面我就说一说设计数据库表要注意的一些地方吧。我不是DBA,只是Java后端开发,以下是根据我的个人经验所得,至于能不能体会,看个人了。
外键、触发器
外键、触发器不要有。
有了外键、触发器,你会发现: 写代码不方便。 订正数据不方便。 迁移数据也麻烦。 总之,你要是坚持用,后续的坑等着你。
自增id
数据库表,一定要有id,而且要用自增id!
有些人喜欢用自定义的,用UUID或者其他七七八八的id,如果在架构设计,代码比较好的情况下,不会出啥大问题,但是一旦代码写的不行,极有可能就造成id重复之类的问题。
自增id另外还有一个好处,就是在数据迁移的时候,分页查询通过id来进行分页,速度会比传统分页快很多。
创建时间&修改时间
创建时间和修改时间这两个字段,每个表都要有! 注意,一定要用数据的时间戳,自动生成。不要通过代码去操作这两个字段。
有了这两个字段。你可以追溯到数据的时间点,创建和修改的时间点。极大方便你在某些情况下的排查数据问题。
创建人&修改人
建议每个表也有这两个字段。
还是和前面一个原因,出问题的时候可以追溯起因,否则遇上日志过久无法查看或者其他原因出现未知数据,都不知道数据怎么来的,需要花非常大的代价查看日志、代码等。
大文本字段
一列需要占很大空间的字段,一定要单独拎出来,不要和常用信息放一张表。
举个例子: 文章的信息和文章的内容,这一定要分成两个表。否则会给你的文章性能带来极大的挑战。因为很多情况下,查看文章列表,根本不需要查看到文章的内容。
表与表的关联
表与表之间的信息,用id进行关联,尽量不要有冗余的信息数据,否则你需要更新同一份信息的时候,需要更新多个地方。
但是在某些情况下,你确认信息不会经常变动,且该信息确实在两个表中都有会比较好,那么,放心的去冗余吧。但是注意,数据的更新用上事务。
单库单表单系统写原则
这个原则我自己想出来的,也就是说,数据库中的一张表,只能有一个系统对其进行写操作。 其他的系统如果想写这张表,那么经过调用这个系统的接口进行操作。
如果有多个系统写同一张表,可能带来的问题会很多。首先就是数据并发问题,其次就是事务问题,还有就是表结构变更问题,数据来源追溯问题等等。
如果谁有一张表数据想用多个系统来进行写,那肯定是想把团队拖垮。时间越久,债务越多!
总结
数据库设计,标准其实是在变的,不变的只是思想。
业务场景不同,实际需求不一样,不会存在一样的设计,但是通用的思想都是一样的,为业务服务,为未来服务,方便维护,方便扩展。
这条路很长,只能慢慢在路上体会了。
设计大数据量表结构
在大数据量的情况下,如何去提前设计这个表结构,来达到一个比较好的效果。对于团队,对于后续的维护和扩展都带来更大的便利。
自增id
自增id还是可以有,但是不是必须的了。但是建议还是每张表中有一个自增id。 为什么,还是那句话,做数据查询,迁移,排序的时候,有着天然的一些优势。
唯一标识
这个标识无论是token,还是其他例如订单的订单号或者其他唯一标识都行。 重点是唯一,不只是在单系统中唯一,而是需要在并发的情况,也能够保持唯一。
关于分布式id的生成方案,网上已经有很多了,这里就不重复了。谷歌搜索搜索,自己看下原理,跑跑demo,能够满足自己业务的最大并发情况下的唯一即可。
比如说你未来几年的最大并发也就是100,搞个能支持在几千并发下不会出现标识重复的实现方案即可,并发几万,几十万,真的需要吗?
后续如果真的能够达到几万并发,那说明什么?说明业务火爆了啊。难道还抽不出时间,抽不出人来做一个id生成方案的改造?这里有比较重要的一点,不要太过超前设计,没必要,也没那个时间。
创建时间&修改时间
创建时间和修改时间还是要有的,而且建议时间精确到毫秒级别,在上一篇,我没有说精确多少,那是因为并发不高,秒级完全够了。但是在大数据量的情况下,可能一秒有几十、几百、上千、上万的数据新增都是有可能的。那么秒级在这种情况下完全就不够看了,选择毫秒级别是一个比较好的选择。
分库分表
前面的唯一标识/创建时间可以说就是为了这步准备的。
但是怎么来设计分库分表,选择什么方式,范围还是hash,选择哪个字段,还是选择几个字段。平滑迁移还是停机迁移。
这些都没有唯一答案。只能是根据场景来区分不同的情况。下面举几个例子来进行一个讲解,不是标准答案,同一个场景为了满足不同的需求,也可能有不同的一个设计。
1:支付订单的场景
例如,订单每日新增千万级,那么在这个情况下。我们还需要区分一下。
1.1 订单号包含了时间戳
那么强烈建议按照时间维度进行分库分表。 也强烈建议在订单号中将时间戳放进去。
优点:
单表的大小是可以预知的,一天多少订单量,一个月多少订单量
非常便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加库表即可,不需要对已经存在的分片数据进行迁移。使用订单号进行范围查找时,可以快速定位查询,避免了跨片查询的问题。
缺点:
最近的订单会存在着热点数据,可以通过其他方式进行解决,例如缓存等
1.2 订单号不包含时间戳
不包含时间戳,你可以选择创建时间来做范围分片。 或者使用订单号来做hash分片,也就是取模运算分片。
hash分片的缺点就是后期扩容会涉及到老数据的迁移,但是现在有一种方案可以避免该缺点,那就是使用虚节点,先占位,但不使用,需要的节点需要是2的次方个才行。大家可以去网上了解一下,这里就不展开了。 另外一个缺点就是跨片查询的性能问题,当查询条件中没有订单号的时候,会无法定位到数据库表,所以会遍历所有的库表,进行查询,再在内存中合并数据,取最小集返回,在这种情况下,分库分表反而会成为累赘。
其他一些分库分表带来的事务问题大家可以看看现在的一些分布式事务解决方案,都还挺不错的。阿里的Seata可以了解一下。
最后,分库分表,并不是一定要分库的,也可以只分表,这样很多分库分表的问题就不存在了。 分库分表还是要跟进实际的数据增长速度来评估,比如说,每年数据才几十万或者百万,那么没有必要进行一个过渡设计,单表即可。等数据库到了瓶颈,可以再考虑优化。
很多时候,瓶颈也不一定是在数据库。
性能优化
在这里,对于性能优化提一句,因为自己也刚完成一个性能优化的需求不久,提升性能2倍左右。这次优化完全没有动数据库。 主要优化点在:同步方法异步调用第三方服务、计算异步处理、批量单次调用、部分不变数据缓存 重点:拿资源(空间、线程)换时间
总结
当数据量大了之后,其实很多设计和传统的数据库还是没有很大变化的。
主要是要考虑到数据量大之后,该表如果分库分表,那么怎么设计更加合理一点,也许当下不需要分库分表,但是可以给以后少埋点坑。
但是注意,还是那句话,不要过度设计,也不要不去设计。简单的说,可以预估到以后的业务每日数据量新增是万级几十万以上的,就可以考虑下以后的分表,但是当期并不需要做。 但如果是日增百万千万级别,那么这个分库分表肯定是当期就需要进行的。 假如是日增几百几千的表,那么就不要花过多时间去考虑什么分库分表的方案了,真的用不上。
建议的一个提前思考时间,1年左右的思考维度设计比较好。即不会超前,也不会因为迭代了一两次业务就有人提出,不知道哪个**设计的结构,又得重新来设计。
最后,能够在技术方案时就明确的事情,绝不留到写代码的时候再去明确! 技术方案越清晰(注意:不是说超前设计,这里面有个度,只可意会不可言传),写代码越轻松,团队协作越流畅。
设计SaaS系统表结构
在公司做了一年的SaaS内核系统,但是有些东西不知道能不能透露出来。我尽量在不透露一些敏感东西的情况下(这个度我无法把控,只能是笼统了),将某些关于数据库方面的精髓传递出来。如果表达不畅,请谅解。
前面的两篇讲解了在传统系统和大数据量下的数据库设计应该注意的事项。
接下来需要换一种思路,在SaaS系统中,数据库应该如何进行设计。
与传统开发的思考点不同,在SaaS中,可能更多考虑的是数据隔离(在这里考虑共享数据库,共享数据表),数据通用方面
租户id
既然是SaaS平台,那么肯定是多租户的一个生态。那么在数据库层面,一定要有一个字段来隔离数据。
这个字段在每个表都需要有,且每一次数据库的操作都需要有该字段作为条件。
这一步数据安全的控制都放在了代码中,所以安全性和隔离性都是要依赖编码的。
表的自增id
这个字段还是要有的,但是强烈建议不要在删除行数据,查询数据,修改数据时使用到该字段,因为该字段的单独操作会破坏掉数据的隔离性。也就是前面所说的,所有的sql操作,都要带上租户id再进行。
数据来源标识
作为SaaS平台,在很多情况下,不同的租户可能有一样的数据,或者是通过某些编程,或者是通过配置的方式,通过一套标准数据生成了各个租户的数据。可以实现租户的自定义。
但是在某些情况下,可能某个特性不需要租户进行自定义,而是SaaS系统进行一个控制,那么就需要一个标识,来知道这个数据来源一致。
需要确定的是,这个标识,在这个租户下是唯一的。也就是说,前面的自增id没有用,但是这个标识和租户的id是可以唯一索引到一条数据的。
强烈建议使用租户id和数据的来源标识进行操作数据。
元数据
元数据用一句话就是描述数据的数据
在SaaS中,存在着一些通用配置,通过这些通用配置,可以自由定义一些业务模型,可以极其快速的实现业务需求。
这个元数据,我正在公司负责这块,但是可能不能透露。
简单的说几句,元数据能用非关系型数据库就不要用关系型数据库。前期量级小的时候问题,后面访问量,压力很大。
其次,通用的一些业务模型,一定要抽取出来。元数据对业务来说是极其基础的存在的,业务不应该感知到元数据的存在。业务感知的应该是元数据的一些业务组合,也就是业务模型。
通过组装业务模型,可以配合不同的业务场景,最后实现业务功能。
租户数据
租户的数据,是基于一些元数据来生成的,所以是可扩展的。在这里,也建议不要使用关系型数据库,因为不太适合,非关系型数据库更加适合SaaS系统这个体系。
其实东西很多,但是暂时先讲到这了,我也不知道某些东西是不是属于公司的,前些日子我们公司刚爆出了员工透露公司机密到网上,所以。。。
最后讲下吧,如果要做SaaS系统,一定要考虑长远,不要先被业务拖累。如果在半年一年内无法脱离业务需求来架构设计与开发SaaS系统,那么我的建议是,不要做什么SaaS了,开发业务吧,不然公司都活不下去的。
整篇看上去会比较理论,但是实际上,这些都是实践后的一些理论点。有很多的一些东西,我无法分享太多,非常抱歉。这次也是借这个阿里云活动的机会,分享一点出来。