作为软件工程专业出身的程序员,之前所接受的关于数据库的教育都是基于关系型数据库。对key-value based数据库和document-based数据库的都只是仅仅了解而已。
最近公司要做一个类似电商的系统,我来负责数据库的设计和接口的提供。当然,我们使用的数据库也是传统的关系型数据库SQL SERVER 2005,所以我也并没有什么太大压力。
但是当头儿看到我设计的分类系统的数据库表结果时意见很大,一翻沟通下来,我基本上就崩溃了。他的一翻话基本上推翻了我对数据库的基本认识。
我的设计
要实现的分类是书籍分类体系,会至少有4层,总共大约有几百个分类。我很自然地想到了邻接表(Adjacency List)模式。然后设计了的表结构大体如下:
列名 CategoryId ParentId Name 描述 主键 父键 分类名当然还有别的字段,与问题无关就不列了。
使用CTE递归的方式来获取一个分类下的子分类树。我考虑数据量并不大,所以性能上应该不成什么问题。
和头儿的争论头儿看到我的设计,直接说,不用这么麻烦了,不是有4层吗?那就用4列,一列表示一级。
我说国图法分类可能有7层以上。他说那就7列好了。
我说这样数据量会有大量冗余,更新起来会很麻烦。
头儿说,我之前做过很多树型结构,一开始也和你一样。但是做着做着就发现查询继承关系好麻烦啊,还不如直接用列存储下来。
我说查询继承的代码我已经写好了,用CTE递归我并不觉得麻烦啊。
头儿说,不要用递归,我们要为读优化,而不是为写优化。
这个我完全赞同,但是这个数据量并不大,但是我还是觉得这个做法很山寨。不符合数据库的基本范式啊。
他说你知道范式,也要了解反范式。而且这个数据库本身不要包含业务逻辑。你看现在出来的Redis和MemeryCached,存的都是名值对儿,没有逻辑。逻辑是给业务处理的。什么是程序?程序就是算法加数据,数据和逻辑是要分开的。
我说从属关系也是逻辑吗?数据一致性也是逻辑吗?
应该算啊,数据库就是负责存储数据的就够了,你自己好好想想吧。
之后我还是坚持了自己的做法,头儿勉强接受,但是要多加一个Path列,存储这个分类的所有父节点。
这个我倒是同意加上,这属于为特定查询做的优化。
背景和现状我进这个项目的时候,这个项目的数据库基本上已经建立完了。现在只是由于业务扩展需要新建数据库表。刚进公司的时候,我就觉得这里的数据库用法很诡异。
- 只有主键,没有外键,也就是数据库基本无从保证数据的一致性。
- 没有使用ORM,需要在C#代码中拼SQL来实现业务对象的读取和写入。
- 不允许使用存储过程。这会引入过多的业务逻辑。
- 我也没见过任何View,任何Trigger,任何自定义函数。
现在发现这些现状与头儿的想法其实是比较一致的。但是给人的感觉就是把SQL SERVER当作一个Key-Value的Database来实用。自然也就不需要那些SQL SERVER的功能。
但是我很奇怪,这样的用法,为什么不直接用No SQL方案呢?不用也就算了,更让我困惑的地方就在于,把No SQL的理念用在关系型数据库上合适吗?
我在这里也没有看到过任何数据库性能Profiling的代码,对于数据库性能的考量,基本上依赖数据库服务器的CPU占用。(也许有测试人员做过我不知道而已吧。希望是这样。)我们仅有的一名DBA也于年前离职了。
我的想法我一直觉得数据库设计是一项很重要很复杂的工作,需要了解业务需求,需要了解用户的使用方式,需要能够遇见到基本的可变性,需要对数据库底层机制有深入的了解,等等等等。也听过很多数据库高手改写一段SQL,就让数据库查询变得飞快的故事。而且一切优化都应该有实际的数据来说话。也许我们头儿在数据库设计上有相当的功力,很有感觉。但是真的是这样的吗?我有我自己的想法,不知道是不是正确,但希望能和大家讨论一下。
关于反范式和优化反范式作为一种数据库优化方案,我并觉得和其它的优化有什么根本上的不同。但是从优化的次序上来讲,我个人觉得应该先在查询的SQL上做足功夫,不行了再祭出反范式这个以空间换时间的杀手锏。不应该是从数据库设计一开始就惦记着的事情,尤其是这还是在关系型数据库上。当然,资深人员一眼就能感觉出哪里需要这么干当然更好。不过对于上面的情况,我个人觉得不需要。
关于数据库不能有逻辑这句话首先需要澄清什么属于逻辑。由于这是我们头的观点,我也没有跟他详细讨论过这个问题。我来讲下我的理解吧。数据库在设计之初就是为某个业务设计的,脱离了这个业务,这个设计本身也没有了意义。所以我不认为逻辑从数据库分离能让数据库可以被重用。但是数据库包含了过多的逻辑会增加数据库服务器的负担,所以逻辑还是少放在数据库为好。所以我基本同意这句话。
但是,我认为。数据关系(如外键)不属于逻辑,数据完全性不属于逻辑。而且这些就应该主是由数据库来保证的。Web 服务器可以去检查外键,检查唯一约束,但是即使Web服务器不做这些,数据库也应该做到这些。
最常见的就是数据插入和更新,如果涉及到多张表,那这些SQL写入一个存储过程以方便外界调用是很好的。同时,存储过程或是View也可以把数据库表设计与程序之间的依赖解除,在一些简单的情况下实现不修改程序而对数据库表结构进行变更。使用存储过程也许会给数据迁移带来麻烦,但是有谁没事儿换数据库呢?
我们目前这种在程序中拼SQL去操作数据库的做法,我没有看到什么优势。纯粹就是为了“数据库不能有‘逻辑’”而拆分出来的东西。
关于数据库系统的选用我并没有反对NoSQL或MemeryCached的理念,毕竟是用于处理不同的情况问题的。
像微博、社交、互动相关的系统。因为数据量大,访问量大,数据结构相对简单,用户访问具有时效性、地域性,使用这种NoSQL+MemeryCache方案是合理甚至是唯一的选择。但是任何产品或是理念都有它适用的范围,没有银弹。把 MemoryCache用在员工作业平台或是项目管理系统上我觉得就相当的不靠谱。
但是电商系统似乎是处于两者之间的一种模式,而且我们的业务量在可预见的未来也不会增长到淘宝的那个级别。我和头的争论也就源于这些理念上的差别,他像是在建立大统一理论,把各种先进思想整合在一起,而我又不确定这是创新呢?还是不伦不类的方式?于是有了题目上的疑问。而按头的说法,也许这就是思考问题层次上的差别。
我在数据库设计上并没有什么经验,却又做不到别人说什么我就听什么。上面只是我根据对于数据库自身的理解产生的一些想法,还很不成熟,希望大家能说说自己的见解。如果实在看不下去了,也欢迎吐槽。