关系数据库系统是支持关系模型的数据库系统。常见的数据库MySQL,Oracle、DB2,SQLServer都是关系型数据库。
关系:
由行和列构成的二维结构,对应关系数据中表,也就是数据的存储结构
元组
关系数据库中的一个表的行,也就是一条记录。
关系数据库的特性
- 在一个数据库中,表名称唯一
- 表中的每个列名称不同,不同的表列名称可以相同
- 列是无序的
- 行是无序的
关系数据模型中的键
关系模型中具有一些键:超键、候选键、主键、外键,这些键用来约束关系完整性。
超键:含有主键和其他键。
候选键:candidate key 即主键
主键:主键的选择遵循以下原则:
- 主键要尽可能的小
- 主键值不应该被改变,主键通常会被其他表所引用,如果更改后,引用的键值也需要做相应的更改,否则引用就无效了
- 主键通常使用数值类型
- 最好是单列
一般在表的设计中,有能唯一标识记录的字段,但是跟业务有关,我们一般不用作主键,额外增加一个自增的列做为主键。我们在设计表的时候必须给表一个主键,如果没有主键表中的数据更新和删除就会很困难。
聚集索引和主键的区别和联系:每个表只能有一个聚集索引,聚集索引可以不要求具有唯一性。有的数据库中如果表没有聚集索引,在创建主键的时候可以设定把主键作为聚集索引。
聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚集索引,因为一个表的物理顺序只有一种情况,所以,对应的聚集索引只能有一个。如果某索引不是聚集索引,则表中的行物理顺序与索引顺序不匹配,与非聚集索引相比,聚集索引有着更快的检索速度。
关系完整性
上面我们讲了关系模型中的键,这些键约束了完整性的规则,关系数据模型有两个重要的完整性规则:实体完整性和参照完整性。
实体完整性即表中的主键列不能为空。
NULL:空值,表示目前还不知道或者不可用的值,
空值参与算术表达式结果为NULL
true and null=null
fasle and null = false
null and null =null
true or null = true
false or null = null
null or null =null
not null = null
null=null 会返回null 而不是ture,如果要判断是否为不为空,要使用 is null 或者is not null
参照完整性:就是表中存在外键,外键必须与主表的某些记录的候选键相同,或者外键的值必须全部为空。
除上述的完整性规则,通常实际的业务场景有业务规则的约束,约束特定的列能够接受的值,一般的数据库都
有,即check约束。比如性别取值:男和女等,这些约束要根据业务来制定。
规范化,范式
规范化是组织数据的一种技术,规范化方法对表进行分解,以消除数据冗余,避免异常更新,提高数据完整性。规范化是通过范式规则实现的,最常用的范式有:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)。
- 第一范式1NF,表中的列只能含有原子性的值,也就是每个列的值都不能再分,假如有一个人有两个手机号,不能存到一个列中,手机号需要单独存一个表中。
- 第二范式2NF,2NF要满足两个条件:
- 满足第一范式
- 没有部分依赖,即每个非主属性完全函数依赖于候选键。
也就是表要有一个主键,其他字段都依赖主键,每个非主属性是由整个主键函数决定的,而不能由主键的一部分来决定,如果有属性跟主键不相关则叫做部分依赖,就不满足第二范式。
例如(学生选课表):
学生 | 课程 | 教师 | 教材 | 教室 | 上课时间 | 职称 | |
李四 | Spring | 张老师 | 《Spring深入浅出》 | 301 | 08:00 | 副教授 | |
张三 | Struts | 杨老师 | 《Struts in Action》 | 302 | 13:30 | 教授 |
这里通过(学生,课程)可以确定教师、教师职称,教材,教室和上课时间,所以可以把(学生,课程)作为主键。但是,教材并不完全依赖于(学生,课程),只拿出课程就可以确定教材,因为一个课程,一定指定了某个教材。这就叫不完全依赖,或者部分依赖。出现这种情况,就不满足第二范式。
修改后如下:
选课表:
学生 | 课程 | 教师 | 教室 | 职称 | 上课时间 | |
李四 | Spring | 张老师 | 301 | 副教授 | 08:00 | |
张三 | Struts | 杨老师 | 302 | 教授 | 13:30 |
课程表:
课程 | 教材 |
Spring | 《Spring深入浅出》 |
Struts | 《Struts in Action》 |
- 第三范式
第三范式也要满足两个条件:
- 满足第二范式
- 没有传递依赖,所有非主属性对任何候选关键字都不存在传递依赖。
简单的说,第三范式首先要满足第二范式,其次非主属性之间不存在函数依赖。由于满足了第二范式,表示每个非主属性都函数依赖于主键。如果非主属性之间存在了函数依赖,就会存在传递依赖,这样就不满足第三范式。
还是上面选课表例中修改后的选课表中,一个教师能确定一个教师职称。这样,教师依赖于(学生,课程),而教师职称又依赖于教师,这叫传递依赖。第三范式就是要消除传递依赖。
满足第三范式的选课表如下:
学生 | 课程 | 教师 | 教室 | 上课时间 | |
李四 | Spring | 张老师 | 301 | 08:00 | |
张三 | Struts | 杨老师 | 302 | 13:30 |
课程表:
课程 | 教材 |
Spring | 《Spring深入浅出》 |
Struts | 《Struts in Action》 |
教师表
教师 | 教师职称 |
张老师 | 副教授 |
杨老师 | 教授 |
范式主要有六种:第一范式、第二范式、第三范式、BC范式、第四范式和第五范式,一般设计数据库的时候满足三范式就可以了,规范化带来的好处是通过减少数据冗余提高更新数据的效率,同时保证数据的完整性。然而,我们在实际的应用中也要防止过度规范化,规范化的程度越高,划分的表就越多,在查询数据时要关联的表越多,就会影响查询的效率。关键的问题是要依据业务需求,仔细权衡数据查询和数据更新的关系,制定最合适的规范化程度,还有一点需要注意的是,不要为了遵循严格的规范化规则而修改业务需求。
命名规范
mysql可以设置成区分大小写(默认不区分),但hive是不区分大小写的
- 数据库设计,需要先设计逻辑模型,再设计物理模型,最后形成各模型的模型文件和数据库设计文档,文档包括:表,字段,字段类型主键、默认值和说明
- 所有的数据库对象名称必须使用小写字母并用下划线表示。
- 所有数据库对象名称禁止使用mysql保留关键字
- 数据库对象的命名要能做到见名知意,并且最好不要超过32个字符。太长不方便使用,并且会在传输时增加网络开销
- 临时表必须以tmp_为前缀并以日期为后缀
- 备份表必须以bak_为前缀并以日期为后缀
- 所有存储相同数据的列名和列类型必须一致,比如user表中的id和order表中的user_id
数据库设计规范
- 数据库读写分开,做主备,主库主要进行写操作,备库主要进行查询操作。主库采用Innodb,备库采用myisam库。
- 数据库和表的字符集统一使用UTF-8,如果要存储一些如表情符号的,还需使用UTF-8的拓展字符集, 数据库,表,字段字符集一定要统一,统一字符集可以避免由于字符集转换产生的乱码, 在mysql中UTF-8字符集,汉字占3字节,ASCII码占1字节.
- 所有表和字段都需要添加注释
- 数据库设计文档必须要有
- 尽量控制单表数据量大小,
- 建议控制在500万条数据以内,虽然500万并不是mysql的数据库限制,但是会给修改表结构,备份,恢复带来很大困难。mysql一行数据不能超过8095字节
- 单表可存储数据量大小取决于存储设置和文件系统
- 想减少单表数据量:历史数据归档(常见于日志表),分库分表(常见于业务表),分区表
- 建议不要使用mysql分区表,因为分区表在物理上表现为多个文件,在逻辑上表现为一个表。(hive是分目录)如果一定要分区,请谨慎选择分区键,跨分区查询效率比查询大数据量的单表查询效率更低
- 建议采物理分表的方式管理大数据,但是对应用程序的开发要求和复杂度更高
- 尽量做到冷热数据分离,减少表的宽度(字段数)
减少磁盘IO,保证热数据的内存缓存命中率,更有效的利用缓存,避免读入无用的冷数据,对表的列进行拆分,将经常使用的列放到一个表中,可以避免过多的关联操作,也可以提高查询性能。
对目前mysql来说,修改一个字段的成本要远远大于增加一个字段的成本
8禁止在数据库中存储图片,文件等二级制数据
a) 这类数据如果要存,就得使用blog或者text这样的大字段加以存储,会影响数据库的性能
b)这种文件通常所占数据容量很大,会在短时间内造成数据库文件的快速增长,而数据库在读取数据时,会进行大量的随机IO操作,如果数据文件过大,IO操作会非常耗时,从而影响数据库性能
c) 正确做法是将这类数据存储在文件服务器中,而数据库只存储地址信息
9禁止在线上做数据库压力测试
10禁止从开发环境,测试环境直连生产环境数据库
注:生产环境:真实数据
索引设计规范(Innodb中主键实质上是一个索引)
- 限制每张表上索引数量,建议单表不超过5个索引。索引并不是越多越好,可以提高查询效率,但是会降低插入和更新的效率。甚至在一些情况下,还会降低查询效率,因为mysql优化器在选择如何优化查询时,会根据统计信息,对每一个可用索引来进行评估,以生成一个最好的执行计划,如果同时有很多索引都可以用于查询,就会增加mysql查询优化器生成查询计划的时间。
- 每个Innodb表都必须有一个主键。Innodb是一种索引组织表,是指数据存储的逻辑顺序和索引的顺序是相同,Innodb是按照主键索引的顺序来组织表的,因此,每个Innodb表都必须要有一个主键,如果我们没有指定主键,那么Innodb会优先选择表中第一个非空唯一索引来作为主键,如果没有这个索引,那么Innodb会自动生成一个占6字节的主键,而这个主键的性能并不是最好。
- 不使用更新频繁的列作为主键,不使用多列联合主键。因为Innodb是一种索引组织表,如果主键上的值频繁更新,就意味着数据存储的逻辑顺序频繁变动,必然会带来大量的IO操作,降低数据库性能
- 不要使用uuid,md5,hash,字符串列作为主键。因为这种主键不能保证主键的值是顺序增长的,如果后来的主键值在已有主键值的中间段,那么这个主键插入的时候,会将所有主键值大于它的列都向后移。
- 最好选择能保证值的顺序为顺序增长的列为主键。并且数据不能重复,每个表给设置一个自增的id做主键
6. 哪些列上建立索引?
a) 在select,delete,update的where从句中的列
b) 包含在order by,group by,distinct字段中的列
c) 多表join的关联列:mysql对关联操作的处理方式只有一种,那就是嵌套循环的关联方式,所以这种操作的性能对关联列上的索引的依赖性很大
join左表放小表(数据量小),因为坐标会全部进缓存
字段设计规范
1. 优先选择符合存储需要的最小的数据类型
a) 尽量将字符串转化为数字类型存储:如将ip存储为数字:inet_aton(‘255.255.255.255’) = 4294967295 ,反之, inet_ntoa(4294967295) = ‘255.255.255.255’
注:inet_aton inet_ntoa就是两个函数 先不用在意怎么用
b) 对于非负整型数据,优先使用无符号整型来存储,如:id,age,无符号相对于有符号,可以多出一倍的存储空间
c) mysql中,varchar(n)中n表示字符数而不是字节数
d) 避免使用text,blog来存储字段,这种类型只能使用前缀索引,如果非要使用,建议将这种数据分离到单独的拓展表中
e) 能够确定范围的值能用tinyint的就不要用int
f) 自增的主键字段使用bigint
3.避免使用enum类型。
4. 尽可能把所有列定义为not null。(虽然这并不可能)
a) 索引null列需要额外的空间来保存,占更多空间
b) 进行比较和计算时,对null值作特别的处理,可能造成索引失效
5. 禁止使用字符串来存储日期型数据。
a) 无法使用日期函数计算比较
b)字符串存储要占更多的内存空间,datetime(8字节)和timestamp(本身是以int存储,占4字节,范围:1970-01-01 00:00:01到2038-01-19 03:14:07)
d)可以使用数值存储日期
6. 财务相关数据,使用decimal类型 (精准浮点类型,在计算时不丢失精度)。
SQL开发规范
注:自增字段使用bigint
1. 建议使用预编译语句(prepareStatment)进行数据库操作
a) 可以同步执行预编译计划,减少预编译时间
b) 可以有效避免动态sql带来的SQL注入的问题
c) 只传参数,一次解析,多次使用,比传递sql语句更高效
2. 避免数据类型的隐式转换
a) 一般出现在where从句中,会导致索引失效,如:select id,name from user where id = ‘12’;
3. 充分利用已存在的索引
a) 避免使用双%的查询条件(模糊查询),不走索引
b) 一个SQL只能利用到复合索引中(多个列构成的索引)的一列进行范围查询
c) 使用left join或not exists来优化not in操作 注:not in 做的是笛卡尔积
4. 程序连接不同的数据库使用不同的账号,禁止跨库查询 避免权限过大而产生的安全风险
5. 禁止使用select * 来查询,必须用字段名
a) 可能会消耗更多的cpu和IO以及网络资源
b) 无法使用覆盖索引
c) 可以减少表结构变更对已有程序的影响
6. 禁止使用不含字段列表的insert语句。 可以减少表结构变更对已有程序的影响
注:hive要插入只能全部插入
7. 禁止使用子查询
a) 虽然可使sql可读性好,但是缺点远远大于优点
b) 子查询返回的结果集无法使用索引,结果集会被存储到一个临时表中,结果集越大性能越低
c)把子查询优化为join操作,但是并不是所有的都可以优化为join,一般情况下,只有当子查询是在in子句中,并且子查询是一个简单的sql(不包含union,group by,order by,limit)才能转换为关联查询
注:子查询即嵌套select
8. 避免join过多的表
a) 每join一个表会占一部分内存(join_buffer_size)
b) 会产生临时表操作,影响查询效率
c) mysql最多允许关联61个表,建议不超过5个
9. 减少同数据库的交互次数
a)数据库更适合处理批量操作
b)合并多个相同的操作到一起,提高处理效率
10. 使用in代替or
a) in的值不要超过500个
b) in 操作可以有效利用索引
11. 禁止使用order by rand()进行随机排序
a) 会把表中所有符合条件的数据装载到内存中进行排序
b) 会消耗大量的cpu和io及内存资源
c) 推荐在程序中获取随机值
12. 禁止在where从句中对列进行函数转换和计算 ,会导致无法使用相关列上的索引
1. where date(create_time)=’20170901’ 写成 where create_time >= ‘20170901’ and create_time < ‘20170902’
13. 在明显不会有重复值时使用union all而不是union
union all得到的结果不去重不排序
union得到的结果去重并排序
14. 拆分复杂的大sql为多个小sql
a) 目前mysql中一个sql只能使用一个cpu计算,不支持多cpu并行计算
b) sql拆分后可以通过并行执行来提高处理效率
数据库操作行为规范
主要面向手动操作数据库的行为
1. 超过100万的批量写操作,要分批多次进行操作
a). 主从复制中:大批量操作可能会造成严重的主从延迟,因为当主库执行完成后,才会在从库执行
b). binlog日志为row格式时会产生大量的日志
c). 避免产生大量事务,产生阻塞,占满可用连接
2. 对大表数据结构的修改一定要谨慎
a). 可能会造成严重的锁表操作,尤其是生产环境,是不能忍受的
b). 对于大表使用pt-online-schema-change修改表结构:
c). 首先会建立一个与原表结构相同的新表
d). 然后在新表上进行表结构的修改
e). 然后把原表中的数据复制到新表中,并且增加一些触发器,以便把原表中即时新增的数据也复制到新表中
f). 在行的所有数据复制完成之后,会在原表上增加一个很准的时间锁,同时把新表命名为原表,把原表删掉
g). [实际上是把一个原子的DDL操作分解成多批次进行]
h). [避免大表修改产生的主从延迟问题]
i). [避免在对表字段进行修改时进行锁表]
3. 禁止为程序使用的账号赋予super权限
a). 当数据库连接数达到最大限制时,允许1个有super权限的用户连接
b). super权限只能留给DBA处理问题的账号使用
4. 对于程序连接数据库账号,遵循权限最小原则
a). 程序使用的数据库账号只能在一个DB下使用,不准跨库
b). 程序使用的账号原则上不准有drop权限
5. 禁止使用非本人账户登录
数据仓库
数据仓库是一个面向主题的、集成的、非易失的且随时间变化的数据集合,用于支持管理人员的决策。
面向主题示例•
例:一个面向事务处理的“商场”数据库系统, 其数据模式如下
采购子系统:
订单(订单号,供应商号,总金额,日期)
订单细则(订单号,商品号,类别,单价,数量)
供应商(供应商号,供应商名,地址,电话)
销售子系统:顾客(顾客号,姓名,性别,年龄,文化程度,地址,电话)
销售(员工号,顾客号,商品号,数量,单价,日期)