字段类型设计,设计规范、范式
很少去用,因为维护成本很高。使用关联表的方式来代替。
对精度要求高,方案有两个:定点数(decimal)、小单位,大数额(int)。整数的运算是没有精度问题的,只有小数才存在精度问题,因为在计算机中不能将小数完全转换为二进制。
如37.85元——>3785分
- 非空字段要比null字段的处理效率要高些
- 不需要判断是否为null
- null在MySQL中,不好处理,需要占用额外的存储空间,运算也需要特殊的运算符!
- null参与常规运算,结果就是null
解决方案:
使用一个特殊的数据,进行占位
int not null default-1
string not null default ‘ ’
注意:但是会产生歧义,因为假如一个字段的本身值就是为‘ ’,那么就不知道这个数据表示的是null还是‘ ’。所以应该尽量使用不会出现在业务逻辑中的数据作为默认,表示null数据。
如果过多就可能导致某个业务逻辑只用到其中少部分的字段
预留字段当前没有作用,为了在后期业务逻辑设计改变时,快速修改表结构
一个记录的字段比较多,分布到多个表中进行存储
在多端,使用关联字段,关联一端的主键
一个多对多的关系由三张表实现
由两个一对多的关系来实现
字段原子性,不可(再)分割,关系型数据库默认满足第一范式
- 主键:可以唯一标识记录的字段或字段的集合
- 依赖:A字段可以确定B字段,则B字段依赖A字段
- 对主键部分依赖:如果某个字段依赖复合主键的一部分字段,则称之为对主键的部分依赖
独立数据独立建表
表中存在于业务逻辑无关的ID主键
表之间的关系由关联字段(关联表)进行表示
典型优势:
MySQL中的数据,索引,以及其他数据是如何存储的。是一套文件系统的实现。
数据和索引的存储 | 文件移动 | 插入顺序 | 是否产生空间碎片 | 事物 | 外键 | 锁定级别 | 并发处理能力 | |
---|---|---|---|---|---|---|---|---|
MyISAM | 分开存储(.myd和ymi) | 支持 | 表末尾插入 | 会 | 不支持 | 不支持 | 支持表级锁定 | 弱 |
Inoodb | 集中存储 .idb | 不支持 | 主键顺序插入(插入需进行排序) | 不会 | 支持 | 支持 | 表级锁定和行级锁定 | 强 |
- 如果没有需求,请选择Innodb
- MyISAM:以读写和插入为主的应用程序,例如:新闻、博客、门户网站
- Innodb:更新、删除操作频繁,或者要求数据完整性比较强。并发性好,支持事物和外键保证事物完整性
先尝试加锁,如果锁定成功就使用资源。否则等待或者放弃。
不同类型的锁,导致的并发操作是不一样的。
- 在MySQL中,实现了不同粒度的锁。
- MySQL的Innodb实现了表级和行级锁定
- 一旦加锁,锁定的记录数量是不同的
innodb和myisam都支持表锁
- 加锁:lock tables table-name1,table-name2 READ|WRITE;
- 解锁:unlock tables;
对于innodb支持行锁:
innodb的行锁,其实是一个子范围锁,依据条件锁定的部分范围。学名:间隙锁
- 对查询的记录增加共享锁:select * from table where LOCK IN share mode
- 对查询记录增加排他锁:select * form table where UPDATE
不同的类型对关键字的限制不同,其他都相同
在创建表时完成。
-- 创建了一个主键索引,通常都是自动增长的与业务逻辑无关的id
id int auto_increment primary key,
first_name varchar(16),
last_name varchar(16),
sn varchar(16),
information text,
-- 创建一个基于first_name、last_name上的复合索引,并命名为name
key name (first_name,last_name),
-- 创建了基于sn的唯一索引,没有命名,默认以字段名命名索引
unique key(sn),
-- 创建了全文索引
fulltext key(information)
在修改表结构时完成
-- 假设有一个user表
alter table user(表名)
-- 创建一个基于first_name、last_name上的复合索引,并命名为name
add key name (first_name,last_name),
-- 创建了基于sn的唯一索引,没有命名,默认以字段名命名索引
add unique key(sn),
-- 创建了全文索引
add fulltext key(information)
注意:删除主键时,通常伴随着自增长,自增还依赖于主键,需要先去掉自增,再删除主键
在执行的select前,使用关键字explain,可以获取该查询语句的执行计划。若要查询更完整的,就在语句的最后再加上/G
当执行SQL时,先分析优化,形成执行计划,按照计划执行
建立的索引,会在哪些情况下被使用,根据这些情况去建立索引
where查询
当一个字段没有索引时,我们要根据这个字段查询时,可以先对该字段建立索引然后再查询((alter table 表名 add index(字段名)))。这样会大大加快查询效率。若某个字段有多个索引,系统会选择一个最优的使用
order by排序
filesort,外部排序。是相对于内部排序(排序的数据可以被一次性的载入到内存中进行排序,就叫做内排序)来说的,性能较低。需要将数据读取到内存,但是不会一次性全读取,需要分段读取。合并排序结果,叫做外部排序。外部排序一个很大的瓶颈就是要先将数据读取到内存。
如select username from user where ordey by username limit 5;
join连接
如select c.* , count(s.id) from class c join student s on c.id=s.class_id group by c.id limit 5
索引覆盖
是一种现象:指的是索引中提供的关键字覆盖了需要查询的字段数据,此时查询会由索引提供,而不是数据表提供。
假如给student表的first_name字段和last_name字段加了复合索引。查询语句如下:
select first_name,last_name form student limit 5
则查看执行计划,发现此种情况并没有在where查询、order by排序、join连接这三种情况中,但是还是用了索引查询,这就是索引覆盖的现象。
如假如给student表的first_name字段和last_name字段加了复合索引。但是查询语句如下:
select first_name,last_name,user form student limit 5
发现更改查询组合后,在执行计划中可以看到查询并没有用到索引,发生了全表扫描
分析:第一次查询的两个字段刚好是建立索引的两个字段,而第二次查询字段相对于建立的索引多了一个,所以第一次查询发生了索引覆盖,所以建议select查询时只写必要的字段,该覆盖的可能性会提升
字段要独立出现在等式的一侧若没有独立出现则不能触发该字段上的索引
例如:在student表中,id为主键。则下列语句:
select * from student where id=20
或select * from student where 21-1=id
都使用了主键索引
但如下语句就不能触发主键索引:
select * from student where id+1=20
MySQL中的通配符:
%,任意字符的任意数量,正则表示:/.*/
_,任意的一个字符。以正则表示/./
如:在user表中的name字段已经建立了索引,则:
select * from user where name like '%亮'
此查询语句就不会使用索引查询,因为like查询%在开头
select * from user where name like '张%'
此like查询语句中,%没有在开头,所以次查询用到了索引
注意:
字符串比较时,不能使用包含的逻辑,标题中包含“PHP”的内容:subject like '%PHP%'
,实际操作中一定不能使用。
包含逻辑的解决方案:全文索引。第三方提供的全文索引完成。
实际开发中like可以使用,但是仅仅可以使用在匹配开头的情况。例如,搜索建议:PHP:like ‘PHP%’
PHP培训
PHP框架
PHP开发
index name(first_name,last_name);
独立对first_name和last_name去执行索引,效果是不同的
使用左侧字段查询
select * from student where first_name=?
;采用了索引查询使用右侧字段查询
select * from student where last_name=?
;没有采用索引查询注意:复合索引,右边字段相当于没有检索索引
原因:复合索引是先根据左侧字段排序,如果左侧字段相同,再根据右侧字段排序。意味着总体上右侧字段是没有排序的。有点类似于order by查询(order by first_name,last_name)
复合索引的使用场景是?组合条件查询
常规的查询,是first_name 和 last_name通常一起出现。
where first_name like’xxx%’ and last_name like ‘xxx%’
此时就要比分别在first_name 和 last_name上建立索引要高效一些
分别建立索引,查询时就要使用两个索引,计算两个索引的交集才可以
cond1 or cond2
同时保证cond1和cond2都有索引才可以。
gender 0,1,2,表示男,女,保密
值的数量较少
支付状态:未支付,已支付
配送状态:未配送,配送中,已收货
这样的状态值即使在字段上加了索引,往往也不能起作用。
测试相同时才查询比对状态值字段和 非状态值字段
语法原因:
会导致一个状态值同时匹配大量的记录,查询大量的记录,MySQL有时会认为用索引的开销要比全表扫描还要大,主动放弃索引。
举例:
一个大厅一楼有一个公司门牌号的表,假如要去找一个公司,直接看门牌号的表,然后去找对应的公司即可。但是如果总共有100个公司,要找50家公司,每次找了一个公司,然后再返回一楼寻找下一家公司的门牌号,这样还不如不看门牌号,直接把所有的公司都看一遍。
建立基础索引,在where , order ,join 字段上建立索引
优化组合索引,基于业务逻辑
语法:alter table 表名 create index(field(10))
含义:使用field字段的前10个字符建立索引。默认情况使用字段的全部内容建立索引。
前缀的标识度足够的情况下,需要使用前缀索引
例如:密码字段就适合使用前缀索引
select count(*) / count(distinct (password)) from student
,算出使用整个的密码字段所能达到的最大密码标识度。假设等于1.0038.select count(*) / count(distinct left(password,1)) from student
,逐个的改变前缀数,直到标识度能够达到1.0038为止。Btree索引、hash索引、聚簇索引
以上概念,指的是,索引的存储结构是数据结构上的概念(与使用关系不大)
索引存储在磁盘上所用的基础的通用存储结构,无论MySQL、MongoDB,或者其他的数据库,在磁盘上存储索引时,用的都是Btree树结构。
特点:
一个Btree节点存储多个索引关键字。多少是由节点的大小和关键字的大小来确定的,通常节点的大小是固定的,由计算机文件系统来确定,磁盘一次性读取内存容量(512kb)就是一个节点的大小。
大量的关键字分散到多个节点上进行存储。
通过上层节点的子节点指针,指向下层节点,来关联所有的节点。节点指针位于关键字之间。
指针指向的关键字的顺序,一定位于指针两侧的关键字之间
无论是普通,唯一,主键,全文索引都存储为Btree结构
辨析:
Btree是多路平衡查找树,BinaryTree是二叉树。
关键字和记录在一起存储,称之为聚簇结构,聚簇索引。
常规的,索引是关键字和位置的映射关系
而聚簇索引不是关键字和位置的映射,而是关键字和记录就存储在一起
在数据结构上聚簇索引采用的是B+Tree结构。
在MySQL中仅仅是innodb的主键索为聚簇索引。其他的索引包括innodb的非主键索引都是典型的Btree结构。空间固定,往往占用的空间比较小
当数据被载入到内存时采用hash结构存储。
是MySQL层面提供的数据缓存,用于缓存select查询结果。
找到mysql安装位置的my.ini文件(linux下是my.cnf)
在[mysql]段中,添加配置
query _cache_type=0;
(0表示关闭缓存;1表示开启,但是默认缓存,需要加sql-no-cache提示放弃缓存;2表示开启,但是默认不开启缓存,需要加sql-cache开启缓存)
一般情况下以2为主
配置完成后,需要重启mysql才能生效
重启之后在客户端,使用:show variables like 'query_cache_type';
查看结果
[外链图片转存失败(img-gJ43KuRp-1568707495139)(C:\Users\liu\AppData\Roaming\Typora\typora-user-images\1553734791386.png)]
配置项:query_cache_sieze来配置
先查看一下:
show variables like 'query_cache_size';
[外链图片转存失败(img-GoOO9Knd-1568707495140)(C:\Users\liu\AppData\Roaming\Typora\typora-user-images\1553735080433.png)]
可以改变缓存的大小:
set gloabal query _cache_size = ·····;
[外链图片转存失败(img-LdD8suZj-1568707495140)(C:\Users\liu\AppData\Roaming\Typora\typora-user-images\1553735650285.png)]
如果缓存开启参数设置为1,那么直接查询即可,若要不缓存,则加上sql-no-cache即可
如果缓存参数设置为2,那么需要在查询时加上sql-cache提示使用缓存
如:select sql-cache * from student limit 5
;查询消耗时间0.03s
第二次再次执行查询语句select sql-cache * from student limit 5
;查询消耗的时间为0s;
reset query cache 完成清空缓存。
当数据表发生变动时,基于该数据表的任何缓存都会被清空。表层面的管理。
当数据有明显的业务逻辑差异时,应选择条件分配,若只想让数据分开存储,应选择hash或者key这种均匀存储。
将一张表中的数据和索引分散到不同的文件中进行存储,称为分区操作。划分出来的文件就是不同的分区。
分布之后,每个文件中包含的记录数量显著减少,保证单独文件的执行效率。
演示:最常见的分区语法,使用id,将数据分布到10个分区中
id主键是最主要的检索要素。
在设计表时,使用partition选项,可以完成分区配置:
--利用id字段,使用hash算法,将数据分布到10个分区内
create table articles(
id int unsigned auto_increment primary key,
subject varchar(255),
content text
)
partition by hash(id) partitions 10;
使用一个整数值,将记录分布到分区中,采用求余方案。
hash:一类算法的总称,求余,md5,sha1,都是hash算法。只要可以使用某个输入,得到某个特定的输出的算法就是hash算法,要求相同的输入应该得到相同的输出。
hash-table,哈希表。特殊的数据结构。key就是输入,value就是输出。
hash分区算法,在逻辑上表示均匀分配。
也是一个hash算法,更加通用的hash算法。在hash中,仅仅可以对整数进行求余运算。在key算法中,可以使用非整型字段,输入数据不一定为整数。核心与hash一致,但是要先通过分区字段计算一个整数值,再完成求余操作。
也可以使用字符串将文章分布到10个区域中。
分区字段一定是主键的一部分,分区字段一定是强检索字段。
范围条件算法,主要使用<(小于)来实现条件,分区时严格指定每一个分区条件,不仅仅是指定分区的数量。
是一种分区算法,指的是,将数据使用某种条件分散到不同的区中。
演示:利用文章的不同发布时间 ,将分区分不到不同的区域中:
create table articles_range(
id int unsigned auto_increment;
subject varchar(255),
content text,
pubtime int,
primary key(id,pubtime)
) charset=utf8 engine=innodb
partition by range(pubtime)(
partition p201710 values less than (1509465599),
partition p201711 values less than (1512057599),
partition p201712 values less than (1509473599)
);
仅仅支持 测试: 分析:150965499小于第一个分区,所以改动的是第一个分区的文件。 条件范围算法 此时,改动的是第二个分区的文件。 list,range可以删除或者新增分区 hash,key可以修改分区数量 在客户端程序不变的情况下,将服务器的数据分不到不同的物理文件中,进而做到提高数据表的处理能力。 当数据表中数据量很大时,分区可以提升效率。 只有检索字段为分区字段时,分区效率才会比较明显。因此分区字段的选择很重要。业务逻辑应该根据分区字段做出调整。 可以将分区文件部署到不同的磁盘上,充分利用磁盘的性能 通常指的是,通过应用程序,将数据划分到不同的表中进行存储。而分区是在服务器完成的分区算法。 分表会导致客户端明显改变,在服务器端,出现结构相同的多张表。 分区和分表都称之为水平分割 数据库减压, 分区算法的局限。(重要原因) 数据库支持不完善,分区在5.1之后才支持的 水平分割:每个表的结构相同,分别存储数据 垂直分割:每个表的记录数量一致,但是字段不一致。分表之间是一对一的关系。分割一依据:在业务逻辑中经常在一起使用的字段分布到一个表中。 由多台MySQL服务器提供数据存储服务器。 横向扩展是根本提升数据库性能的手段,受限于单台计算机的硬件处理能力,使用多台计算机完成对同一个服务的支持。 比较典型的概念: 读写分离,负载均衡。 需要用到的技术:MySQL的复制技术,负载均衡中间件。insert into articles_range values(
null,
150965499
);
list算法
create table articles_list(
id int unsigned auto_increment;
subject varchar(255),
content text,
pubtime int,
status tinyint,-- enum('写作中','完成','发布')
primary key(id,pubtime)
) charset=utf8 engine=innodb
partition by list(status)(
partition pwriting values in(1,2),
partition ppublished values in (3)
);
insert into articles_list values(
null,
150965499,
3
);
分区管理语法
list,range增加分区
alter table articles_range add partition(
partition p201801 values less than (1517414400),
partition p201802 values less than (1519833600)
);
list/range删除分区
alter table articles_range drop partition p201710
删除名为p201710的分区key/hash增加分区
alter table articles add partition partitions 4
,在原来的基础上增加4个分区key/hash减少分区
alter table articles coalesce partition 6
,在原来的基础上减少6个分区key和hash的分区管理,不影响数据。在管理时,需要将数据重新分配到新的分区中,效率较低。尽量在设计阶段,考虑分区的总数,满足设计。
分区的使用
分表
分表的原因
垂直分割和水平分割
横向扩展技术