需求分析
-
了解系统中所要存储的数据
-
了解数据的存储特点
-
了解数据的生命周期
思路:
实体及实体之间的关系(1对多,1对1,多对多)、
实体所包含的属性、
哪些属性或属性的组合可以唯一标识的一个实体
逻辑设计
-
将需求转化为数据库的逻辑模型
-
通过ER图的型式对逻辑模型进行展示
-
同所选用的具体的DBMS系统无关
设计范式概要:
1. 第一范式:数据库中的表都是二维表
2. 第二范式:数据库中的表中不存在非关键字段对任一候选关键字段的部分函数依赖。
3. 第三范式:如果数据表中不存在非关键字段,对任意候选关键字段的传递函数依赖则符合第三范式
4. BC范式:数据库中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合BC范式。也就是说如果是复合关键字,则复合关键字之间也不能存在函数依赖关系。
物理设计
-
选择合适的数据库管理系统
- oracle、sqlServer商业数据库:成本
- mysql、PgSql开源数据库:免费、事务成本比oracal高
- mysqyl、PgSql适用于互联网项目, oracle、sqlServer更适合企业级项目
-
定义数据库、表及字段的命名规范
- 可读性原则
- 表意向原则
- 长名原则
-
根据所选的DBMS系统选择合适的字段类型
- 优先选择数字类型,其次选择日期或者二进制类型,最后选择字符类型。对于相同级别的数据类型,应该有限选择占用空间小的数据类型。(主要关系到存储空间不同的类型占用不同存储空间)
- 对比数据进行比较操作时:同样的数据,字符处理往往比数字处理慢
- 数据库中,数据处理以页为单位,列的长度越小,利于性能提升(主要是提升磁盘io效率)
- 如果列中要存储的数据长度差不多的一致的,则应该考虑用char;否则应该考虑用varchar
- 如果列中的最大数据长度小鱼50Byte,则一般也考虑用char、(当然,如果这个列很少用,则基于节省空间和减少IO的考虑,还是可以选择用varchar)
- 一般不宜定义大于50Byte的char类型列
- decimal用于存储精确数据,而float只能用于存储非常精确数据。故精确数据只能选择用decimal类型。
- 由于float的存储空间开销一般比decimal小(精确到七位小数只需要4个字节,而精确到15位小数只需要8字节),故非精确数据优先选择float类型。
- 时间类型存储:
- 使用int存储时间字段:
- 字段长度比datetime小
- 使用不方便要进行函数转换
- 只能存储到2038-1-19 11:14:07即2147483648
- 需要存储的时间粒度:年月日时分秒周选择不同时间类型
- 使用int存储时间字段:
-
如何选择主键
- 区分业务主键和数据库主键:业务主键用于标识业务数据,进行与表之间的关联。数据库主键为了优化数据存储(innodb会生成6字节的隐含主键)
- 根据数据库的类型,考虑主键是否要顺序增长:有些数据库是按主键的顺序逻辑存储的
- 主键的字段类型所占空间要尽可能的小:对于使用聚集索引方式存储的表,每个索引后都会附加主键信息
-
避免使用外键约束
- 建立数据导入效率
- 增加维护成本
- 虽然不建议使用外检约束,但是相关联的列上一定要建立索引
-
避免使用触发器
- 降低数据导入效率
- 可能会出现意想不到的数据异常
- 使业务逻辑变得复杂
-
预留字段
- 无法准确的知道预留字段的类型
- 无法准确的知道预留字段中所存储的内容
- 后期维护预留字段所要的成本,同增加一个字段所需要的成本是相同的
- 严禁使用预留字段
-
反范式化设计(空间换时间)
- 减少表关联的数量
- 增加数据的读取效率
- 反范式化一定要适度
维护优化
-
维护数据字典
- 第三方工具对数据字典进行维护
- 利用数据库本身的备注字段来维护数据库字典(可以导出备注),但是需要注意维护备注
-
维护索引
- 如何选择适合的列建立索引
- 出现在where从句,group by从句,order by 从句的列
- 可选择性高的列要放到索引的前面
- 索引中的列不要包括太长的数据类型
- 索引并不是越多越好
- 过多的索引不但会降低写效率而且会降低读的效率
- 定期维护索引碎片,碎片会影响读取效率
- 在sql语句中不要使用强制索引关键字,在后面表修改处理后可能不合适
- 如何选择适合的列建立索引
-
维护表结构
- 使用在线变更表结构的工具:pt-online-schema-change,mysql5.6之后本身支持在线表结构的变更
- 同时对数据字典进行维护
- 控制表的宽度和大小(拆分)
- 数据库中适合的操作:
- 批量操作大于逐条操作
- 禁止使用select * 这样的查询,浪费io及字段名变更可能影响程序的出错
- 控制使用用户自定义函数:使用函数不能使用索引
- 不要使用数据库中的全文索引:需要另外的所以文件进行维护,对中文支持不友好,可以选择适当的搜索引擎代替
-
在适当的时候对表进行水平拆分或垂直拆分
- 垂直拆分:列分开成两张表
- 经常一起查询的列放到一起:优化io减少查询复杂程度
- text、blob等大字段拆分出到附加表中
- 水平拆分:一张表数据放到相同结构多张表里
- 主键hash处理分表
- 垂直拆分:列分开成两张表
个人总结
- 平常使用中数据库表的设计主要是针对业务需求,不针对实际业务有需求的设计都是流氓设计
- 针对不同的数据确定其数据的作用、重要性、生命周期、以及查看修改删除插入的频率都应该做出分析。对于朝生暮死的数据尽量不放数据库,生命周期过短,可以考虑使用nosql数据库
- 反范式的设计工作中主要是字段冗余,这里主要是为了提高查询效率,那么说明这里的数据主要也是查询为主。对于这样的设计一定要注意是否真的冗余到了点上,是否真的提供了效率,在更新主体时是否也需要同步这部分冗余数据
- 数据字典,在工作中一般都是在数据库中有专门的表设计做数据字典,这里需要注意及时做维护
- 减少字段查询,减少io也是一种手段。平常其实很多时候为了偷懒直接select * 这其实是个坏毛病
- 索引使用前应当知道在查询sql中什么时候使用了索引,哪些写法不会使用索引,这样才真的达到了建立索引的目的。
- 分表分库个人觉得数据量不到就不要这么搞吧,分了之后对数据的处理又要多不少步骤,不要过度设计吧。
- 数据量过高可以考虑搜索引擎进行搜索,减小数据库压力,增加搜索效率,搜索引擎可以进行分词对大字段搜索的支持也好得多
索引分享
SQL什么条件会使用索引?
当字段上建有索引时,通常以下情况会使用索引:
- INDEX_COLUMN = ?
- INDEX_COLUMN > ?
- INDEX_COLUMN >= ?
- INDEX_COLUMN < ?
- INDEX_COLUMN <= ?
- INDEX_COLUMN between ? and ?
- INDEX_COLUMN in (?,?,…,?)
- INDEX_COLUMN like ?||’%’(后导模糊查询)这个面试经常会问到
- T1. INDEX_COLUMN=T2. COLUMN1(两个表通过索引字段关联)
SQL什么条件不会使用索引?
查询条件 不能使用索引原因
-
INDEX_COLUMN <> ?INDEX_COLUMN not in (?,?,…,?) 不等于操作不能使用索引
-
function(INDEX_COLUMN) = ?INDEX_COLUMN + 1 = ?INDEX_COLUMN || ‘a’ = ?
经过普通运算或函数运算后的索引字段不能使用索引 -
INDEX_COLUMN like ‘%’||?INDEX_COLUMN like ‘%’||?||’%’
含前导模糊查询的Like语法不能使用索引 -
INDEX_COLUMN is null B-TREE索引里不保存字段为NULL值记录,因此IS
NULL不能使用索引 -
NUMBER_INDEX_COLUMN='12345’CHAR_INDEX_COLUMN=12345
Oracle在做数值比较时需要将两边的数据转换成同一种数据类型,如果两边数据型不同时会对字段值隐式转换,相当于加了一层函数处理,所以不能使用索引。
a.INDEX_COLUMN=a.COLUMN_1 给索引查询的值应是已知数据,不能是未知字段值。
注:经过函数运算字段的字段要使用可以使用函数索引,这种需求建议与DBA沟通。有时候我们会使用多个字段的组合索引,如果查询条件中第一个字段不能使用索引,那整个查询也不能使用索引如:我们company表建了一个id+name的组合索引,以下SQL是不能使用索引的Select * from company where name=?Oracle9i后引入了一种index skip scan的索引方式来解决类似的问题,但是通过index skip scan提高性能的条件比较特殊,使用不好反而性能会更差。
我们一般在什么字段上建索引?
这是一个非常复杂的话题,需要对业务及数据充分分析后再能得出结果。主键及外键通常都要有索引,其它需要建索引的字段应满足以下条件:
- 字段出现在查询条件中,并且查询条件可以使用索引;
- 语句执行频率高,一天会有几千次以上;
- 字段条件可筛选的记录集很小,那数据筛选比例是多少才适合?这个没有固定值,需要根据表数据量来评估,以下是经验公式,可用于快速评估:
小表(记录数小于10000行的表):筛选比例<10%;
大表:(筛选返回记录数)<(表总记录数单条记录长度)/10000/16 单条记录长度≈字段平均内容长度之和+字段数2
例如:
-
需要建立索引字段:外键
- PRODUCT_ID,COMPANY_ID,MEMBER_ID,ORDER_ID,TRADE_ID,PAY_ID
有对像或身份标识意义字段 HASH_CODE,USERNAME,IDCARD_NO,EMAIL,TEL_NO,IM_NO
- PRODUCT_ID,COMPANY_ID,MEMBER_ID,ORDER_ID,TRADE_ID,PAY_ID
-
索引慎用字段:需要进行数据分布及使用场景详细评估
- 日期 GMT_CREATE,GMT_MODIFIED
- 年月 YEAR,MONTH
- 状态标志 PRODUCT_STATUS,ORDER_STATUS,IS_DELETE,VIP_FLAG
- 类型 ORDER_TYPE,IMAGE_TYPE,GENDER,CURRENCY_TYPE
- 区域 COUNTRY,PROVINCE,CITY
- 操作人员 CREATOR,AUDITOR
- 数值 LEVEL,AMOUNT,SCORE
- 长字符 ADDRESS,COMPANY_NAME,SUMMARY,SUBJECT
-
不适合建索引的字段:描述备注
- DESCRIPTION,REMARK,MEMO,DETAIL
- 大字段 FILE_CONTENT,EMAIL_CONTENT