2009年6月22日星期一

web2py 正式支持 order by desc

昨晚做gastinus的消息编写页,发现orderby="post_on desc"居然出错,进去gluon里的源码一看,跟以前不一样了(刚更新了服务器代码)。于是搜索了一下文档,发现关于orderby的文档更新了,现 在DAL的orderby不再支持"fieldname desc"的形式,而是通过=~实现同样的功能。例如,gastinus的发言页面,需要按post_on倒排,应该写成

db().select(db.quotation.ALL, orderby=~db.quotation.post_on)

这样确实比以前可读性更好,而且因为逻辑实现在代码而不是字符串里,更利于开发工具实现错误检查。

另外,GQLQuery似乎没有limit,只有limitby=(lmin, lmax)这样一种写法。其实GQL本身的limit语法与关系型数据库的普遍实现并无二致,不明白为何不统一。

2009年6月3日星期三

[Web2py]Web2py DAL 与 Postgres数据库

在诸多Python 数据访问框架中,Web2py的DAL算是比较有趣的一个。因为工作的关系,最近比较关注DAL与Postgres的组合。这里简单总结一下。
开头又臭又长,建议跳过,留着娱乐时间再读。直接从下一集开始阅读

数据存储与访问——理想与现实的妥协
在IT界,从来没有一个系统边界可以像数据库与应用层之间这样不断摩擦,冲突激烈,没推倒前的柏林墙庶几近之。不同的是同胞之间的藩篱终于在两德人民之间轰然倒下,而数据库与应用层的斗争只会光华灿烂,鸡飞狗跳。
鹅,写错了,是光华灿烂,旦复旦兮。几年不读圣贤书,已经赶不上语文课代表啦。
是的,应用层与数据访问层的关系,只会在冲突和妥协中不断发展和变化,周而复始。
为了吸引用户,满足需求,数据库开发者总是在努力丰富服务器端的功能。从Key/Value到层次存储,网状关联,再到平面表,再到关系数据库,再到复杂数据类型和过程化编程,再到事务、存储过程、甚至嵌入式混合编程,以及咸鱼翻生的MapReduce,千奇百怪,不一而足。(当然,类似分布式存储,裸设备访问这些NB的性能优化技术和存储技术也在不断发展,但这不是今天我们要讨论的问题。)
而为了吸引用户,降低学习成本,提高占有率,应用层开发者总是在努力提高代码的通用性。于是,在第一代数据库SDK直连的理念后,顺理成章的出现了ODBC这样的通用驱动桥接层。由于各种各样的原因,ODBC始终也没有实现一统天下的目标,JDBC等平台专用链接库层出不穷,就连老东家微软都忍无可忍推出了ADO和ADO.NET,于是,这一层的访问方式基本稳定在了一种运行时一个主流库的态势上,Java是JDBC、.net是ADO.net,Perl是DBX,Python统一这一层的是DBAPI。
但是,驱动桥接是面向数据行的,对于编程并不方便,需要应用开发程序员大量编写重复代码,手工维护连接,遍历得到的游标记录集等等,这甚至对于数据库服务器也不够友好。于是,我们看到,ADO.net中开始强烈的倾向一种自动生成对象结构的自动化编程趋势,而这个思想在Java中最先成为一个诱人的产品——Hibernate,这就是ORM。
其实大家都明白ORM付出了比较高的性能和功能代价,但是ORM对生产力的提升是不可抵挡的诱惑,各个平台纷纷陷落,还出现了不同的框架自己实现ORM的场面,Python尤其明显,这对于忠诚的"统一标准"信徒,简直是一件无法无天大摇其头的荒唐事。
于是,现在对于数据存储访问,就出现了一个相对比较稳定的分层,最下面是RDBMS以及其它数据库,不过这里我们只讨论最主要的RDB应用;上一层是各种桥接层,现在的趋势是每种数据库针对每种运行时提供自己的桥接层,以达到最优的功能和性能接口,而过去由ODBC、BDE、OLE DB这些系统桥接工具提供的通用性和移植性的黑锅,就由Hibernate们背了起来。
也许Python的生产力真的是太高了,以至于即使出了几种广受好评的ORM(例如SQLObject、SQLAlchmy、Storm)之后,Django、Web.py、Web2py等web框架还是实现了自己的ORM,这其中有些是在通用ORM上实现的,有些则是框架直接基于数据库访问接口组建的。而Web2py的DAL(Database Abstraction Layer),算是其中相当有趣的一个。

DAL的取舍
如同前面所介绍的,为了使得ORM组件吸引更多的用户,获得更大的通用性,通常都会牺牲数据库层的功能,作为一个年轻的ORM工具,DAL只能做到对最常见,也最简陋的MySQL和SQLite为目标进行了支持。事实上,即使MySQL的编程语法,也做不到完全的支持(至少,MySQL的全文检索就没有支持)。调侃一下,我印象比较深刻的数据库,如果编程能力打个分,大概是下图这个样子。
http://picasaweb.google.com/March.Liu/Blogger0=2#5343071442111853506
忘了加sqlite了,不过那东西也就是个40分的样子。而DAL能直接支持的,也就在这个40分的程度上。
不幸的是,我工作中用到的,恰恰是那个编程能力最强的,在我看来可以给100分(倒不是说就是满分)的PostgreSQL。
有功能不用,倒是不一定就会有什么不好,但是如果可以充分了解ORM层和DB的功能特性,就可以尽可能的发挥这两层工具的能力,提高工作效率和产品质量,精益求精永远都是一件好事。
这里简单介绍几个DAL及相关的技术特色,具体的技术细节凡是可以直接在文档中查询到的,这里就不细讲了。

数据库连接定义

DAL用来连接数据层的,是DB,对应通常的关系型数据库,是SQLDB对象,对应GAE Bigtable,则是GQLDB,这个对象是DAL整个访问操作的核心。当然,一个web2py实例内可以同时存在几个DB对象。
首先DAL的构造接口非常简单,只需要一个URI格式的字符串。例如,对于astinus项目的企业版,这个uri是postgres://astinus:march@127.0.0.1/astinus,包含了用户名、口令、服务器种类、位置、数据库名。虽然比起逐个参数进行指定的方式略为复杂,但是由于格式非常符合我们对于web url的使用经验(其实是现有uri的概念,才有了url这个具体的东西),所以非常好记。DAL支持的完整的DB列表及连接方式,可以在DAL的文档 http://web2py.com/examples/default/dal 中读到。
另外DAL针对一些数据库,例如Postgres,还提供了一个可选参数:pools,也就是数据连接池。有兴趣的朋友可以在glon/tools.py中找到这部分代码。
当然,DAL的连接定义也不是完美的,这里主要问题表现在两个方面:
  • 一定要指定password,有些数据库允许密码之外的身份验证方式,这样可以提供更可靠的安全性,但是DAL目前还只能使用密码验证。
  • 不能指定schema。对于PG和Oracle,不得不说是个遗憾。其实实现schema支持并不是一个很麻烦的事情,Web2py社区早有用户提过一些方法,我自己也想到过几个,例如采取类似trac DB URI的n参数方式就不错。目前我对此采用了一个妥协的方法:设定连接用户在数据库环境中的的search_path变量。这样就允许用户访问postgres数据库中public之外的schema,以及可以指定schema搜索顺序。
数据库结构定义与对象映射

DAL通过调用define_table方法,可以定义数据表,表结构中的field字段由SQLField对象构造。这种结构使得DAL可以定义丰富的字段约束信息,DAL提供以下特色功能:
  • 假设有SQLDB(或GQLDB对象)对象db,通过db.table或db['table']就可以访问指定的表table,再进一步可以访问表中的字段:db.table.fileld。这样就可以用应用层程序员习惯的对象结构构造查询和访问数据
  • DAL的SQLField对象可以提供非常丰富的数据约束条件,通过SQLFORM对象,可以快速构造带有页面、应用服务器、数据库三层数据验证约束的数据访问层次。
  • DAL可以同步数据库结构,也可以通过migate=False禁止这中同步。
  • Web2py提供内置的Database Administrat 界面。
同样,DAL的数据库结构定义功能也有一定的不足:
首先,同样是schema问题
  • postgres提供强大的text类型(不同于MySQL等数据库中常见的Text类型),但是在DAL中string无法充分利用这一功能。
  • postgres提供一个基本功能的XML类型,支持xpath 1.0和dom访问,但是同样DAL无法利用。
  • DAL定义的表结构一定要带有一个整数自增字段列作为主键,这一点不如django灵活。
  • DAL定义的表结构,外键定义非常简单好用,但是也不够灵活,不能指定非主键列(可以放在验证中,这样可以看作是实现了外键约束)。
  • DAL可以支持导出数据,但是不如Django按对象导出那么强大。
  • DAL的pg实现使用char(1)作为逻辑类型,而PG自带了真正的boolean。
  • DAL无法映射存储函数,而这时PG中非常强大的一个功能。
基于以上原因,我总是手工在数据库中建立表结构和相关对象,并且在define_table中设定关键字参数migrate=False。
DAL 的检索

DAL的数据检索(对应SQL select)做的很有意思。它可以支持:
  • 局部化选取,只读取表中一部分字段。
  • 支持连接查询,甚至支持左外连接,外连接在一些ORM工具中没有提供。
  • 由上两个技术,DAL可以支持相当灵活的查询集组合。
  • 子查询。
  • 统计计算,而且这个统计计算是基于SQL生成和惰性计算的,将计算放在服务器端,这是一个非常好的特性,而且使用起来很友好。
  • 服务器端排序和分页访问。得益于新SQL标准的关键字limit,DAL可以比较方便的提供分页查询功能,不过这个功能在MySQL上性能很糟糕(就海量数据集的场景而言),这个黑锅我想不能由DAL背。
  • 将查询集直接生成为多种输出,如XML、XMLRPC、CVS、JSON等。或者生成为SQLFORM以供web请求使用。

DAL的检索基本上是如下格式:
  • db(查询条件).select(字段列表,**{附加参数})
这是一个很经典的MapReduce格式
  • db(filter).select(map, reduce)
当然,在select函数中,也包含了个别filter操作(limit),这可以看作是对SQL语法的一种妥协。
同样,DAL查询也存在一些问题:
  • DAL不支持PG的全文检索运算符@@。
  • DAL不支持PG的正则表达式运算符~和~*。
  • DAL不支持FULL Join和Right JOIN,当然,这并不是很大的问题。
  • DAL不支持跨db对象的联接,即使它们指向同一个数据库。这一点我认为是合理的。
  • DAL查询集不支持完整的序列运算,不能切割,不能反向索引,这一点我认为是一种遗憾,应当改进。
我们这里不详解DAL的用法,有兴趣的朋友可以详细阅读DAL的文档,了解各种查询的使用方式。

DAL 的update、insert、delete操作

DAL也同样支持局部化的update/insert操作,这同样是很多ORM做不到的。在数据写操作时,DAL也有一些有趣的功能实现:
  • DAL允许 data={...}这样的局部化批量赋值,而db.person[0]=dict(name='Max') 则是一个非常巧妙的插入操作(DAL的id是从1开始计数的)。
  • DAL允许用Python的 del 关键字删除数据记录。
  • DAL的db对象支持commit和rollback。
  • DAL增删改查都可以方便的cache。
  • 可以从CSV批量导入数据。
  • 内置的curd辅助工具。
  • 可以方便的生成多种数据提供或RPC服务。
同样,我认为DAL在此方面也有一些地方有待改进:
  • ADO.net 支持定义增删改查对应的存储过程,DAL不提供此类功能。
  • DAL不能像Django一样提供基于业务对象的数据导入(XML或JSON格式)。
  • 仿dict接口的快捷访问非常漂亮,但是受限于id列,不能像django的get方法或ado.net的command.ExecuteScala(方法名记不清了)r一样直接构造单个对象选取操作。
进一步的,DAL虽然为db对象提供了executesql方法,但是却没有提供参数传入功能,也是一个比较明显的遗憾。

  • DAL的发展与讨论
我认为,针对DAL的PG应用,期待以下功能的实现:
  • 无id列,用户自定义主键的表结构,至少应该支持uuid。
  • 可以方便的指定schema范围或直接引用。
  • 更方便的外键定义。
  • 支持函数,最好可以自定义返回集结构,可以select query。
  • 至少应该提供参数化SQL执行功能。
  • 基于业务模型的数据迁移能力。
  • 更友好和强大的数据库管理工具。
  • 更充分和合理的利用PG的数据类型,如text和boolean。
  • 支持复杂数据结构,如数组、几何类型等。

--
杀人放火金腰带,补路修桥无尸骸。

……

劉鑫
March.Liu

2009年5月31日星期日

Astinus 文档格式

Astinus 文档格式

Astinus 项目需要支持相对复杂的通用文档格式,要能够描述多种格式化或带有元信息的文档。因此,需要约定一种通用文档格式。

数据格式

基本格式

Astinus文档的基本单位为条目,使用XML格式存储。根元素为 <entry id='uid'/> 。

文档条目的主体为 xpath:entry/content 在正文中允许嵌入元信息节点。

<entry/> 节点下允许加入其它节点,提供map类型的属性映射。

content 的 map 属性通过 xpath:entry/content[@*] 设置。

<entry/> 节点下不写入text属性

<cotent/> 节点下的信息是有序的

允许在 <entry/>内部嵌入<entry/>,或在 xpath:entry/content 内嵌入 <entry/>

数据退化

没有附加属性的情况下,可以没有<entry/>节点,根直接为 <content id='uid'/>

可以以XML字符串格式传递,默认的头是 <?xml version="1.0" encoding="utf-8"?>

数据表现层或新建文档输入项可以不提供uid,这样数据结构进一步退化为 <entry><content></content></entry>

JSON 转换

Astinus 文档允许表达为JSON 格式,也接受JSON格式的数据转入

JSON 格式的根是{id:'uid', cotent:[]},

数据表现层或新建文档输入项可以不提供uid,这样数据结构最大可退化为 [...]

content序列内,以字符串序列表达正文内容,序列元信息通过文本元素间嵌入的{}元素表达。

设计原则

Astinus 文档格式的设计遵循如下的原则:https://bitbucket.org/March/astinus/wiki/edit/AstinusDocumentFormat

  • 对应用层编程友好,尽可能支持通用编程接口。
  • 对存储层友好,容易存储和优化性能,允许在数据库端编程管理。
  • 自描述,在离开服务器支持的环境下可以解读
  • 非SQL表格,支持条目的非结构化字段表述
  • 允许文本化,添加全文索引支持
  • 尽可能少的保留字
  • 允许DAL信息的使用

Astinus 的文档处理环境遵循如下的原则:

  • 在应用层保持文档数据完整性
  • 允许提取元信息进行计算
  • 允许元信息编程
  • 允许版本化支持(考虑并发处理)
  • 允许解释DAL和嵌入式编程


--
杀人放火金腰带,补路修桥无尸骸。

……

劉鑫
March.Liu

2009年5月25日星期一

[web2py]DAL中隐藏的秘密:limit和orderby desc

web2py 有所有新兴开源项目的通病:文档落后于代码。
例如,你在document下的DAL相关文档里,只能读到orderby的用法,但是没有告诉你如何desc。在该文档中也找不到limit的介绍。
其实这两个功能都很重要,分页几乎是现代数据库应用中必备的功能,limit关键字就是为此而诞生的。而对于不能任意split(我查了web2py的源码才发现小于0的索引SQLROWS直接抛异常),又不能直接reverse的DAL数据集,倒排查询就是很重要的功能了。
其实这两个功能DAL都有。
oderby的支持看来开发人员还没有找到很好的形式,例如,我们可以 orderby=db.ta.id,但是没有一个descorderby=db.ta.id,不过其实我们可以 orderby="ta.id desc",这样看起来有点怪,但是可用,而且不算很难理解。
limit关键字参数的支持就自然很多,类似标准SQL,DAL的limit支持两种形式:
limit=n,取数据集前n项。
limit=(n,m)从m项起取前n项,等效于PG的limit n offset m
--
杀人放火金腰带,补路修桥无尸骸。

……

劉鑫
March.Liu

web2py 的自定义路径

Web2py不同于django和web.py的一个特色,它自然的就是restful的形式。当然这也造成了一定的限制,就是我们有时候不能方便的自定义请求的路径。
其实Web2py一样可以自定义请求路径。只是它默认情况下没有给出这个配置。简而言之,在web2py的根目录下有一个 routers.examples.py,只要看那个文件的内容,就可以参照着写出自己的routers.py了:)。它采用的是类似django和 web.py的正则表达式重定向形式。
--
杀人放火金腰带,补路修桥无尸骸。

……

劉鑫
March.Liu

web2py 的远程开放

web2py是一个有趣的框架,它的特色之一就是在页面上集成了一个简单的开发环境。但是这个前提是只能从127.0.0.1访问,否则会提示你"不是安全通道"。
那么,是不是就没有办法把代码集中部署到一个远程服务器上开发了?
后来,我在web2py的官网上搜索到一个方法,,就是通过ssh映射远程端口到本地,可以实现远程开发。例如,我在192.168.0.13主机上run了一个web2py示例:
python web2py.py -i 0.0.0.0

那么我就从本机:

ssh -L 8000:127.0.0.1:8000 192.168.0.13

登录之后,如果再从浏览器访问 http://127.0.0.1:8000 ,打开的就是远程主机的回环IP。此时,就可以对远程的站点项目进行开发了。由于Web2Py提供了编辑冲突保护,甚至我们可以用这种方法进行团队集中式开发。

当然,我还是建议尽可能走成熟的版本管理系统进行开发。一个比较好的模式是每个团队成员在本机有一个开发环境,在测试服务器上有一个集中的环境,成员通过分布式版本管理系统——例如Python和OpenJDK项目使用的mercurial,或web2py和ubuntu项目使用的bzr——与测试服务器上的中心仓库同步,就可以实现比较理想的写作效果。更严格的管理模式,可以使用一个SVN,然后对可集成的分支挂一个hook,使其可以在有用户commit后自动发布到测试/生产服务器。

在这种情况下,通过ssh远程进入web2py的开发环境,也可以帮助我们获取错误信息,仍然是一项很有意义的技巧。
--
杀人放火金腰带,补路修桥无尸骸。

……

劉鑫
March.Liu

2009年5月13日星期三

数据库设计建议,范式以及进一步

几乎每一个新人在初学关系型数据库设计的时候,都会接触到关系范式。但是,我还是见到了大量很离谱的设计。客观的说,背下关系范式,离一个合格的数据库设计师还差很远。设计工作总是在理想与现实之,规范与工艺之间妥协。建筑如是,造船如是,操作系统设计如是,数据库设计亦如是。
是的,你记得范式,你还记得反范式建议。你知道范式减少冗余,提高一致性;你还知道反范式可以方便编程。不幸的是,最终的结果总是遵守范式的做法使自己的应用层代码混乱,而反范式的企图使得数据库也陷入混乱。
这是谁的错?
不必太自责,设计工作是一个经验的积累过程。没有人天生就会做设计。天才与勤奋,是乘法关系。并不是你笨,只是天才对面的那个系数还不够大而已。
以下的一些经验,或许在你读完关系范式以后,可以抽空看一看 。世上没有魔法,读完这篇文章,并不会立即让你拥有多年设计经验。但是,这些在设计工作中积累的经验教训,应该可以帮助你少走一些弯路。

关于范式

关系范式并不邪恶,也不要把它想得太神秘,如果书本上的定义不能让你很快理解,不妨试着回答以下的问题:
字段还可以再分吗?分成两个或更多的字段以后,还能不能表达完整的含义?
字段的值是不是有限的几个离散的状态?
两个或若干个字段,能不能提取出来建立为一个数据字典?
如果表中某个字段依赖其他表,被依赖的字段是不是唯一的(最好是主键)?
查询中是否会出现超过两个表的Join?

将数据库设计与系统设计结合起来

数据库设计并不是一个孤立的过程,整个软件生命期中,各方面的工作应该有机结合。这方面我觉得ACCP过去的教材讲得还不错,至少思路是对的:
在做需求分析的时候,做Use Case。此时可以分析出应用层的功能接口,对于数据库的实体分类可以有一个大概的划定。例如,这个项目会需要一个工作流,这个项目会需要一个订单系统,或者一个文档库,等等。通常,每个子系统可以对应一个
在做概要设计的时候,出类关系和ER简图。通常来说,此时不能确定所有的字段,但是会有哪些表,有哪些主外键依赖,有哪些地方应该需要存储过程和触发器的辅助,等等。
详细设计时尽可能将数据库结构完全固定。尽管现代开发工具不断提升XP能力,重构越来越简单。数据库的重构仍然是一件牵一发而动全身的事情,毕竟数据库是信息存储的根本。大厦楼顶加个小花园容易,把地基下面的承重柱子拔出来换两根试试?

重视SQL

近年来ORM发展很快,几乎每个框架都要提供这个功能,以至于会有些菜鸟认为“ORM”会淘汰SQL语言。
这是一块试金石,如果你有这样的感觉,应该考虑认真评估一下自己在这个领域是不是太菜了。
SQL不是一种编程语言这么简单,SQL代表的是一种与应用开发语言完全不同的思想。面向集合,过程无关,着眼于规则定义。可以说,SQL是FP High Order计算的最成功应用,也可以说,SQL是一种静态强类型的MapReduce语言。
看,换上时髦的名词,会不会让你觉得它上等起来了?
在应用层语言惨烈竞争的同时,SQL语言压倒了同时代出现的其他关系型数据库操作语言,在这个拥有巨大利润的领域占据了绝对统治地位。即使桀骜不驯的Postgres,也在1995年变身为PostgrSQL。这一过程,并非像VC淘汰BC那么多盘外招,而是长时间争议与选择的结果。
对于信息操作规则定义,SQL几乎是最好的表达方式。接近自然语言,高度可读,并且非常利于优化。
打个比方,一个基于过程语言的上帝,这样说:
* 构造一个光源对象
* 构造一个能源对象
* 调用光源对象方法,设置能源
* 调用光源对象的发光方法,传入照明范围内的对象列表
基于SQL的上帝说,要有光。
当然,在这位老兄背后,要有打杂的小弟去完成插电点灯的事情,但是作为上帝,什么活都自己做了,要天使干什么?
看看那些应用层语言的list comprehensions(列表推导式)。不止一次我想要为Python实现一个基于存储层的列表推导式实现,都可耻的失败了。
当然,我承认这跟跟人能力有关,我不是Gudio。
看看LINQ,不管如何吹嘘,它就是一个抽象出I/O的SQL。我见过一些人激烈的贬低SQL,抬高ORM,同时又对LINQ顶礼膜拜,这可真够分裂的。
ORM对应用层编程效率的提升是客观的,无需回避。但是随着你数据操作越来越精细和复杂,就越来越需要通过规则定义来抽象High Order I/O过程。当你转了一圈儿回来,会发现自己又在写SQL。
想想Hibernate的HQL,想想C#的LINQ。
计算机不会变魔术。想让它做事更聪明,就需要你这个驭者更加聪明才行。
好的工具和方法可以给你带来更高的能力系数,但是记住,一个乘法计算,仅有一头大是不够的。
不懂SQL的人,是不能驾驭好ORM的。

与ORM做朋友

ORM对于开发工作,无疑是有好处的。我的朋友沈葳说,人脑能组织和分析的事务是有限的,所以代码越短,越有利于提高代码质量。从这个角度讲,ORM是非常重要的开发工具,其意义不亚于C API 函数集到GUI 框架的进步。
要想让ORM充分发挥威力,有时候需要从数据库设计时就做出一定妥协。
例如,你往往会需要加入自增标识列,会放弃一些精巧但是不利于ORM访问的依赖设定,甚至要放弃一些漂亮的命名(它们在应用层语言中是保留字,但是你用的ORM不懂如何规避)。
但是,这往往是必要的。就像建筑师向气候和建筑材料妥协一样。
在ORM默认的自增字段外,也许你还需要基于业务规则的唯一约束,那么额外加索引。
好的ORM会帮助你方便的查询数据字典,生成对象映射,跟踪数据变更,提供数据完整性的应用层检查,构造两阶段提交事务,减少不必要的I/O。
同样,不懂得运用ORM,也可能会破坏数据完整性,降低数据访问速度,甚至造成数据库死锁。作为项目开发人员,应该将ORM视为朋友而不是负担。

合理分层

过去,流行使用复杂的数据库设计,将业务规则存储于数据库的存储过程。现在,又流行抛弃数据层的一切约束,所有的规则都放在应用层。
这两者都不合理,除了应用需求的影响,前者与Oracle的广告部宣传有关,后者与MySQL阵营的鼓动有关。背后都有一些不合理的力量推动。
每一层应该保证自己的完整性,这才是分层的意义。那么,在数据库层,应该保证数据的完整性。
数据库备份出来,再恢复进去,应该可以得到所有的业务信息。
直接向数据库导入数据,应该可以有完整的数据规则保护。
数据库里保存的,不仅仅是表和记录,应该是完整的持久性信息。
从这个角度讲,配置文件和应用层代码中不应该有任何业务数据定义,这些信息都应该是数据字典表。如果出现了这种配置文件,大多数情况下都是愚蠢的错误。
实际上,包括Web网站常见的附件上传,都应该保存在数据库中。
独立的I/O文件存储、包括将外键约束转移到应用层,往往是因为对性能的妥协。以及,这里面确实存在MySQL阵营在推广过程中的一些不道德的宣传。
有效利用数据库功能,可以提高应用层的开发速度,简化代码结构,使得数据存储更安全。这通常仰赖与设计人员的经验,根据项目的具体需求进行调整。
基于这个原则,合理利用数据库功能,编写存储过程,触发器,调校索引,都是必要的。
我敢打赌,随着MySQL实现越来越多的功能,它的宣传材料上会越来越多的出现以前被MySQL所摒弃的复杂设计理念,并且宣称这是MySQL所独创或一贯倡导的。

收集整理常见的模式

在设计模式提出这么多年,在关系型数据库问世如此之久后,我很惊讶的一件事就是数据库设计模式仍然是一个相当冷门的领域。实际上,关系数据库的模式也有很多可循之规。例如用户信息(HR或CRM)、工作流,权限管理(如RBAC),订单等等,都有相当成熟的行业经验和时间,往往只要修改一些字段名,或者在关键架构的基础上加以扩展,就可以很好的用于实践。
每一个有志于成为高水平设计人员的开发者,都应该积极的收集自己体会到的数据库设计模式,积极的与同行交流。
这方面,Oracle的示例Schema,Postgres的示例数据库项目(在Soureforge上可以找到),都是很好的例子。相对来说,微软在MSSQL和Access中提供的示例库更为轻量和简单,也是作为入门的不错借鉴。