`

数据库里不要这样做!

 
阅读更多

 

*不要把有意义的列作为表的主键。

    有含义的列,即便不会重复,也不要作为表的主键。

    原因:

    1.主键可能会对应到许多其他表的外键。如果这个有含义的列中的某条数据,因为某些原因需要修改。这样就会导致数据库中的许多其他表中的数据也需要发生修改。

    2.有含义的列中的数据大多数是外部输入。而外部输入有可能会出现失误。从而造成数据会发生重复。

 
 
 
 
【以下转自《三种东西永远不要放到数据库里》】
    我已经在很多演讲里说过,改进你的系统的最好的方法是先避免做“蠢事”。我并不是说你或你开发的东西“蠢”,只是有些决定很容易被人们忽略掉其暗含的牵连,认识不到这样做对系统维护尤其是系统升级带来多大的麻烦。作为一个顾问,像这样的事情我到处都能见到,我还从来没有见过做出这样的决定的人有过好的结果的。
 
*图片,文件,二进制数据
    既然数据库支持BLOB类型的数据,把文件塞进BLOB字段里一定没有错了!?错,不是这样的!别的先不提,在很多数据库语言里,处理大字段都不是很容易。
 
    把文件存放在数据库里有很多问题:
  • 对数据库的读/写的速度永远都赶不上文件系统处理的速度
  • 数据库备份变的巨大,越来越耗时间
  • 对文件的访问需要穿越你的应用层和数据库层
    这后两个是真正的杀手。把图片缩略图存到数据库里?很好,那你就不能使用nginx或其它类型的轻量级服务器来处理它们了。
    给自己行个方便吧,在数据库里只简单的存放一个磁盘上你的文件的相对路径,或者使用S3或CDN之类的服务。
 
 
*短生命期数据
    使用情况统计数据,测量数据,GPS定位数据,session数据,任何只是短时间内对你有用,或经常变化的数据。如果你发现自己正在使用定时任务从某个表里删除有效期只有一小时,一天或数周的数据,那说明你没有找对正确的做事情的方法。使用redis, statsd/graphite, Riak,它们都是干这种事情更合适的工具。这建议也适用于对于收集那些短生命期的数据。
    当然,用挖土机在后花园里种土豆也是可行的,但相比起从储物间里拿出一把铲子,你预约一台挖土机、等它赶到你的园子里挖坑,这显然更慢。你要选择合适的工具来处理手头上的事。
 
 
*日志文件
    把日志数据存放到数据库里,表面上看起来似乎不错,而且“将来也许我需要对这些数据进行复杂的查询”,这样的话很得人心。这样做并不是一个特别差的做法,但如果你把日志数据和你的产品数据存放到一个数据库里就非常不好了。
    也许你的日志记录做的很保守,每次web请求只产生一条日志。对于整个网站的每个事件来说,这仍然会产生大量的数据库插入操作,争夺你用户需要的数据库资源。如果你的日志级别设置为verbose或debug,那等着看你的数据库着火吧。
    你应该使用一些比如Splunk Loggly或纯文本文件来存放你的日志数据。这样去查看它们也许会不方便,但这样的时候不多,甚至有时候你需要写出一些代码来分析出你想要的答案,但总的来说是值得的。
    可是稍等一下,你是那片不一样的雪花,你遇到的问题会如此的不同,所以,如果你把上面提到的三种东西中的某一种放到了数据库里也不会有问题。不,你错了,不,你不特殊。相信我。
 
【以下转自《程序员应该知道的数据库设计的两个误区》】
在几乎所有的企业级应用程序中,包括各种MIS、ERP、CRM等等,都会使用数据库,这样的好处是显而易见的,很容易地实现了数据层和业务逻辑层的分离,而且对于性能的优化也在一定程度上提供了便利。然而,在我所经历过的项目中,某些数据库的设计会存在一些问题,尤其普遍的就是下面将要描述的这两点,个人觉得是应该避免的误区,总结出来与大家讨论。
误区之一 备用字段
现象描述:
在数据表中,不仅设计了当前所需要的字段,而且还在其中留出几个字段作为备用。
比方说,我设计了一个人员表(Person),其中已经添加了各种必要的字段,包括姓名(Name)、性别(Sex)、出生年月日(birthday)等等。大功告成之后,我忽然想到,将来系统中应该还会有很多其它与人相关的内容吧,比方说毕业院校,比方说工作单位等等,尽管现在根本不需要填写,以后可能还是会用到的吧。拍脑袋一项,那就加入5个varchar2型的字段,分别叫做Text1、Text2……Text5,然后又想,应该还有一些日期型的字段需要备用,就又建立了三个date型的字段,分别起名叫做date1、date2、date3,……
原因分析:
大家应该已经看出问题了,在这个数据表中存在大量暂时无用的字段,我们可以称之为备用字段,它们的作用是什么呢?就是以防万一,防备可能的情况。
这似乎可以叫做防患于未然,等到时候需要的时候,就不需要在表中增加新的字段了,而且这样做的话,一个表的数据应该会被存储在相邻的物理空间中,这对于性能也是有好处的。
另外的原因就是,在古老的数据库中,如果改变数据库的定义(包括增加字段、改变字段的类型、删除字段等等),那么其中所有的数据就会丢失,所以这项工作非常麻烦,我们需要先建立临时表,将数据备份出来,然后创建新表,将数据导入其中,最后再删除原来的表。
问题所在:
这样的做法对于项目会导致很多问题,而且原先想要解决的问题并不一定能够解决,不信的话,请往下看。
问题一:增加大量备用字段,必定会浪费很多空间,尽管其中可能都没有具体的数据,但是仅仅是空字段也会占据一定的空间的。
问题二:由于命名的特点,如果没有完善的文档管理流程,用不了多久(可能也就是两三年),就没有人能够说清楚到底哪个字段代表的是什么意义了。就算有文档管理,这些管理工作也会比较麻烦,而且在每次使用的时候都需要申请,还有可能会出现冲突的情况。
问题三:增加了这些备用字段就真的会够用吗?不一定,因为我们只是每个类型的字段留出几个备用,如果数量超过,或者要使用特殊的、不常用的类型的时候,还是需要增加新的字段。比方说在上述的Person表中,我们要存储照片,那么可能就要增加一个blob类型的photo字段,这在初期设计的时候可不一定会留出这样的备用字段。而且如果没有完善的管理,谁又能说清楚倒底哪个字段已经被使用,哪个字段还可以使用呢?到时候还不是要增加新的字段。
解决方案:
其实上面的这种设计方式就是一种“过度设计”,我们应该做的就是“按需设计”,在经过详细有效的分析之后,在数据表中只放置必要的字段,而不要留出大量的备用字段。
当需要增加相关的信息的时候,就要具体情况具体分析:
如果数量很少,而且信息的性质与原表密切相关,那么就可以直接在原表上增加字段,并将相关的数据更新进去。
如果数量较大,或者并非是原表对象至关重要的属性,那么就可以新增一个表,然后通过键值连接起来。
对于表的数据的存储位置所导致的性能问题,我们可以通过在特定时间对数据库的数据进行重组来解决,而这项工作对于长期运行的数据库来说,也是需要定期进行的。
 
误区之二 有意义的编码
现象描述:
使用有意义的编码作为一条记录的ID,甚至作为数据库的主键存在,例如,一个员工的编码设置为0203004,其中02代表员工所在分公司,03代表员工所在部门,004代表员工进入到该部门的序号。
原因分析:
ID的设置方式大概有以下几种,一种是纯粹的流水号,从1开始,每次加1,或者对其将以改进,将数字转换成为字符串的格式,比方说“0000001”;一种是无意义的随机编码,比方说GUID;还有一种就是有意义的编码,特定的位数会代表一定的意义。
我想之所以大家这么喜欢使用这种方式,主要是因为想要从编码中就能够得到一些信息,甚至有些程序中还有专门的对编码进行解析的模块。就像我们的身份证号码一样,看到身份证号就可以知道办身份证时的所在地、生日、性别等信息。
问题所在:
其实有意义的编码会导致很多问题,请看:
问题一:对编码资源的浪费。如果是纯粹的流水号,那么从1到10000就可以代表一万条记录,但是,如果使用有意义的编码,很可能1000条记录就会让五位的编码不够用。我就遇到过真正的情况,我们公司的投保单号码的第一位就是有意义的,代表的时该投保单所属的渠道,后面跟着很长的一串数字(9位)。理论上来说,这些编码永远都不会用完,但是,最开始的三个渠道使用的是1、4、7三个编码,但是一次新保险法的实行,导致原有的投保单作废,于是又启用了三个数字2、5、8,接下来公司改名,三个渠道又分别将投保单报废,重新启用新的开头数字,就这样,短短的几年间,所有的投保单号码全都被用完了,其实打印出来的投保单不过100万张。
问题二:不一定是唯一的,难以作为主键。想一下,我们的身份证号码就是这样的。原先15位的时候,后三位是序号,而男性会使用奇数,女性会使用偶数,这样就是说,一个地区同一天生日的人,男女都不能超过500人,否则就会导致号码的重复,尽管出现这种现象的概率比较低,但是还是客观存在的。
问题三:代表的意义不一定准确。比方说用带有意义的编码来为员工定义工号,其中可能会有部门、职务等等意义,但是如果员工在部门间发生了调动,或者职级发生了改变,是否需要改变他的编码呢?改变吧,那么所有的历史数据都要随之修改一次,工作量会非常大;不改变吧,那么代表的意义就不再准确,我们就无法从编码中得到该员工准确的信息。
解决方案:
所以,对于编码,非常不建议使用有意义的编码,要么使用纯粹的流水号,但这样可能需要定义一个范围比较大的类型,对于海量记录的数据,可能会不够用;那样的话就可以使用GUID,这样编码永远都不会重复,而且会有大量的编码资源可用。
从上面的两点我们可以看出,在数据库设计的过程中,有一些在非常多系统中都使用了,但是却带来了很多问题的方法,对于这种情况,我们就应该仔细思考,然后痛下决心,坚决抵制。
 
 
分享到:
评论

相关推荐

    无数据库日志文件恢复数据库方法两则

    3. **覆盖数据文件**:使用原数据库的数据文件(.mdf)覆盖新数据库的数据文件,这样新数据库就会包含原始数据。 4. **重启SQL Server**:重启服务以使更改生效。 5. **处理置疑状态**:打开企业管理器时,数据库可能...

    数据库建表原则 数据库建表原则

    当 PK 是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空间大,而且速度也慢。 8. 正确认识数据冗余 主键与外键在多表中的重复出现,不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚。非...

    Qt应用程序连接access数据库

    例如,如果你想要从Access表中读取数据并进行转换,可以这样做: ```cpp QSqlQuery query; query.prepare("SELECT * FROM 表名"); if (query.exec()) { while (query.next()) { // 访问查询结果并进行数据转换 ...

    SQL数据库安装教程

    这样做可以防止因C盘问题导致的数据丢失。 5. **创建数据库登录用户** - 在"安全性" > "登录"中,新建一个具有数据库访问权限的用户,设定用户名和密码。 6. **设置权限** - 为该用户分配适当的权限,例如读取、写入...

    jsp连接MySQL数据库

    此外,可以考虑使用连接池(如Apache DBCP或HikariCP)来管理数据库连接,这样可以提高应用的效率和可扩展性。 在实际开发中,你可能还需要处理异常,比如数据库连接超时、无权限等。同时,确保数据库配置信息的...

    Qt连接Mysql数据库驱动

    在网络配置中,可以选择启用TCP/IP连接并设置端口号(默认为3306),这样可以远程访问MySQL数据库,否则只能在同一台机器上访问数据库。 3. Qt连接MySQL数据库驱动编译 最后,需要编译Qt与MySQL数据库之间的驱动。...

    spring 动态切换数据库

    同时,安全地处理数据库凭证,不要在代码或配置文件中硬编码,可以使用环境变量或加密存储。 总的来说,Spring的动态切换数据库功能通过`AbstractRoutingDataSource`和自定义的策略实现了灵活的数据源选择。通过...

    asp读取access数据库生成带图片word.zip

    这篇文档将深入探讨如何使用ASP(Active Server Pages)来读取Access数据库,并生成包含图片的Word文档。在ASP中,我们可以利用ADO(ActiveX Data Objects)库与数据库进行交互,而Access数据库则作为数据源,提供了...

    图片存入sql数据库

    另外,考虑将图片存储在文件系统中,只在数据库中存储文件路径,这样可以减轻数据库的压力。 6. 安全性和备份: 不要忘记对数据库进行定期备份,以防止数据丢失。同时,确保数据传输过程中的安全性,例如使用HTTPS...

    数据库表命名规范

    同时,表名和库名需要用反引号``包含,这样做可以在某些数据库系统中避免解析错误。 综上所述,数据库表命名规范是确保数据库设计质量的基础。遵循这些规范能够有效提升数据库的可维护性、可读性和团队间的协作效率...

    数据库设计实践总结.docx

    之前我觉得这样做没多大意义,由于预留字段的列名是没有实际意义的。这样程序中使用的时候就会让人费解。但现在觉得还是有必要的,很有必要的,即便在用到时需要自己格外清楚之前预留的无意义字段现在表示什么意义。...

    python 读取数据库并绘图的实例

    文章中展示了如何定义一个游标对象并指定参数cursorclass=MySQLdb.cursors.DictCursor,这样做是为了让查询返回的结果集是一个键值数组,而不是默认的元组形式,从而方便后续的数据处理。然后使用cursor.execute(sql...

    java程序读取数据库表 转为sql文件 仅供参考 不得宣传

    在实际操作中,确保数据库访问的安全,避免泄露敏感信息,例如不要在代码或日志中明文存储数据库凭证。 8. **异常处理与资源关闭**: 在程序执行过程中,确保适当地处理可能出现的异常,并在完成操作后关闭数据库...

    java 连接数据库实现用户登录功能

    你可以使用PreparedStatement来执行SQL,这样可以防止SQL注入攻击。例如: ```java String sql = "SELECT * FROM Users WHERE username = ? AND password = ?"; PreparedStatement pstmt = conn.prepareStatement...

    Access数据库修复工具

    Access数据库修复工具是一种专门用于解决Microsoft Access数据库损坏问题的应用程序。在日常操作中,由于各种原因,如系统崩溃、病毒攻击、不正确的关闭或存储媒介损坏等,Access数据库(.mdb或.accdb文件)可能会变...

    数据库基础分享.pdf

    ### 数据库基础知识分享 #### 一、数据库设计总纲 在进行数据库设计之前,需要对整个设计过程有一个清晰的认识。数据库的设计与实现大致分为以下几个阶段: 1. **业务分析** - 对业务需求进行深入分析,理解业务...

    android操作MySQL数据库

    由于Android系统为安全性和性能考虑,不允许直接在应用中运行像JDBC这样的服务器端技术。因此,通常我们会通过网络请求(如HTTP或HTTPS)与后端服务器通信,由服务器处理数据库交互。在这个过程中,Android应用发送...

    图片存进数据库以及读出

    不要忘记在操作完成后关闭数据库连接: ```objc [db close]; ``` 在实际项目中,为了提高性能和用户体验,通常不建议直接将图片存储到数据库。较大的图片数据可能会影响数据库的读写速度。一种更好的方式是将...

    Excel数据库创建和调用

    最后,不要忘记保存你的工作簿,这样就完成了数据库的基本创建过程。 **额外提示(Office 2003及以上版本)** 如果你使用的是Office 2003或更高版本,可能会有一些额外的操作步骤。这些步骤可能涉及到了解如何更好...

    oracle数据库的启动和关闭

    建议在数据库启动前执行`ALTER DATABASE BACKUP CONTROLFILE TO TRACE`命令,生成最新的CREATE CONTROLFILE命令的跟踪文件,存储在`USER_DUMP_DEST`指定的路径,这样可以确保在需要时有最新的控制文件创建指令。...

Global site tag (gtag.js) - Google Analytics