转载自:https://blog.csdn.net/haiross/article/details/50427382html
1. 原始单据与实体之间的关系 java
能够是一对1、一对多、多对多的关系。在通常状况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。注视:mysql
图书 1 和 该实体取名为“借还书” n
读者 1 和 该实体取名为“借还书” n
7. 主键PK的取值方法
PK是供程序员使用的表间链接工具,能够是一无物理意义的数字串, 由程序自动加1来实现。也能够是有物理意义
的字段名或字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引
占用空间大,并且速度也慢。
8. 正确认识数据冗余
主键与外键在多表中的重复出现, 不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚。非键字段的重
复出现, 才是数据冗余!并且是一种低级冗余,即重复性的冗余。高级冗余不是字段的重复出现,而是字段的派生出现。
〖例4〗:商品中的“单价、数量、金额”三个字段,“金额”就是由“单价”乘以“数量”派生出来的,它就是冗余,
并且是一种高级冗余。冗余的目的是为了提升处理速度。只有低级冗余才会增长数据的不一致性,由于同一数据,可
能从不一样时间、地点、角色上屡次录入。所以,咱们提倡高级冗余(派生性冗余),反对低级冗余(重复性冗余)。
9. E--R图没有标准答案
信息系统的E--R图没有标准答案,由于它的设计与画法不是唯一的,只要它覆盖了系统需求的业务范围和功能内容,
就是可行的。反之要修改E--R图。尽管它没有唯一的标准答案,并不意味着能够随意设计。好的E—R图的标准是:
结构清晰、关联简洁、实体个数适中、属性分配合理、没有低级冗余。
10 . 视图技术在数据库设计中颇有用
与基本表、代码表、中间表不一样,视图是一种虚表,它依赖数据源的实表而存在。视图是供程序员使用数据库的
一个窗口,是基表数据综合的一种形式, 是数据处理的一种方法,是用户数据保密的一种手段。为了进行复杂处理、
提升运算速度和节省存储空间, 视图的定义深度通常不得超过三层。 若三层视图仍不够用, 则应在视图上定义临时表,
在临时表上再定义视图。这样反复交迭定义, 视图的深度就不受限制了。
对于某些与国家政治、经济、技术、军事和安全利益有关的信息系统,视图的做用更加剧要。这些系统的基本表完
成物理设计以后,当即在基本表上创建第一层视图,这层视图的个数和结构,与基本表的个数和结构是彻底相同。
而且规定,全部的程序员,一概只准在视图上操做。只有数据库管理员,带着多我的员共同掌握的“安全钥匙”,
才能直接在基本表上操做。请读者想一想:这是为何?
11. 中间表、报表和临时表
中间表是存放统计数据的表,它是为数据仓库、输出报表或查询结果而设计的,有时它没有主键与外键(数据仓
库除外)。临时表是程序员我的设计的,存放临时记录,为我的所用。基表和中间表由DBA维护,临时表由程序员
本身用程序自动维护。
12. 完整性约束表如今三个方面
域的完整性:用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Check按钮,通
过它定义字段的值城。
参照完整性:用PK、FK、表级触发器来实现。
用户定义完整性:它是一些业务规则,用存储过程和触发器来实现。
13. 防止数据库设计打补丁的方法是“三少原则”
(1) 一个数据库中表的个数越少越好。只有表的个数少了,才能说明系统的E--R图少而精,去掉了重复的多余的
实体,造成了对客观世界的高度抽象,进行了系统的数据集成,防止了打补丁式的设计;
(2) 一个表中组合主键的字段个数越少越好。由于主键的做用,一是建主键索引,二是作为子表的外键,因此组
合主键的字段个数少了,不只节省了运行时间,并且节省了索引存储空间;
(3) 一个表中的字段个数越少越好。只有字段的个数少了,才能说明在系统中不存在数据重复,且不多有数据冗
余,更重要的是督促读者学会“列变行”,这样就防止了将子表中的字段拉入到主表中去,在主表中留下许
多空余的字段。所谓“列变行”,就是将主表中的一部份内容拉出去,另外单独建一个子表。这个方法很简
单,有的人就是不习惯、不采纳、不执行。
数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。“三少”是一个总体概念,综合观点,
不能孤立某一个原则。该原则是相对的,不是绝对的。“三多”原则确定是错误的。试想:若覆盖系统一样的功
能,一百个实体(共一千个属性) 的E--R图,确定比二百个实体(共二千个属性) 的E--R图,要好得多。
提倡“三少”原则,是叫读者学会利用数据库设计技术进行系统的数据集成。数据集成的步骤是将文件系统集成
为应用数据库,将应用数据库集成为主题数据库,将主题数据库集成为全局综合数据库。集成的程度越高,数据
共享性就越强,信息孤岛现象就越少,整个企业信息系统的全局E—R图中实体的个数、主键的个数、属性的个数
就会越少。
提倡“三少”原则的目的,是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据库变成了随意
设计数据库表的“垃圾堆”,或数据库表的“大杂院”,最后形成数据库中的基本表、代码表、中间表、临时表
杂乱无章,不可胜数(即动态创表而增长表数量),致使企事业单位的信息系统没法维护而瘫痪。
“三多”原则任何人均可以作到,该原则是“打补丁方法”设计数据库的歪理学说。“三少”原则是少而精的
原则,它要求有较高的数据库设计技巧与艺术,不是任何人都能作到的,由于该原则是杜绝用“打补丁方法”
设计数据库的理论依据。
14. 提升数据库运行效率的办法
在给定的系统硬件和系统软件条件下,提升数据库系统的运行效率的办法是:
(1) 在数据库物理设计时,下降范式,增长冗余, 少用触发器, 多用存储过程。
(2) 当计算很是复杂、并且记录条数很是巨大时(例如一千万条),复杂计算要先在数据库外面,以文件系统方
式用C++语言计算处理完成以后,最后才入库追加到表中去。这是电信计费系统设计的经验。
(3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的作法是,以该表主键
PK的某个值为界线,将该表的记录水平分割为两个表(便可以表维护 表行数过大 手动分割为两个 建个两表union的视图对程序透明)。若发现某个表的字段太多,例如超过八十个,则
垂直分割该表,将原来的一个表分解为两个表。
(4) 对数据库管理系统DBMS进行系统优化,即优化各类系统参数,如缓冲区个数。
(5) 在使用面向数据的SQL语言进行程序设计时,尽可能采起优化算法。
总之,要提升数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化,这三
个层次上同时下功夫。
程序员
上述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的。对于这些经验的运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握。并逐步作到:在应用中发展,在发展中应用。算法
转载自:http://www.javaeye.com/topic/281611
sql
--------------------------------------------------------------------------------------------------------------------------数据库
转载自:https://www.cnblogs.com/RunForLove/p/5693986.html数组
数据库设计的三大范式:为了创建冗余较小、结构合理的数据库,设计数据库时必须遵循必定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须知足必定的范式。缓存
在实际开发中最为常见的设计范式有三个:第一范式是最基本的范式。若是数据库表中的全部字段值都是不可分解的原子值,就说明该数据库表知足了第一范式;第二范式在第一范式的基础之上更进一层。第二范式须要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不能够把多种数据保存在同一张数据库表中;第三范式须要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。总结一下,就是:第一范式(确保每列保持原子性);第二范式(确保表中的每列都和主键相关);第三范式(确保每列都和主键列直接相关,而不是间接相关)。安全
在目前的企业信息系统中,数据库仍是最佳的数据存储方式,虽然已经有不少的书籍在指导咱们进行数据库设计,但应该那种方式是设计数据库的表结构的最好方法、设计时应听从什么样的原则、四个范式如何可以用一种方式达到顺畅的应用等是我一直在思考和总结的问题,下文是我针对这几个问题根据本身的设计经历准备总结的一篇文章的提纲,欢迎你们一块进行探讨,集思广益。其中提到了领域建模的概念,但未做详细解释,但愿之后可以有时间咱们针对这个命题进行深刻探讨。
1.不该该针对整个系统进行数据库设计,而应该根据系统架构中的组件划分,针对每一个组件所处理的业务进行组件单元的数据库设计;不一样组件间所对应的数据库表之间的关联应尽量减小,若是不一样组件间的表须要外键关联也尽可能不要建立外键关联,而只是记录关联表的一个主键,确保组件对应的表之间的独立性,为系统或表结构的重构提供可能性。
//注意他这里说的是"不要建立外键关联",建立外键关联的语句是: //foreign key(member_id) references member (id); //咱们几乎没有用到这条语句,由于咱们就是这样作的,用到外键时,只是记录关联表的主键,而非在数据库级别上建立外键。 //也不知道是歪打正着,仍是前辈DBA过于强大,已经考虑好了。
2.采用领域模型驱动的方式和自顶向下的思路进行数据库设计,首先分析系统业务,根据职责定义对象。对象要符合封装的特性,确保与职责相关的数据项被定义在一个对象以内,这些数据项可以完整描述该职责,不会出现职责描述缺失。而且一个对象有且只有一项职责,若是一个对象要负责两个或两个以上的职责,应进行分拆。
// 领域模型驱动的方式,目前用的还不是很熟,考虑的不够多。由于常常的数据库中的表只是拿来作存储用而已, //特别是小需求,要加什么字段,找到相关表加上去就好了,不太考虑领域模型。这个在中文站老业务表里很常见
3.根据创建的领域模型进行数据库表的映射,此时应参考数据库设计第二范式:一个表中的全部非关键字属性都依赖于整个关键字。关键字能够是一个属性,也能够是多个属性的集合,不论那种方式,都应确保关键字可以保证惟一性。在肯定关键字时,应保证关键字不会参与业务且不会出现更新异常,这时,最优解决方案为采用一个自增数值型属性或一个随机字符串做为表的关键字。
4.因为第一点所述的领域模型驱动的方式设计数据库表结构,领域模型中的每个对象只有一项职责,因此对象中的数据项不存在传递依赖,因此,这种思路的数据库表结构设计从一开始即知足第三范式:一个表应知足第二范式,且属性间不存在传递依赖。
//数据库三范式记不得的同窗去查资料温习一下。 //我的认为第三范式的目的是尽可能减小数据冗余,保证相同的数据只存在一份。 //第三范式其实咱们遵照的并非很严格,特别是老的数据库表中会有冗余字段。这个要看状况决定吧。
5.一样,因为对象职责的单一性以及对象之间的关系反映的是业务逻辑之间的关系,因此在领域模型中的对象存在主对象和从对象之分,从对象是从1-N或N-N的角度进一步完善主对象的业务逻辑,因此从对象及对象关系映射为的表及表关联关系不存在删除和插入异常。
//最后一句看不懂,多是"因此表及表关联关系不该该出现删除和插入异常。"?
6.在映射后得出的数据库表结构中,应再根据第四范式进行进一步修改,确保不存在多值依赖。这时,应根据反向工程的思路反馈给领域模型。若是表结构中存在多值依赖,则证实领域模型中的对象具备至少两个以上的职责,应根据第一条进行设计修正。第四范式:一个表若是知足BCNF,不该存在多值依赖。
//第四范式咱们遵照的并很少吧。 //例如: //VAS_WP_CONFIG.config_name字段的值包括:adv(广告主题)/glare(炫彩滚动主题)/theme_simple(普通主题)/theme_cartoon(动画主题)/ theme_none(不显示背景主题) //cate_background(类目背景)/video(公司视频)/board_cartoon(动画招牌)/board_simple(普通招牌)等。 //若是遵照第四范式,则须要新增一张VAS_WP_CONFIG_NAME表,存储配置名称枚举值,而VAS_WP_CONFIG.config_name字段改成VAS_WP_CONFIG.config_name_id。 //这样作更利于扩展,不会由于每一个人的理解不一致而向VAS_WP_CONFIG.config_name字段里设置乱七八糟的值,可是这样须要维护更多的小表,形成数据值表的数量膨胀,DBA可能会以为管理上有更多的困难。 //咱们采用潜规则约定、java枚举类等其它方式来进行保证。但有时候效果并非很好,常常发现旧数据库表中枚举字段的值五花八门,不全是约定的。
7.在通过分析后确认全部的表都知足2、3、四范式的状况下,表和表之间的关联尽可能采用弱关联以便于对表字段和表结构的调整和重构。而且,我认为数据库中的表是用来持久化一个对象实例在特定时间及特定条件下的状态的,只是一个存储介质,因此,表和表之间也不该用强关联来表述业务(数据间的一致性),这一职责应由系统的逻辑层来保证,这种方式也确保了系统对于不正确数据(脏数据)的兼容性。固然,从整个系统的角度来讲咱们仍是要尽最大努力确保系统不会产生脏数据,单从另外一个角度来讲,脏数据的产生在必定程度上也是不可避免的,咱们也要保证系统对这种状况的容错性。这是一个折中的方案。
8.应针对全部表的主键和外键创建索引,有针对性的(针对一些大数据量和经常使用检索方式)创建组合属性的索引,提升检索效率。虽然创建索引会消耗部分系统资源,但比较起在检索时搜索整张表中的数据尤为时表中的数据量较大时所带来的性能影响,以及无索引时的排序操做所带来的性能影响,这种方式仍然是值得提倡的。
//索引目前都是DBA根据具体的SQL来建立的,不过开发写SQL时,也应该适当考虑一下字段的索引。
9.尽可能少采用存储过程,目前已经有不少技术能够替代存储过程的功能如"对象/关系映射"等,将数据一致性的保证放在数据库中,不管对于版本控制、开发和部署、以及数据库的迁移都会带来很大的影响。但不能否认,存储过程具备性能上的优点,因此,当系统可以使用的硬件不会获得提高而性能又是很是重要的质量属性时,可通过平衡考虑选用存储过程。
//目前都是杜绝使用存储过程的,我以为用起来比较方便,对于咱们来讲,主要缘由是会给DBA带来管理方面的麻烦, //由于时间一长,存储过程的逻辑和使用场景,每每没人能了解,容易产生更多问题
10.当处理表间的关联约束所付出的代价(经常是使用性上的代价)超过了保证不会出现修改、删除、更改异常所付出的代价,而且数据冗余也不是主要的问题时,表设计能够不符合四个范式。四个范式确保了不会出现异常,但也可能由此致使过于纯洁的设计,使得表结构难于使用,因此在设计时须要进行综合判断,但首先确保符合四个范式,而后再进行精化修正是刚刚进入数据库设计领域时能够采用的最好办法。
11.设计出的表要具备较好的使用性,主要体如今查询时是否须要关联多张表且还需使用复杂的SQL技巧。我感受遵照的范式越多,就越使SQL复杂,具体状况具体分析。设计出的表要尽量减小数据冗余,确保数据的准确性,有效的控制冗余有助于提升数据库的性能
所以,考虑了以上条件以后,表设计约定规则以下:
//规则1:表必需要有主键。 //规则2:一个字段只表示一个含义。 //规则3:老是包含两个日期字段:gmt_create(建立日期),gmt_modified(修改日期),且这两个字段不该该包含有额外的业务逻辑。 //规则4:MySQL中,gmt_create、gmt_modified使用DATETIME类型。 //规则5:禁止使用复杂数据类型(数组,自定义类型等)。 //规则6: MySQL中,附属表拆分后,附属表id与主表id保持一致。不容许在附属表新增主键字段。 //规则7: MySQL中,存在过时概念的表,在其设计之初就必须有过时机制,且有明确的过时时间。过时数据必须迁移至历史表中。 //规则8: MySQL中,再也不使用的表,必须通知DBA予以改名归档。 //规则9: MySQL中,线上表中如有再也不使用的字段,为保证数据完整,禁止删除。 //规则10: MySQL中,禁止使用OCI驱动,所有使用THI驱动。
关于MySQL的部分学习笔记总结:
1、事务跟存储引擎
1.四种事务隔离级别:read uncommited, read commited(大多数db默认的),repeatable read(mysql默认), seriazable。
2.mysql是默认的auto commited, 也就是说每次查询默认都是自动提交的(show variables like 'autocommited')。mysql能够经过set transaction isolatioin level命令来设置隔离级别,例如:set session transaction isolation level read commited。
3.mysql中像innodb采用mvcc(多版本并发控制)来处理并发。mvcc只工做在read commited,repeatable read这两种事务隔离级别上。read uncommited隔离级别不兼容mvcc是由于在该级别得下的查询,不读取符合当前事务版本的数据行,而是最新版本的数据行。seriazable隔离级别不兼容MVCC,由于该级别下的读操做会对每一个返回行进行加锁。
4.选择存储引擎,并发选用myisam,事务选择innodb,myisam比innodb更容易出错,出错了恢复的时间也比较长。只有myisam支持全文检索。
5.把表从一种存储引擎转到另外一种引擎:
// 1. alter table mytable engine=falcon; 操做费时,可能会占用服务器的全部i/o处理能力。 // 2. create table innodb_table like myisam_table; // alter table innodb_table engine=innodb; // insert into innodb_table select * from myisam_table;
2、数据类型
1.尽量的要把field定义为Not NULL, mysql比较难优化使用了可空列的查询,它会使索引,索引统计更加复杂。可空列须要更多的存储空间,还须要mysql内部进行特殊处理,当可空列被索引时,每条记录都须要一个格外的字节。 即便要在表中存储"没有值"的字段,考虑使用0,特殊字段或者空字符串来代替。
2.datetime与timestamp能保存一样的数据:精确度为秒,可是timestamp使用的空间只有datetime的一半,还能保存时区,拥有特殊的自动更新能力。可是timestamp保存的时间范围要比datetime要小得多。mysql能存储的最细的时间粒度为秒
3.mysql支持不少种别名,如bool,integer,nummeric.
4.float与double类型支持使用标准的浮点运算进行近似计算。 Decimal类型保存精确的小数,在>=mysql5.0,mysql服务器自身进行了decimal的运算,由于CPU不支持直接对它进行运算,因此慢一点。
5.mysql会把text与blob类型的列当成有实体的对象来进行保存。他们有各自的数据类型家族(tinytext,smalltext,text,mediumtext,longtext; blob相似); mysql对blob与text列排序方式和其余类型有所不一样,它不会按照字符串的完整长度来排序。而只是按照max_sort_length规定的若干个字节来进行排序。
6.采用enum来代替字符串类型。mysql在内部把每一个枚举值都保存为整数。enum在内部是按照数字进行排序的,而不是按照字符串。enum最很差的就是字符串列表是固定的,添加和删除必须使用alter table。
7.ip地址,通常会采用varchar(15)列来保存。事实上,IP地址是个无符号的32位整数,而不是字符串。mysql提供了inet_aton()和inet_nota()函数在证书与ip地址之间进行转换。
3、索引
1.汇集索引不只仅是一种单独的索引类型,并且是一种存储数据的方式。Innodb引擎的汇集索引实际上在一样的结构中保存了B-Tree索引和数据行。当表有汇集索引时,它的数据行实际上保存在索引的叶子上。注意是存储引擎来实现索引。
2.myisam与innodb数据布局:myisam索引树(不管是主键索引仍是非主键索引)叶子节点都是指向的数据行,而innodb中汇集索引,主键索引树叶子节点就带得有数据的内容,而非主键索引树中叶子节点指向主键值,而不是数据的位置。
3.mysql有两种产生排序结果的方式:使用文件排序,或者扫描有序的索引。目前只有myisam支持全文索引。
4.myisam表有表级锁;myisam表不支持事务,实际上,myisam并不保证单条命令完成;myisam只缓存了mysql进程内部的索引,并保存在键缓存区内。OS缓存了表的数据;行被紧密的保存在一块儿,磁盘上的数据有很小的磁盘占用和快速的全表扫描。
5.innodb支持事务和四种事务隔离级别;在mysql5.0中,只有innodb支持外鍵;支持行级锁与mvcc;全部的innodb表都是按照主键汇集的;全部索引(出开主键)都是按主键引用行;索引没有使用前缀压缩,所以索引可能比myisam大不少;数据转载缓慢;阻塞auto_increment,也就是用表级锁来产生每一个auto_increment。
4、MYSQL性能分析
1.mysql提供了一个benchmark(int 循环次数,char* 表达式); 能够分析表达式执行所花时间。 例如:
// select BENCHMARK(10000,SHA1('aaaaaaaaaaaaaaaa'))
2.mysql有两种查询日志:普通日志和慢速日志。
5、MYSQL高级特性
1.在mysql中,只有myisam存储引擎支持全文索引。myisam全文索引是一种特殊的具备两层结构的B树。
2.存储引擎事务在存储引擎内部被赋予acid属性,分布式(XA)是一种高层次事务,它能够历哟内部个两段提交的方式将acid属性扩展到存储引擎外部,甚至数据库外部。阶段1:通知全部提交者准备提交 阶段2:通知全部参与者进行真正提交。
3.mysql 的字符集和校对规则有 4 个级别的默认设置:服务器级、数据库级、表级和字段级。Mysql4.1 开始支持 SQL 的子查询。
/******************************************/ /* 数据库全名 = degopen@10.218.249.92:3318【mysql】 */ /* 表名称 = task_new */ /******************************************/ CREATE TABLE `task_new` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `task_name` varchar(128) NOT NULL COMMENT '任务名称', `image` varchar(128) DEFAULT NULL COMMENT '任务图标', `description` varchar(1024) NOT NULL COMMENT '任务描述', `content` varchar(1024) NOT NULL COMMENT '任务内容', `finished_message` varchar(128) DEFAULT NULL COMMENT '任务完成提示信息', `task_scope` int(11) NOT NULL COMMENT '任务范围, 0-平台任务, 1-游戏任务', `series_task` int(11) NOT NULL DEFAULT '0' COMMENT '任务类型: 系列任务,单独任务', `task_type` int(11) NOT NULL DEFAULT '0' COMMENT '任务类型: 固定任务, 推广任务, 平常任务', `pre_task` varchar(128) DEFAULT NULL COMMENT '前置任务', `post_task` varchar(128) DEFAULT NULL COMMENT '后置任务', `task_status` int(11) NOT NULL COMMENT '任务状态, 待审核、未开始、生效中、已暂停、已完成、审核未经过', `auto_task` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否手动任务, 0-否, 1-是', `is_required` tinyint(4) NOT NULL COMMENT '是否必须任务', `event_type` varchar(64) DEFAULT NULL COMMENT '关心的事件类型', `task_target` bigint(20) DEFAULT '0' COMMENT '任务目标', `reset_num` int(11) NOT NULL COMMENT '重置次数', `reset_cycle` int(11) NOT NULL COMMENT '重置周期', `task_interval` int(11) NOT NULL COMMENT '任务间隔', `xiaoer` bigint(20) unsigned NOT NULL COMMENT '建立人', `review_id` bigint(20) unsigned NOT NULL COMMENT '审核人ID', `last_start_time` datetime DEFAULT NULL COMMENT '上次生效时间', `gmt_create` datetime NOT NULL COMMENT '建立时间', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `start_time` datetime NOT NULL COMMENT '开始时间', `end_time` datetime NOT NULL COMMENT '结束时间', `start_condition` varchar(1024) NOT NULL COMMENT '任务触发条件', `end_condition` varchar(1024) NOT NULL COMMENT '任务完成条件', `enable` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可用', `rule` varchar(4096) NOT NULL COMMENT '任务规则', `priority` int(11) NOT NULL DEFAULT '1' COMMENT '任务优先级', `progress_rule` varchar(2048) NOT NULL DEFAULT '' COMMENT '进度计算规则', `order_no` int(11) DEFAULT '1' COMMENT '排序号', `classification` int(11) DEFAULT '0' COMMENT '0:默认分类\n1:玩游戏\n2:抽奖', `level` int(11) DEFAULT '0' COMMENT '针对同一个分类,不一样的等级', `ext1` longtext COMMENT '扩展字段1(UU中使用该字段指示按钮跳转)', `ext2` longtext COMMENT '扩展字段2,暂时预留', `channel` int(11) DEFAULT '0' COMMENT '任务渠道:0-uu或者1-game_box', `consecutive_day` int(11) DEFAULT '1' COMMENT '连续完成任务的天数', `activity` varchar(256) DEFAULT 'default' COMMENT '任务所属的活动名字', `device` text COMMENT '机型', `packages` text COMMENT '应用', PRIMARY KEY (`id`), KEY `name_channel` (`task_name`,`channel`), KEY `activity` (`activity`(255)) ) ENGINE=InnoDB AUTO_INCREMENT=1194 DEFAULT CHARSET=utf8 COMMENT='任务表';
随着工做经验的积累,我日益感受到,对一名程序员来讲,拥有良好的数据库设计能力是很重要的,甚至是最重要的。
程序员界有一句著名的话
Talk is cheap, show me the code
把这句话演变一下,就成了
Code is boring, show me the data structure
面对一样的数据结构,一百个程序员会写出一百种风格的代码。看别人写的代码,每每是很boring的。
代码是围绕数据结构运行的。
客户端展示的动态数据,都是存储在数据库中,这对程序员来讲必定是常识了。
为了便于阐述,咱们拿简书的文章页面做为样板。
文章的做者、标题、正文、评论、喜欢等等,只要你打开任意两篇文章,两个页面不同的地方,几乎都是由于在数据库中存储的内容不一样。
良好的数据结构能够提高性能,使代码变得简单、清晰。数据结构清晰了,围绕着数据运行的代码天然就清晰了。
提到数据库设计原则,首先会想到第1、第2、第三范式,这些理论能了解最好,本文再也不赘述了。
从实践的角度面对一个具体的应用场景,设计数据库时应遵循哪些原则?
数据结构的设计要能达到应用场景的要求,这是最基本的。举个例子,文章的正文存储在了数据表中的某个字段,该字段的长度被设定为10000字,在文章字数没有被限制在10000字之内的前提下,这显然不能知足应用场景的当前需求。须要考虑,什么样的字段类型才能存储大规模的文本数据?
文章页面中的元素,哪些是主体部分,哪些是附属部分?
一篇文章能够没有评论,评论的有无、多少不影响文章自己的完整性,评论能够被添加、删除。因而可知,文章的评论属于附属部分。阅读次数、喜欢该文章的用户与数量一样如此。
拆分的好处在于,首先数据结构更清晰了,其次能够提升读写性能。当文章有了新评论,只需更新存放评论的表。若是不拆分,须要更新的记录占用的磁盘空间很大,这对磁盘IO速度是个考验。
或许你已经注意到了,文章的标题下面有这篇文章的字数。计算文章的字数,有两个时机:
后者的优点在于数据表中少了一个字段,并且这个字段不是必需的。哪一个时机更好?我的以为前者更好,理由以下
若是可以提升应用的性能,适当的冗余是必要的。
页面的头部有文章做者的昵称,这适合做为冗余字段存储在文章主体数据中吗?用户能够随时更改本身的昵称,若是将昵称做为冗余字段,须要额外的工做以保持数据一致性,从这一点看,用户昵称不适合做为冗余字段。
选择做为冗余的字段应不须要额外的工做来保持数据一致性。
如何存储喜欢文章的用户信息才能作智能推荐?一个好的数据结构应该能应对可能出现的新需求。
为了达到应用的要求,最简单的方式是将这些用户放在一条记录里,存储的字段能够是数组类型。这样设计,喜欢文章的用户信息与用户数量都能轻易获取,读写性能也很好。但对于“喜欢该文章的人还喜欢了”此类的智能推荐,这样的设计明显是难以应对的。将用户放在数组里支持“查询喜欢某文章的用户”,对“查询某用户喜欢的文章”的支持就不好或者根本作不到了,这是一种单向查询的数据结构。
随着用户量不断增长,网站业务数据愈来愈多,文章数量也达到了百万级。这时若是只把文章存在一张数据表里,读写性能必然是会急剧降低的,这可能会致使用户体验变差,用户流失。老板不能容忍,DBA也不能容忍。
合理的解决方案之一是分为两张数据表,一张存储热门文章,另外一张存储非热门文章。热门文章的占比不多,相应的加载速度就会好于非热门文章。
本文总结了设计数据库时需遵照的几个原则
认识到数据结构的重要性,才能设计出好的数据结构。