MySQL数据库优化技术一

纵论

对mysql优化时一个综合性的技术,主要包括 

  1. 表的设计合理化(符合3NF)
  2. 添加适当索引(index)  [ 四种: 普通索引、主键索引、唯一索引unique、全文索引 ]
  3. 分表技术( 水平分割、垂直分割 ) 水平分割根据一个标准重复定义几个字段值相同,表名称不同的表,表的结构相同
  4. 读( 将查询query分配到多个数据库中,子数据库);写(主数据库用来写)[ 写: update/delete/add ]分离
  5. 存储过程 [ 模块化编程,可以提高速度 ],存储过程其实是一个数据对象,从数据库的三层结构来说,分为 客户端java程序---dbms(数据库管理系统)通常说的数据库指的是数据库管理系统---db(本质就是一个文件),java程序一个sql语句发送到dbms的时候,dbms会将sql语句进行编译,然后执行,执行过后将数据暂时缓存一下,第二次操作的时候,数据就会快。Sql语句最致命的问题就是,sql编译很耗费时间,这时候有人就提出了将经常使用的语句在数据库里面编码,让它形成一个二进制文件,到时候直接调用,这样提高速度,存储过程,最常见的就是,分页存储过程,配合触发器的存储过程,存储过程是一个已经编译好,这时候就会直接执行,存储过程有利于模块化开发。存储过程的致命缺点:移植性不好
  6. 对mysql配置优化 [ 配置最大并发数my.ini  max_connections=100,一般网站调整到1000左右即可,如果更大会把内存撑破; 调整缓存大小 ]
  7. mysql服务器硬件升级,服务器内存的提高,优化到位了,硬件不够要向上反馈提高硬件性能
  8. 定时的去清除不需要的数据,定时进行碎片整理( MyISAM引擎要定时清理,不然会越来越大 ) 

优化技术根据不同业务场景也不同,但是底层一些使用方式和原理是通用的,本篇博文从最基本的数据库基本字段类型展开优化技术的描述,详细如下

一、列类型的概念

整数类型:

从数学上来讨论tinyint

  1. 占据空间:指定的时候就已经分配好空间1字节
  2. 存储范围:就是能存储的现实世界的数据范围

数学推算

[0][0][0][0][0][0][0][0];

[1][1][1][1][1][1][1][1];

如果表示负数,可以用最高位来标志符号位,

思考:此时的表示范围

-128-->127 (只有后7位表示绝对值,最高位表示的是符号)至于为什么是-128,原因在于这里面在有符号的时候采用(补码)的形式推算,往前补一位,最后结果-1即可,所以这个是范围的由来。

类型                    字节             位             无符号                 有符号

Tinyint                  1                 8             0-->255              -128--->127

Smallint                2                16           0-->2^16-1          -2^15-->2^15-1

Mediumint            3                 24          0-->2^23-1          -2^23-->2^23-1

Int                        4                 32           0-->2^32-1          -2^31-->2^31-1

Bigint                   8                 64           0-->2^64-1          -2^63->2^63-1

其中的范围就是现实世界中的数据范围

Tinyint默认是有符号的存储-128--> 127

Tinyint(M) unsigned zerofill

❶    M:宽度(在0填充的时候才有意义)也就是在zerofill的时候才有意义

❷    Unsigned: 无符号类型(非负)影响存储范围

❸    Zerofill:0 填充,默认无符号,说明无符号位,长度一致

❹    一般tinyint(5) zerofill 就可以表示 tinyint unsigned zerofill,所以没有必要多写以个unsigned

❺    比如插入的数据是34的时候,那么最后添加进去展示的效果是00034,这只是为了数据的显示效果,与数据的表示没有关系,没有zerofill就没有必要声明M了。

❻    列的默认值,而且推荐声明默认值,往往这样配合使用,Not NULL default

比如表中的age字段就可以用 tinyint表示,因为tinyint在无符号的情况下可以存储0-255范围的数据。

比如绿毛龟则可以用smallint表示年龄

Int可以表示的数据类型的范围是40多亿,但是实际开发过程中,一旦数据的长度超过了某一范围,就不会用int来存储。

比如int(5)中这个5代表的就是宽度,当没有zerofill这个关键字的时候,存储的效果是没有变化的,放进去是什么样的,显示的就是什么样的,如果添加了zerofill,那么就可以对存储的数据进行显示方向的修改,比如存储的是123,那么查询出来之后的显示就是00123,如果存储的是99999999,宽度超过了5,那么存储进去的就是99999999,显示的还是99999999,这就是宽度的显示方面的作用了,影响展示效果

小数型/浮点型 定点型:

Float(M,D),M代表总位数,D代表小数位数,不包括小数点,也不含正负号(6,2)为例-9999.99-->9999.99

Float(4,2) (unsigned) -99.99-->99.99 (0.00-99.99)

那么float占几个字节呢,在声明float的时候,就已经指定了它所占的字节数,4个字节或者8个字节。因为在计算机里面表示float比较复杂,所以都用的比较大。

当M<=23时用4个字节,当M>23位数的时候,就用8个字节

字符型:

Char:定长字符串,char(M)M代表宽度,可容纳的字符数为M,0<=M<=255之间,char(10)表示能输入10个字符,但是前提不要忘记M在char中最多只能255个字符,char型,如果不够M个字符,末尾用空格补齐,取出来的时候,将尾部空格再去掉。所以数据后面本身就有空格存进去,但最后还是会被去掉

varchar:变长字符串,varchar(M)M代表宽度,可容纳的字符数为M,0<=M<=65535(以ascii字符为例,utf22000个汉子左右)

一个汉子就是一个字符,字符在不同字符集下所占的字节数是不一样的,gbk占两个字节,utf-8占3个字节,一个英文字母在任何编码集下都占用一个字节

每个字符都有一个ASCII码,一个ASCII码占一个字节,汉字在不同的编码集下占用的字符不同

区别在哪里:

char定长,那么就是不变,如果存的小于M个字符,实占M个字符

Varchar:M个字符,存的小于M个字符,设为N,N

类型            宽度    可存字符    实存字符i<=M    占用空间                    利用率
Char             M            M                   i                       M                               i/M<=100%
Varchar         M            M                   i                       i字符+(1-2)字节      i/(i+1-2)<100%

如何选择:定长速度快

考虑两个要素即可

选择原则:

  1. 空间利用效率,成语表则可以选char,个人简介或者微博(140)可以用varchar,这个可变

用户名:varchar 造成速度变慢,宁可让字节多浪费几个,而选择用char,没有定死的规则,根据实际变通。

  1. 速度

Text的不好的地方就是不能全文索引,搜索速度慢,不知道现在是否可以。

结论:

如果不是特别大的内容建议用char,varchar来代替

Text不用就加默认值(加了也没用)

日期时间类型:

Year类型:1个字节,表示 1901-2155,少了一个位数是为了防止出错的情况下做的操作,出错用的是0000表示

比如:如果输入两位,范围在00-69之间表示2000-2069

70-99表示1970-1999年,所以嫌弃麻烦的话,直接用4位输入即可

Date类型:                                典型格式1992-08-12

日期类型:                                1000-01-01---9999-12-31

Time 典型格式:                        HH:mm:ss

时间范围:                                -838:59:59 ---838:59:59

Datetime类型 典型格式:         1989-05-05 14:32:03

日期时间类型和范围:              1000-01-01 00:00:00 ----9999:12-31 23:59:59

注意:在开发中,很少使用日期事件类型来表示一个需要精确到秒的列

原因:虽然日期事件类型能精确到秒,而且查看方便,但是计算不方便,比如一个用户注册时间在某一个时间点,要求统计出距离这个时间点30分钟的注册用户查出来,那么就不方便了,到这里我就明白了之前看项目过程中,为什么创建时间要用timestamp来做了,而更新时间用datetime来存储了

Timestamp 时间戳用int来存储,方便计算,对于显示来说也可以方便的格式化,可以格式化成想要的格式

Java中用long表示毫秒数,变量用long保存,与之对应的MySQL的数据类型就是bigint,占用8个字节

Enum和set枚举型和集合类型:

关系型数据库中,不要出现一个列可以有多个值的现象,这样列的值就不唯一,还不如用一张表来表示各种字段

建表案例:

现在设计一张表:

姓名:                char(3)

年龄:                tinyint unsigned

Email:                 varchar(30),

Telephone:          char(11)

Introduction:        varchar(1000)

Salary:                decimal(7,2)

报道日期:         date

Engine:               myisam,

Charset:              utf-8

date是1970-01-01 00:00:00到当前的秒数,一般存注册时间,商品发布时间等,并不是datetime存储,而是用时间戳,因为datetime虽然直观,但是计算不便

Limit 【offset】 ,N

offset:偏移量

N:取出的总条数

二、五种子句:

Where子句:条件查询,运用在各种场合

Group 子句:

Having 子句:

Order by 子句:

Limit 子句:

五种子句的先后顺序要记住

三、三种子查询

1.什么是子查询?

当一个查询是另一个查询的条件时,称之为子查询。

2.子查询有什么好处?

子查询可以使用几个简单命令构造功能强大的复合命令。

Where 子查询:指把内层查询的结果作为外层查询的比价条件

Exist 子查询:把外层的查询结果拿到内层,看内层的查询结果是否成立

语法:SELECT ... FROM table WHERE EXISTS (subquery)

该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(TRUE 或 FALSE)来决定主查询的数据结果是否得以保留。

article 文章表:
MySQL数据库优化技术一_第1张图片

 user 用户表:
MySQL数据库优化技术一_第2张图片

 我们要查出 article 表中的数据,但要求 uid 必须在 user 表中存在。SQL 语句如下:

SELECT * FROM article WHERE EXISTS (SELECT * FROM user WHERE article.uid = user.uid)

定义:exists子查询就是对外层表进行循环,再对内表进行内层查询。和in ()差不多,但是它们还是有区别的。主要是看两个张表大小差的程度。

若子查询表大则用exists(内层索引),子查询表小则用in(外层索引);

效率的区别就在于使用的索引(where后面的变量就是我们使用的索引)不同摆了,我们可以对大表使用索引提高搜索速度。

From 子查询:指把内层的查询结果当成临时表,供外层sql再次查询

语法:SELECT ... FROM (subquery) AS name ...

例子:FROM 子查询 SQL 如下:
SELECT s1,s2 FROM (SELECT s1, s2*2 AS s2 FROM table1) AS temp WHERE s1 > 1

总结:

5种子句的使用顺序 where , group , having, order by , limit

DML:对应CRUD

四、Union的用法:

合并查询的结果(取select结果的并集)

❶    对于重复的行,去掉

❷    如果不去重复,可以用union all

Union的要求:

❶    各select查出的列数一致

❷    如果子句中使用功能了 order by limit ,那么子句要用“()”包裹起来

❸    如果子句只用order by 没有limit ,Order by 会被优化掉,不起作用

五、左连接,右连接,内连接,MySQL没有外链接

左连接与右连接可以相互转换,join后的为主表

内连接:左右连接的交集

两张表能相互匹配上的行

六、DDL语句,列的增删改

增加列:

❶    Alter table 表名 add 列声明

❷    增加的列默认是在表的最后一列

❸    可以用after来声明新增的列在哪一些列后面

❹    Alter table 表名 add 列声明 after username

❺    如果新增的列在最前面怎么做

❻    Alter table 表名 add 列声明 first

修改列名:

        Alter table 表名 change 被改变的列名  新列名 列声明

删除列名:

        Alter table 表名 drop 列名

七、视图:

create [algorithm=merge[temptable|undefined]] view viewName as select ....

如果某个查询结果出现的非常频繁,也就是,拿这个结果进行子查询出现的非常频繁

视图的定义:

        视图是由查询结果形成的一张虚拟表

创建语句:

        Create view 视图名称 as select 语句;

删除视图:

Drop view

使用的时候,直接将其当做表来使用就可以了

为什么要视图:

  1. 可以简化查询
  2. 可以进行权限控制:把表的权限封闭,但是开放相应的视图权限,视图里只开放部分数据
  3. 大数据分表的时候可以用到:比如,表的行数超过200万的时候,就会变慢,可以把一张表的数据拆成4张表来存放,存放的方法最常用的方法可以用ID进行取摸计算,拆成几张表就是摸几

视图与表的关系:

视图是表的查询结果,那么表的数据改变了,自然影响视图的结果

视图增删改也会影响表,但是视图并不是总是能增删改的,当视图的数据与表的数据一一对应的时候,就可以改变,否则不能,对于视图的insert还应该注意,视图必须包含表中没有默认值的列,也就是视图中的列有个列没有默认值,那么插入的时候就要给他赋值

视图的算法algorithm

Algorithm = merge/temptable/undefined

Merge :(合并)当引用视图时,引用视图的语句与定义视图的语句合并

Temptable:当引用视图的时候,根据视图的创建语句建立一个临时表

Undefined:未定义,自动,系统自动帮你选

Merge

意味着视图知识一个规则,语句规则,当查询视图的时候,把查询视图的语句(比如where那些)与创建时的语句where子句等合并,分析形成一条select语句

例如:

创建视图的语句:

MySQL> create view g2 as select goods_id, cat_id, goods_name, shop_price from goods order by cat_id asc, shop_price desc;

查询视图的语句

Select * from  g2 group by cat_id;

而整句话的效果看起来就像是上面两条语句做了拼接,完整的执行了下面这行代码

select  goods_id, cat_id, goods_name, shop_price from goods group by cat_id order by cat_id asc, shop_price desc;

通过merge算法将sql语句进行前后叠加

temptable

是根据创建语句瞬间创建一张临时表,然后查询视图的时候从该临时表查询数据

创建视图的语句:

比如:create algorithm=temptable view g2  as select  goods_id, cat_id, goods_name, shop_price from goods order by cat_id asc, shop_price desc;

查询视图的语句:

Select * from g2 group by cat_id

最终执行的是2句话,取数据并放在临时表,然后去查询临时表

结论:

以上两次查询视图的对象不一样,一个是查询goods表,第二次查询查询的是临时表,第一个也就是默认的给你多展示一条创建语句罢了,并没有起到视图的效果

八、MySQL的字符集和校对集设置

字符集的设置非常灵活

❶    可以设置服务器默认字符集

❷    数据库默认字符集

❸    表默认字符集

❹    列字符集

如果某一个级别没有指定字符集,则继承上一级

以表声明utf-8为例

存储的数据在表中,最终是utf-8字符集来存储的

set character_set_client=gbk;这个是设置客户端的编码

Set character_set_connection=gbk;这个是字符转换器的设置,告诉字符转换器将客户端传来的值转换成gbk

Set character_set_result=gbk;这个是告诉字符转换器,最终返回的结果是gbk字符集

  1. 告诉服务器,我给你发送的数据是什么编码的?Character_set_client;
  2. 告诉转换器,转换成什么编码?Character_set_connection;
  3. 查询的结果用什么编码?Character_set_results;
  4. 如果以上三种都为字符集N,则可以简写为 set names N,
  5. 什么时候出现乱码:1.client声明与事实不符 2:results与客户面不符
  6. 什么时候丢失数据呢?Connection和服务器的字符集所占的字符数比client小的时候,就会造成数据丢失
  7. 如何解决UTF-8客户端,服务器是GBK的乱码问题,首先告诉转换器我的字符集是utf-8,connection转换器是什么字符集倒无所谓,最后results是UTF-8就可以解决了,这个时候不用考虑字节的变化,他们在转换的时候自然会有码表进行对应

排序规则就是校对集

比如根据assic码表,a97B68,B应该在a前面,但是排序有校对集这个规则,来源于现实的理解,所以应用这种校对集

一个字符集有多种校对集

查看校对集:show collation;        195种

查看字符集:show character;     35个

改变校对集 charset utf8 collate utf8_bin;

默认使用的是utf8_general_ci规则,也可以用二进制来排序utf8_bin

声明校对集:在建表的时候末尾声明校对集,还有就是字符集和校对集也有对应关系,utf8就不能用Latin的校对集

触发器

作用:监视某种情况并触发某种操作

观察以下场景,trigger的一个场景

一个电子商城

商品表,g

主键         商品名             库存

1                 电脑                28

2                 自行车            12

订单表,o

订单主键         商品外键         购买数量

1                         2                         3

2                         1                         5

完成下单与减少库存的逻辑

Insert into o(gid, num) values(2,3);        //插入语句

Update g set goods_num = good_num - 3 where id = 2        //更新过程

这两个逻辑可以看成一个整体或者说 insert ---->引来update

用触发器可以解决上述问题:我们可以监视某表的变化,当发生某种变化的时候,触发某个操作

能监视的操作: 增删改

触发操作:        增删改

触发器的语法

After / before 

监视地点:o表

监视操作:insert

触发时间:after

触发操作:update

语法:

create trigger triggerName  After / before  insert/update/delete  on 表名 for each row

Begin

Sql 语句

End;

Delimiter $$;

所以上面的语句就可以写成这样一个触发器

Create trigger tg1

After insert into o

For each row //固定写法,oracle和sql server 是可选的

Begin

    Update g set num = num -3 where id = 2;

End$$

删除触发器的语法:

Drop trigger triggerName;

接下来完成动态赋值

首先:如何在触发器中引用行的值

❶    对于insert而言,新增的行,用new来表示,行中的每一列的值,用new.列名来表示

❷    对于delete而言,原本有一行,后来被删除,想引用被删除的这一行,用old.列名来表示

❸    对于update而言,被修改的行,修改前的数据用old.列名表示,修改后的数据用new.列名表示

Create trigger tg2

After insert into on o

For each row

Begin

    Update g set num = num - new.much where id = new.gid;

End$$

以上是下订单的时候用的

接下来可以是删除一个订单的时候,相应库存增加

监视的表:o表

监视的事件:delete

监视的时间:after

触发的操作:update

Create trigger tg3

After delete on o

For each row

Begin

    Update g set num = num + old.much  where id = old.gid;

End$$

修改订单数量的时候,库存相应改变

Create trigger tg4

After update on o

For each row

Begin

    Update g set num = num + old.much- new.much where id = old.gid;

End$$

触发器里的After与before 的区别:

❶    After是先完成数据的增删改再触发

❷    触发中的语句晚于增删改,不能对前面的增删改产生影响

Before是先完成触发,再增删改,触发的语句先于监视的增删改,也就是说我们有机会审查判断即将要发生的操作

典型案例:

对于所下订单进行判断,如果订单的数量大于5,就认为是恶意订单,强制把所订的商品数量改成5

监视地点:o表

监视事件:insert

触发事件:update

触发时间:before

目的,触发事件咸鱼监听事件,并判断监听事件的数据

Create trigger tg5

Before insert into on o

For each row

Begin

    If  new.much > 5  then

    Set new.much = 5;

    End if;

    Update g set num = num - new.much where id = new.gid;

End$

查看触发器

Show triggers

常用的表引擎

Myisam:批量插入速度快,不支持事务,锁表

Innodb:批量插入相对较慢,支持事务,行锁

全文索引:以5.5版本为例,myisam和innodb都已经支持

事务

事务的四个特性

❶    通俗的说事务,一组操作要么都成功执行,要么都不执行成功--->原子性

❷    在所有的操作没有执行完毕之前,其他会话不能够看到中间改变的过程-->隔离性

❸    事务发生前和发生后,数据的总额依然匹配--->一致性

❹    事务产生的影响是不能够撤销的--->持久性

如果出现了错误,只能通过补偿性事务进行弥补

如何开启一个事务:

Start transaction

Sql 语句

提交事务commit 或者 rollback回滚事务

有一些语句会造成事务的隐式的提交,但是为了安全性,建议手动提交

事务的基本原理:

不用事务的时候,直接作用在表,所以双方能够马上看到表的记录改变;用了事务之后,先是在事务的日志文件上写上语句的影响,commit的时候,直接通过日志文件作用于表文件。同理,在没有commit之前,因为语句的影响在事务日志文件之中,所以表的改变没有及时改变,只有当commit的时候才可以

备份与恢复:

导出数据库下面的表

MySQLdump  -u用户名 -p密码 库名 表1 表2 表3... >地址/备份文件名称

导出的是建表语句以及insert语句

Oracle则是二进制文件

导出数据库下的所有表

Mysqldump -u用户名  -p密码 库名 > 地址/备份文件名

导出一个库

Mysqldump -u用户名  -p密码 -B 库名1  库名2  >  地址/备份文件名

导出所有库

Mysqldump -u用户名  -p密码 -A   >  地址/备份文件名

恢复库

❶    登录到MySQL命令行

库为单位导出的sql文件

MySQL>source 备份文件地址

对于表级的备份文件

MySQL>use 库名

Mysql>source 备份文件地址

❷    不登录到MySQL命令行

针对库级的备份文件

MySQl  -u用户名  -p密码  <  库级备份文件地址

针对表级的备份文件

MySQL  -u用户名  -p密码  库名  <  表级备份文件地址

索引:

Hash索引:

根据hash算法算出散列值,根据散列值分配空间。

理论上还是会有重复,也就是碰撞性,碰撞性高,那么算法就不好;第二点就是最大区间,如果散列的空间很大,那么服务器的区间不能分配这么多空间,则会报错,而二叉树则可以把1万以内的数量在14次内遍历出结果

索引是针对数据建立的目录

作用:加快查询速度

负面影响:降低了增删改的速度

案例:

设有新闻列表15列, 10列上有索引,公500万行数据,如何快速导入?

  1. 把空表的索引全部删除
  2. 导入数据
  3. 数据导入完毕后,集中建立索引

索引的创建原则:

  1. 不要过渡索引
  2. 在where条件最频繁的列加上索引
  3. 尽量索引散列值,过于集中的值加索引的意义不大

查看一张表上所有索引

Show index from 表名 \G

建立索引:

Alter table 表名 add index /unique/fulltext[索引名](列名)

Alter table 表名 add primary key (列名)//不用添加索引名,因为主键只有一个

删除索引名:

普通非主键索引:Alter table 表名 drop index 索引名

全文索引 中文对MySQL没有作用

删除主键:alter table 表名 drop primary key

存储过程:

概念:概念类似于函数,就是把一段代码封装起来,当要执行这一段代码的时候,可以通过调用这个存储过程来实现,在封装的语句体里面,可以用if else case while 等控制,可以进行sql编程

存储过程没有返回值,函数有

你可能感兴趣的:(mysql,数据库,mysql,数学建模)