MYSQL优化

MySQL如何优化

1. 数据库设计要合理
2. 添加索引(普通索引、朱建索引、唯一索引、全文索引)
3. 分表分库技术(取模分表、水平分表、垂直分表)
4. 读写分离
5. 存储过程
6. 配置mysql最大连接数my.ini
7. mysql服务器升级
8. 随时清理碎片化
9. sql语句调优

数据库三大范式

  • 第一范式:1NF是对属性的原子性约束,要求属性(列)具有原子性,不可再分
    解;(只要是关系型数据库都满足1NF)
    第二范式:2NF是对记录的惟一性约束,表中的记录是唯一的, 就满足2NF, 通常
    我们设计一个主键来实现,主键不能包含业务逻辑。
    第三范式:3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数
    据库设计可以做到。
    但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须
    降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守
    第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增
    加字段,允许冗余。

分表分库

  • 垂直拆分
  • 水平拆分
  • 使用取摸方式分表
    首先我创建三张表 user0 / user1 /user2 , 然后我再创建 uuid表,该表的作用
    就是提供自增的id。
create table user0(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd varchar(32) not null default '')
engine=myisam charset utf8;
create table user1(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd varchar(32) not null default '')
engine=myisam charset utf8;
create table user2(
id int unsigned primary key ,
name varchar(32) not null default '',
pwd varchar(32) not null default '')
engine=myisam charset utf8;
create table uuid(
id int unsigned primary key auto_increment)engine=myisam charset utf8;

创建一个demo项目
POM文件

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

Service代码

@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public String regit(String name, String pwd) {
// 1.先获取到 自定增长ID
String idInsertSQL = "INSERT INTO uuid VALUES (NULL);";
jdbcTemplate.update(idInsertSQL);
Long insertId = jdbcTemplate.queryForObject("select
last_insert_id()", Long.class);
// 2.判断存储表名称
String tableName = "user" + insertId % 3;
// 3.注册数据
String insertUserSql = "INSERT INTO " + tableName + " VALUES ('"
+ insertId + "','" + name + "','" + pwd
+ "');";
System.out.println("insertUserSql:" + insertUserSql);
jdbcTemplate.update(insertUserSql);
return "success";
}
public String get(Long id) {
String tableName = "user" + id % 3;
String sql = "select name from " + tableName + " where id="+id;
System.out.println("SQL:" + sql);
String name = jdbcTemplate.queryForObject(sql, String.class);
return name;
}
}

Controller

@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/regit")
public String regit(String name, String pwd) {
return userService.regit(name, pwd);
}
@RequestMapping("/get")
public String get(Long id) {
String name = userService.get(id);
return name;
}
}

配置文件

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

SQL优化

如何从一个大项目中,迅速的定位执行速度慢的语句. (定位慢查询)

show status

使用show status使用show status查看MySQL服务器状态信息
常用命

--mysql数据库启动了多少时间
show status like 'uptime';
show stauts like 'com_select' show stauts like 'com_insert' ...
类推 update delete(显示数据库的查询,更新,添加,删除的次数)
show [session|global] status like .... 如果你不写 [session|global] 默认是
session 会话,指取出当前窗口的执行,如果你想看所有(从mysql 启动到现在,则应该
global)
//显示到mysql数据库的连接数
show status like 'connections ';
//显示慢查询次数
show status like 'slow_queries';

慢查询

什么是慢查询
MySQL默认10秒内没有响应SQL结果,则为慢查询
可以去修改MySQL慢查询默认时间

如何修改慢查询

--查询慢查询时间
show variables like 'long_query_time';
--修改慢查询时间
set long_query_time=1; ---但是重启mysql之后,long_query_time依然是my.ini中的值

如何定位慢查询

创建表结构

/*部门表*/
CREATE TABLE dept(
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
dname VARCHAR(20) NOT NULL DEFAULT "", /*名称*/
loc VARCHAR(13) NOT NULL DEFAULT "" /*地点*/
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
/*员工表*/
CREATE TABLE emp
(empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
)ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
/*薪水*/
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2) NOT NULL,
hisal DECIMAL(17,2) NOT NULL
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
/*测试数据*/
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);

创建函数

create function rand_string(n INT)
returns varchar(255) #该函数会返回一个字符串
begin
#chars_str定义一个变量 chars_str,类型是 varchar(100),默认
值'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare chars_str varchar(100) default
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i = i + 1;
end while;
return return_str;
end
create FUNCTION rand_num()
RETURNS int(5)
BEGIN
DECLARE i int default 0;
set i =floor(10+RAND()*500);
return i;

创建存储过程

delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
#set autocommit =0 把autocommit设置成0
set autocommit = 0;
repeat
set i = i + 1;
insert into emp values ((start+i)
,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
until i = max_num
end repeat;
commit;
end $$
执行存储过程
call insert_emp (100001,40000000);

如何将慢查询定位到日志中

在默认情况下,我们的mysql不会记录慢查询,需要在启动mysql时候,指定记录慢查询才可
以
bin\mysqld.exe --safe-mode --slow-query-log [mysql5.5 可以在my.ini指定](安全模
式启动,数据库将操作写入日志,以备恢复)
bin\mysqld.exe –log-slow-queries=d:/abc.log [低版本mysql5.0可以在my.ini指定]
先关闭mysql,再启动, 如果启用了慢查询日志,默认把这个文件放在
my.ini 文件中记录的位置
#Path to the database root
datadir=" C:/ProgramData/MySQL/MySQL Server 5.5/Data/"

索引

什么是索引

索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式
保存

  • 索引
    为什么要索引?-----提高查询效率,为什么加了索幻能够提高查询效
    率…索引实现原理折半查找
    索引分类:
    主键索引—primary key
    唯一索引
    组合索引
    全文索引
    普通索引

    1.不要like
    企业实际中不会采用表的全文索引
    全文索引缺点:
    第三方搜索索引框架slor、es等
    索引优缺点:
    优点:提高程序效率
    缺点:增加、删除慢,索引文件需要更新、增加内存
    什么字段适合加索引?
    select * from user where sex=*S’;—不需要
    查询次数比较,值有非常多的不同。
    select * from user where userid=112;—需要
    建立索引where条件需要查询的,并且值非常多的不
    同。唯一几个值,不需要建立索引
    第一个可以不用第二一起作为条件查找…会使用组索引进行查

    第二个条件,不使用第一个条件----不会使用组合索幻查找
    使用第一个条件和第二条件一起查找使用索引进行

  1. 条件加like的效果
    使用索幻的时候,不要likei%%'否则全表扫描
    使用like like开头不要% like '%
  2. 使用or,条件都必须加上索?|,只要有 一个条件不加索51,
    不会使用索幻查找
  3. 判断是否为null,is null不要=null
  4. group by分组不会使用索幻,全表扫描
    6 分组需要效率高,禁止兩f序。ordery null
    7.select * from userld> = 101 select * from userld>100
    不要使用大于=
    8.in no tin
    9.查询量非常大,缓存、分表、分页。
    索引

主键索引

  • 主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过
    AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般
    在创建表的时候指定,例如“CREATE TABLE tablename ( […], PRIMARY KEY
    (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER
    TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主
    键。

创建主键索引

  • 主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过
    AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般
    在创建表的时候指定,例如“CREATE TABLE tablename ( […], PRIMARY KEY
    (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER
    TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主
    键。
    当一张表,把某个列设为主键的时候,则该列就是主键索引
    create table aaa
    (id int unsigned primary key auto_increment ,
    name varchar(32) not null default ‘’);
    这是id 列就是主键索引.
    create table bbb (id int , name varchar(32) not null default ‘’);
    如果你创建表时,没有指定主键索引,也可以在创建表后,在添加, 指令:
    实例:
    alter table 表名 add primary key (列名);
    删除主键索引
    alter table articles drop primary key;
    查询索引
    desc 表名; 不能显示索引名称
    show index from 表名
    show keys from 表名

全文索引

创建表结构

CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
)engine=myisam charset utf8;
INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','DBMS stands for DataBase ...'),
('How To Use MySQL Well','After you went through a ...'),
('Optimizing MySQL','In this tutorial we will show ...'),
('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
('MySQL vs. YourSQL','In the following database comparison ...'),
('MySQL Security','When configured properly, MySQL ...');
错误用法:
select * from articles where body like '%mysql%'; 错误用法 索引不会生效
正确用法:
select * from articles where match(title,body) against ( 'database')

说明:

  • 在mysql中fulltext 索引只针对 myisam生效
    mysql自己提供的fulltext针对英文生效->sphinx (coreseek) 技术处理中文
    使用方法是 match(字段名…) against(‘关键字’)
    全文索引:停止词, 因为在一个文本中,创建索引是一个无穷大的数,因此,对
    一些常用词和字符,就不会创建,这些词,称为停止词.比如(a,b,mysql,
    the)
    mysql> select match(title,body) against (‘database’) from articles;(输
    出的是每行和database的匹配度)

唯一索引

  • 这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只
    能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:
    创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的
    列表);
    修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列
    表);
    创建表的时候指定索引,例如CREATE TABLE tablename ( […], UNIQUE [索引
    的名字] (列的列表) );
    创建表结构
create table ddd(id int primary key auto_increment , name varchar(32) unique);

注意

  • unique字段可以为NULL,并可以有多NULL, 但是如果是具体内容,则不能重
    复,
    但是不能存有重复的空字符串""

普通索引

  • 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问
    速度。因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序
    条件(ORDERBYcolumn)中的数据列创建索引。只要有可能,就应该选择一
    个数据最整齐、最紧凑的数据列(如一个整数类型的数据列)来创建索引。
	create table ccc(
		id int unsigned,
		name varchar(32)
	)
	create index 索引名 on 表 (列1,列名2);

MySQL explain执行计划解读

  • MySql 提 供 了 EXPLAIN 语 法 用 来 进 行 查 询 分 析 , 在 SQL 语 句 前 加 一
    个"EXPLAIN"即可。比如我们要分析如下SQL语句:
    explain select * from table where table.id = 1
    运行上面的sql语句后你会看到,下面的表头信息:
    table | type | possible_keys | key | key_len | ref | rows | Extra
EXPLAIN列的解释
  • table
    显示这一行的数据是关于哪张表的
    type
    这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、
    eq_reg、ref、range、indexhe和ALL
    说明:不同连接类型的解释(按照效率高低的顺序排序)
    system:表只有一行:system表。这是const连接类型的特殊情况。
    const :表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索
    引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它
    当做常数来对待。
    eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都
    从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用。
    ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部
    分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录
    都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。
    range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西
    时发生的情况。
    index:这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更
    好,因为索引一般小于表数据)。
    ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟
    糕,应该尽量避免。
    possible_keys
    显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域
    从WHERE语句中选择一个合适的语句
    key
    实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会
    选 择 优 化 不 足 的 索 引 。 这 种 情 况 下 , 可 以 在 SELECT 语 句 中 使 用 USE
    INDEX ( indexname ) 来 强 制 使 用 一 个 索 引 或 者 用 IGNORE
    INDEX(indexname)来强制MYSQL忽略索引
    key_len
    使用的索引的长度。在不损失精确性的情况下,长度越短越好
    ref
    显示索引的哪一列被使用了,如果可能的话,是一个常数
    rows
    MYSQL认为必须检查的用来返回请求数据的行数
    Extra
    关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏
    的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,
    结果是检索会很慢
    说明:extra列返回的描述的意义
    Distinct :一旦mysql找到了与行相联合匹配的行,就不再搜索了。
    Not exists :mysql优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的
    行,就不再搜索了。
    Range checked for each Record(index map:#) :没有找到理想的索引,
    因此对从前面表中来的每一个行组合,mysql检查使用哪个索引,并用它来从表
    中返回行。这是使用索引的最慢的连接之一。
    Using filesort :看到这个的时候,查询就需要优化了。mysql需要进行额外的
    步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件
    的全部行的行指针来排序全部行。
    Using index :列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表
    返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。
    Using temporary :看到这个的时候,查询需要优化了。这里,mysql需要创
    建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不
    是GROUP BY上。
    Where used :使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回
    给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发
    生,或者是查询有问题。
    因此,弄明白了explain语法返回的每一项结果,我们就能知道查询大致的运行
    时间了,如果查询里没有用到索引、或者需要扫描的行过多,那么可以感到明显
    的延迟。因此需要改变查询方式或者新建索引。mysql中的explain语法可以帮
    助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。当
    然,在大规模数据量时,索引的建立和维护的代价也是很高的,往往需要较长的
    时间和较大的空间,如果在不同的列组合上建立索引,空间的开销会更大。因此
    索引最好设置在需要经常查询的字段中存储引擎

那些列上适合添加索引

查询作为查询条件字段应该创建索引
唯一性太差的字段不适合单独创建索引,即使频繁
Select * from emp where sex=’男’
频繁更新字段,也不要定义索引。
不会出现在where语句的字段不要创建索引
总结:满处一下条件的字段,才应该创建索引
1. 肯定在where条件经常使用
2. 该字段的内容不是唯一的几个值
3. 字段内容不是频繁变化

索引的注意事项

注意:
1.对于创建的多列索引,如果不是使用第一部分,则不会创建索引。
explain select * from dept where loc=‘aaa’\G
就不会使用到索引
2.模糊查询在like前面有百分号开头会失效。
3. 如果条件中有or,即使其中有条件带索引也不会使用。换言之,就是要求使
用的所有字段,都必须建立索引, 我们建议大家尽量避免使用or 关键字
4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使
用索引。(添加时,字符串必须’’), 也就是,如果列是字符串类型,就一定要用
‘’ 把他包括起来.
5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引。

查询所用使用率

  • show status like ‘handler_read%’;
    大家可以注意:
    handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
    handler_read_rnd_next:这个值越高,说明查询低效。

SQL优化技巧

  • 使用group by 分组查询是,默认分组后,还会排序,可能会降低速度,
    在group by 后面增加 order by null 就可以防止排序.
    explain select * from emp group by deptno order by null;
    有些情况下,可以使用连接来替代子查询。因为使用join,MySQL不需要在内
    存中创建临时表。
    select * from dept, emp where dept.deptno=emp.deptno; [简单处理方式]
    select * from dept left join emp on dept.deptno=emp.deptno; [左外连
    接,更ok!]
    对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉
    及的列上建立索引
    应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用
    索引而进行全表扫描,如:
    select id from t where num is null
    最好不要给数据库留 NULL,尽可能的使用 NOT NULL 填充数据库.
    备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用 NULL。
    不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定
    了, 不管是否插入值(NULL 也包含在内),都是占用 100 个字符的空间的,
    如果是 varchar 这样的变长字段, null 不占用空间。
    可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询:
    select id from t where num = 0

MySQL数据引擎

  • 使用的存储引擎 myisam / innodb/ memory
    myisam 存储: 如果表对事务要求不高,同时是以查询和添加为主的,我们考虑
    使用myisam存储引擎. ,比如 bbs 中的 发帖表,回复表.
    INNODB 存储: 对事务要求高,保存的数据都是重要数据,我们建议使用
    INNODB,比如订单表,账号表.
    MyISAM 和 INNODB的区别
    1. 事务安全(MyISAM不支持事务,INNODB支持事务)
    2. 查询和添加速度(MyISAM批量插入速度快)
    3. 支持全文索引(MyISAM支持全文索引,INNODB不支持全文索引)
    4. 锁机制(MyISAM时表锁,innodb是行锁)
    5. 外键 MyISAM 不支持外键, INNODB支持外键. (在PHP开发中,通常不设置外
      键,通常是在程序中保证数据的一致)
      Memory 存储,比如我们数据变化频繁,不需要入库,同时又频繁的查询和修改,我们考
      虑使用memory, 速度极快. (如果mysql重启的话,数据就不存在了)
      存储引擎对比图

Myisam注意事项

  • 如果你的数据库的存储引擎是myisam,请一定记住要定时进行碎片整理
    举例说明:
    create table test100(id int unsigned ,name varchar(32))engine=myisam;
    insert into test100 values(1,’aaaaa’);
    insert into test100 values(2,’bbbb’);
    insert into test100 values(3,’ccccc’);
    insert into test100 select id,name from test100;
    我们应该定义对myisam进行整理
    optimize table test100;
    本人摘自程序员大本营mysql优化
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值