来自:http://www.cnblogs.com/nankezhishi/archive/2012/02/12/2348091.html#2617778
故事的起源
作为软件工程专业出身的程序员,之前所接受的关于数据库的教育都是基于关系型数据库。对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用在员工作业平台或是项目管理系统上我觉得就相当的不靠谱。
但是电商系统似乎是处于两者之间的一种模式,而且我们的业务量在可预见的未来也不会增长到淘宝的那个级别。我和头的争论也就源于这些理念上的差别,他像是在建立大统一理论,把各种先进思想整合在一起,而我又不确定这是创新呢?还是不伦不类的方式?于是有了题目上的疑问。而按头的说法,也许这就是思考问题层次上的差别。
我在数据库设计上并没有什么经验,却又做不到别人说什么我就听什么。上面只是我根据对于数据库自身的理解产生的一些想法,还很不成熟,希望大家能说说自己的见解。如果实在看不下去了,也欢迎吐槽。
posted on 2012-02-12 17:20 南柯之石 阅读(3151) 评论(29) 编辑 收藏
评论
#1楼 2012-02-12 17:55一味
#2楼[楼主] 2012-02-12 18:21南柯之石
这代价是不是有点儿大了。
这样的话,每个平台特有特性是不能用了。我还没有做过迁移平台的事情,但存储过程和触发器这种东西并不是每个平台特有的东西,大家都有。应该不会给迁移带来多大麻烦,即使语法上略有不同,写在存储过程的sql可以很容易用工具进行格式转化,但是写在代码里的sql语句就没有办法了。
#3楼 2012-02-12 18:25Stephen_Liu
#4楼[楼主] 2012-02-12 18:35南柯之石
#5楼 2012-02-12 18:42bluce chen
#6楼 2012-02-12 19:15Stephen_Liu
没有ORM,我们是用C++进行开发的,操作数据库也是直接使用数据库提供的C接口API。sql语句完全在代码中,代码中有相应的class来适配不同的DB。存储过程并不是每一个DB都有的,比如老版本的MysqL、Postgresql和现在的sqlite。因为我们的客户可能已经有了自己的DB,我们的产品不能强制要求客户进行更换,特别是一些日本客户,他们都比较谨慎,所以只能是我们的产品提供这样的支持了。
#7楼 2012-02-12 20:23午后的小睡
#8楼 2012-02-12 23:07pulihe
#9楼 2012-02-13 00:26不若相忘于江湖
在访问量大的系统,不同意使用外键,我认为是性能问题,如果是内部软件,则可以,
2 没有使用ORM,需要在C#代码中拼sql来实现业务对象的读取和写入。
我们现在的做法也是一样,拼sql,不过之前会有相应的安全过滤机制。
3 不允许使用存储过程。这会引入过多的业务逻辑。
我们现在也是不能使用存储过程,理由嘛,有些人写习惯了,会经常把业务逻辑也写到里面去的,维护起来很吃力。
4 我也没见过任何View,任何Trigger,任何自定义函数。
Trigger除非非常有必要,否则坚决不用。
#10楼 2012-02-13 01:47Gu
1 只有主键,没有外键,也就是数据库基本无从保证数据的一致性。
方便扩容和copy数据
2 没有使用ORM,需要在C#代码中拼sql来实现业务对象的读取和写入。
用orm对性能没太大影响,还是应该用的
3 不允许使用存储过程。这会引入过多的业务逻辑。
你只要维护过一堆存储过程的系统,你就知道这个苦啊...
4 我也没见过任何View,任何Trigger,任何自定义函数。
尽量不要用trigger和自定义函数. 但view我还是很喜欢用的.
#11楼 2012-02-13 08:21Kain
2、ORM么,什么都有两面性,ORM牺牲了部分性能来换取开发的效率,就看系统中这种牺牲是否值得了。
3、SP么,现在基本不推荐使用了,能用业务层处理的尽量不用SP干,哪怕效率低上很多,毕竟DB的扩展实在是太难了。
如果使用了过多的数据库的特性(存储过程,触发器等),会将我们的系统架构局限于该数据库平台,对于项目还好,对于产品来说,可能是一种风险。特别是在将来可以预见到有迁移数据库平台的可能的时候。