打开浏览器 --> 网络 --> 发送到服务器 --> 操作数据库 --> 结果响应
数据库的基本结构
1.客户端层
处理连接,授权认证
2.mysql的核心服务层
查询解析,分析优化,缓存,内置函数(时间,数学...)
3.存储引擎
存数据
##数据库的设计和数据类型的优化
数据库设计的陷阱:
1.避免太多列
2.避免太多的关联,粗略的经验法则,单个查询最好在12表以内关联
3.防止过度使用枚举
4.范式和反范式(了解..)
范式优点和缺点:
更新快,没有或者很少有重复的数据
通常都有关联多个表才能查询想要的数据
反范式优点和缺点:
查询减少关联
存在冗余的数据
数据类型的优化:
1.更小的通常更好
2.简单就好,例子:操作数字比操作字符代价更低
3.尽量避免使用null
整型类型:18
tinyint 8位
smallint 16位
mediumint 24位
int 32位
bigint 64位
使用原则:根据需要
实数类型:18.88
decimal: 定点数据类型,存储精确的小数
decimal(m,d) m<65, d<30
float:单精度浮点型
float(m,d) 只能保证6位有效数字是精确,使用4个字节
double:双精度浮点类型,使用8个字节
double(m,d)
使用原则:
精度要求不高: float,double,比decimal少占用空间
精度准确: decimal,需要额外空间和开销
char(10) 定长型
varchar(10) 可变长度,通常情况下比char节省空间,varchar需要额外的字节去记录字符串的长度
使用原则:
不确定长度:使用varchar
确定长度:char
blob(二进制)和text(文本)存储大的数据
使用建议:大量数据,存储文件的路径
枚举类型:
使用枚举类型代替字符串,mysql存储枚举的时候,内部把每个值都保存为整数
例子:
创建表
CREATE TABLE enum_test(e ENUM('fish','apple','dog'));
插入数据:
INSERT INTO enum_test(e) VALUES('fish'),('dog'),('apple');
查询数据:
SELECT e+0 FROM enum_test;
注意:使用整数当做枚举,容易混乱
日期和时间类型:
datetime :使用8个字节 1001-9999
timestamp: 如果没有指定时间,会使用系统时间,使用4个字节1970-2038
bit: 位,尽量避免使用
创建高性能的索引
索引的优点:
1.减少服务器需要扫描的数据量
2.避免服务器的排序和临时表
3.将随机I/O变成顺序I/O
索引添加到哪些列上面比较好?
1.比较频繁作为查询条件字段应该创建索引
select * from tablename where english...
2.唯一性太差的字段不适合创建索引
select * from student where sex=男/女
3.更新比较频繁的字段不适合创建索引
4.考虑在where以及order by涉及的列上创建索引
例子:
创建表:
CREATE TABLE test(
id INT NOT NULL,
username VARCHAR(16) NOT NULL
)
插入100000数据:
DELIMITER #
CREATE PROCEDURE pro1()
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i<100000 DO
INSERT INTO test VALUES(i, CONCAT('aa', i));
SET i=i+1;
END WHILE;
END #
调用:
CALL pro1();
查询一条记录:
select * from test where id=54321;
通过explain查看执行信息
EXPLAIN select * from test where id=54321;
explain关键参数分析:
type:连接访问类型 一般来说,至少range级别,最好能够达到ref级别
rows:估计值,mysql认为获取一个查询结果需要扫描的行数
添加索引:
CREATE INDEX index_id ON test(id);
测试:
EXPLAIN select * from test where id=54321;
普通索引:加速查询
1.创建表时候创建索引
create table test2(
id int not null,
username varchar(200) not null,
index index_username (username(15))
)
2.使用create创建索引
CREATE INDEX index_username ON test2(username(10));
3.使用alter创建索引
ALTER TABLE test2 ADD INDEX index_username(username(10));
唯一索引:加速查询,唯一约束(可以含有null)
1.创建表时候
CREATE TABLE test3(
id INT NOT NULL,
username VARCHAR(20) NOT NULL,
UNIQUE INDEX index_username (username(10))
)
2.使用create
CREATE UNIQUE INDEX index_username ON test3(username(10));
3.使用alter
ALTER TABLE test3 ADD UNIQUE INDEX index_username(username(10));
复合(组合/多列)索引:
1.创建表
create table test4(
name varchar(20) not null,
age int not null,
index index_name_age(name(10), age)
)
2.使用create
CREATE INDEX index_name_age ON test4(NAME(10), age);
3.使用alter
ALTER TABLE test4 ADD INDEX index_name_age(NAME(10), age);
主键索引:加速查询,唯一约束(不可以含有null)
create table test5(
id int not null PRIMARY key AUTO_INCREMENT,
username varchar(20) not null
)
注意:创建索引的时候长度要合适
索引缺点:
1.降低表的更新速度
2.创建索引占用磁盘空间.
查询性能优化
查询优化分析步骤:
1.应用查询是否超过需要数据
select name, age, height form userinfo
2.mysql服务器是否分析超出需要的数据
SELECT * FROM test WHERE id=54321;
正确使用索引:
1.like语句的操作
一般不使用%或_开头
select * from tablename where name like "cn%"
2.组合索引
例如:索引 index index_name(a, b, c),只支持a或a,b或a,b,c 最左前缀
3.尽量避免在where子句进行null判断
例如: select * from t where num is null;
select * from t where num = 0;
4.尽量避免在where子句使用!=,or
5.in和not in也要谨慎使用
例子: select id from t where num in(1,2,3);
修改:对于连续的值,使用between
select id from t where num between 1 and 3;
6.如果在where子句中使用参数,也会导致全表扫描
mysql在 运行时候才会解析变量,mysql语句优化的时间在编译的时候
7.尽量避免在where子句的=左面进行运算
例如:select * from t where num/2 = 100;
修改:select * from t where num = 100*2;
例如:select * from t where substring(name, 1, 3) = 'abc';
select * from t where name like 'abc%';
其他的优化点:
1.尽量使用数字类型的字段
2.在join表要使用相当类型的字段,并将其建立索引
例如: select * from users left join company on users.state = company.state
3.尽量避免使用 order by rand();
4.尽量避免使用 select *
5.一定要为每张表设置主键,最好是int类型,最好设置auto_increment
6.优化count查询
select count(*)
7.不要添加重复或者冗余的索引,删除长期不用索引
例如:创建组合索引(A,B),不要再单独为(A)创建索引
8.尽量避免大的事务操作,降低并发性能
9.优化or条件
创建表
CREATE TABLE test6(
id INT NOT NULL,
NAME VARCHAR(20) NOT NULL,
INDEX index_id(id),
INDEX index_name(NAME(20))
)
插入数据
INSERT INTO test6 VALUES(1, 'zhangsan');
INSERT INTO test6 VALUES(2, 'lisi');
使用or
EXPLAIN SELECT * FROM test6 WHERE id=1 OR NAME='lisi';
使用union all
EXPLAIN SELECT * FROM test6 WHERE id=1 UNION ALL SELECT * FROM test6 WHERE NAME='lisi'
10.优化分页查询(limit)
创建表
create table test7(
id int not null,
username varchar(20) not null,
index index_id(id)
)
插入数据
DELIMITER #
create PROCEDURE pro2()
begin
DECLARE i int default 0;
while i<10000 do
insert into test7 values(i, concat('aa', i));
set i=i+1;
end while;
end #
call pro2();
分页查询数据
explain select * from test7 order by id limit 1000, 20;
优化:
EXPLAIN SELECT * FROM test7 INNER JOIN (SELECT id FROM test7 ORDER BY id LIMIT 1000, 20) AS o USING(id);
EXPLAIN SELECT * FROM test7 t INNER JOIN (SELECT id FROM test7 ORDER BY id LIMIT 1000, 20) AS o ON t.id = o.id
不同存储引擎需要配置参数不一样,选择InnoDB
注意:
1.值不是越大越好
2.不要随意修改,大部分情况下只有很少的参数真正重要
两个重要参数:
1.InnoDB缓冲池
innodb_buffer_pool_size:一般情况可以设置为操作系统的内存70%-80%
2.InnoDB事务日志
减少事务的提交的开销,在日志记录事务
日志是环形写入,写到尾部,就会跳到开头重新写入
innodb_log_file_size:日志文件的大小,设置这个值缓冲池20%-100%
innodb_log_buffer_size:日志文件的缓冲区大小,默认1M,推荐1M-8M
innodb_flush_log_at_trx_commit:控制日志缓冲的频繁程度
cpu : 多和快之间选择,一般选择快(其他条件相同)
内存 : 大内存,位宽高,尽量不要出现SWAP(内存不足,用硬盘暂时存放内存中数据)
硬盘 : 机械(高转速),固态(ssd, PCIe)
操作系统: 尽量64位,可以利用更多的内存
应用层的优化
1.不要请求不需要的数据
2.应用处理本该由数据库处理的事情,或者反过来
例子:获取所有记录,count(*), 使用java代码,select * from table , list.size()
例子:在数据库重执行复杂的字符串操作
3.创建了没有必要mysql连接,使用连接池即可
4.尽量减少长连接使用
5.不使用的连接及时断开
6.缓存数据,用户昵称,id