数据库设计需要注意什么

一、基础规范:

1. 必须使用InnoDB存储引擎

   解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高

2. 新库默认使用utf8mb4字符集

   解读:utf8mb4是utf8的超集,emoji表情以及部分不常见汉字在utf8下会表现为乱码,故需要升级至utf8mb4。

            默认使用这个字符集的原因是:“标准,万国码,无需转码,无乱码风险”,并不“节省空间”。

3. 数据表、数据字段必须加入中文注释

    解读:N年后谁tm知道这个r1,r2,r3字段是干嘛的

4. 禁止使用存储过程、视图、触发器、Event

    解读:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,

    业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,CPU计算还是上移吧

5. 禁止存储大文件或者大照片 

    解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI多好

6. 禁止使用应用程序配置文件内的帐号手工访问线上数据库

7. 禁止非DBA对线上数据库进行写操作,修改线上数据需要提交工单,由DBA执行,提交的SQL语句必须经过测试

8. 分配非DBA以只读帐号,必须通过VPN+跳板机访问授权的从库

9. 开发、测试、线上环境隔离

10. 不在数据库做计算,cpu计算务必移至业务层

11. 平衡范式与冗余,为提高效率可以牺牲范式设计,冗余数据 

12. 拒绝3B(big),大sql,大事务,大批量

 

二、命名规范

1. 只允许使用内网域名,而不是ip连接数据库

   解读:使用域名,在切换数据库服务器的时候,只需要改DNS域名解析,不需要改配置文件。达到一处更改,多处生效的效果。

   不只是数据库,缓存(memcache、redis)的连接,服务(service)的连接都必须使用内网域名,机器迁移/平滑升级/运维管理…太多太多的好处,

   如果朋友你还是采用ip直连的,赶紧升级到内网域名吧。

2. 线上环境、开发环境、测试环境数据库内网域名遵循命名规范

   业务名称:xxx

   线上环境:dj.xxx.db

   开发环境:dj.xxx.rdb     (r 是research, rd 是 research and development 的简称)

   测试环境:dj.xxx.tdb

   从库在名称后加-s标识,备库在名称后加-ss标识 (从库,secondary database)

   线上从库:dj.xxx-s.db

   线上备库:dj.xxx-sss.db

3. 库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用

4. 表名t_xxx,非唯一索引名idx_xxx,唯一索引名uniq_xxx    (idx :索引文件(Index file))

 

三、表设计规范

1. 单实例表数目必须小于500

2. 单表列数目必须小于30

3. 表必须有主键,例如自增主键

    解读:

    a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用

    b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率

    c) 无主键的表删除,在row模式的主从架构,会导致备库夯住

4. 禁止使用外键,如果有外键完整性约束,需要应用程序控制

   解读:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,

   大数据高并发业务场景数据库使用以性能优先

5. 控制单表数据量,单表记录控制在千万级

 

四、字段设计规范

1. 必须把字段定义为NOT NULL并且提供默认值

   解读:

   a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化

   b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多

   c)null值需要更多的存储空间,无论是表还是索引中每行中的null的列都需要额外的空间来标识

   d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’shenjian’,如果存在name

        为null值的记录,查询结果就不会包含name为null值的记录。(无论有无索引,都搜不到,已验证)

2. 禁止使用TEXT、BLOB类型

    解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能

3. 禁止使用小数存储货币

    解读:使用整数吧,小数容易导致钱对不上

4. 必须使用varchar(20)存储手机号

   解读:

   a)涉及到区号或者国家代号,可能出现+-()

   b)手机号会去做数学运算么?

   c)varchar可以支持模糊查询,例如:like“138%”

5. 禁止使用ENUM,可使用TINYINT代替

    解读:

    a)增加新的ENUM值要做DDL操作

    b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?

6. 禁止使用小数存储国币

    有朋友问存储前乘以100,取出后除以100是否可行,个人建议“尽量少的使用除法”。

    曾经踩过这样的坑,100元分3天摊销,每天摊销100/3元,结果得到3个33.33。后来实施对账系统,始终有几分钱对不齐,郁闷了很久(不是几分钱

    的事,是业务方质疑的眼神让研发很不爽),最后发现是除法惹的祸。

    解决方案:使用“分”作为单位,这样数据库里就是整数了。

7. 字段选择类型更小的通常更好:

    解读:更小的数据类型通常更快,因为他们占用更少的磁盘、内存和CPU缓存,并且处理需要的CPU周期也更少。

8. 简单就好:

    解读:简单的数据类型的操作通常需要更少的CPU周期。例如:整型比字符串操作的代价更低,因为字符集和校对规则(排序规则)是字符串比较比整型比较更复杂。

             这里有两个例子:一个是应该使用MySQL内建的类型而不是字符串来存储日期和时间,另外一个是应该使用整型存储IP地址。

9. 因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用DECIMAL。在数据量比较大的时候,可以考虑使用BIGINT代替DECIMAL,

   将对应的值扩大N倍。

10. 二进制比较的优势并不仅仅体现在大小写敏感上。MySQL比较BINARY字符串时,每次按一个字节,并且根据该字节的数值进行比较。因此,二进制比字符比较简单

     的多,所以也就更快。

11. 可以使用BIGINT类型存储微秒级别的时间戳。

12. 一旦选择了一种类型,要确保在所有关联表中都使用同样的类型。会用不同数据类型可能导致性能问题,即使没有性能影响,在比较操作时隐式类型转换也可能

     导致很难发现错误。

13. 如果可能,应该避免使用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。

14. 范式(修改多,关联查询少)和反范式(查询多,修改少)的选择

15. 合理使用缓存表、汇总表、计数器表

 

五、索引设计规范

1. 单表索引建议控制在5个以内

2. 单索引字段数不允许超过5个

   解读:字段超过5个时,实际已经起不到有效过滤数据的作用了

3. 禁止在更新十分频繁、区分度不高的属性上建立索引

   解读:

   a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能

   b)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似

4. 建立组合索引,必须把区分度高的字段放在前面

    解读:能够更加有效的过滤数据

 

参见:58到家数据库30条军规解读

         再议数据库军规

         mysql笔记01 MySQL架构与历史、Schema与数据类型优化

 

 

 

varchar(10) 和 varchar(100) 存储5个字节的数据,是否一样?

如现在用户需要存储一个地址信息。根据评估,只要使用100个字符就可以了。但是有些数据库管理员会认为,反正Varchar数据类型是根据实际的需要来分配长度的。还不如给其大一点的呢。为此他们可能会为这个字段一次性分配200个字符的存储空间。这VARCHAR(100)与VARCHAR(200)真的相同吗?结果是否定的。虽然他们用来存储90个字符的数据,其存储空间相同。但是对于内存的消耗是不同的。对于VARCHAR数据类型来说,硬盘上的存储空间虽然都是根据实际字符长度来分配存储空间的,但是对于内存来说,则不是。内存使用固定大小的内存块来保存值。简单的说,就是使用字符类型中定义的长度,即200个字符空间。显然,这对于排序或者临时表(这些内容都需要通过内存来实现)作业会产生比较大的不利影响。解释可以参见这里。所以如果某些字段会涉及到文件排序或者基于磁盘的临时表时,分配VARCHAR数据类型时仍然不能够太过于慷慨。还是要评估实际需要的长度,然后选择一个最长的字段来设置字符长度。如果为了考虑冗余,可以留10%左右的字符长度。千万不能认为其为根据实际长度来分配存储空间,而随意的分配长度,或者说干脆使用最大的字符长度。

1. 虽然用varchar(100)和varchar(10)储存’abc’在硬盘上的大小一样, 但是, 在内存里前者占100个字节, 后者只占10个字节. 对需要排序操作的字段, 不能分配varchar(100000)这样的长度, 而是需要根据业务, 仔细定一个最大值. 

2. 不能大于最大长度, 例如, varchar(1), 收到’abc’也会报错 

3. CHAR(1)与VARCHAR(1)两这个定义,会有什么区别呢?虽然这两个都只能够用来保存单个的字符,但是VARCHAR要比CHAR多占用一个存储位置。这主要是因为使用VARCHAR数据类型时,会多用1个字节用来存储长度信息。这个管理上的开销CHAR字符类型是没有的。

4. 需要经常变化长度的字段使用char(20)比使用varchar(20)好, 这样可以避免碎片和额外的调整工作。char(20)给字符串分配的空间就是20,而varchar分配的不一定是20,如果字符串更改,超过原来的长度,varchar可能需要额外的调整。

   三是从碎片角度进行考虑。使用CHAR字符型时,由于存储空间都是一次性分配的。为此某个字段的内容,其都是存储在一起的。单从这个角度来讲,其不存在碎片的困扰。而可变长度的字符数据类型,其存储的长度是可变的。当其更改前后数据长度不一致时,就不可避免的会出现碎片的问题。故使用可变长度的字符型数据时,数据库管理员要时不时的对碎片进行整理。如执行数据库导出导入作业,来消除碎片。

参见:https://blog.csdn.net/imzoer/article/details/8435540/ 

           http://tech.it168.com/a2011/0426/1183/000001183173.shtml#articlecomment

 

posted @ 2019-01-05 13:05  Jtianlin  阅读(2810)  评论(0编辑  收藏  举报