对mysql优化时一个综合性的技术,主要包括
优化技术根据不同业务场景也不同,但是底层一些使用方式和原理是通用的,本篇博文从最基本的数据库基本字段类型展开优化技术的描述,详细如下
从数学上来讨论tinyint
数学推算
[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 占用空间 利用率 如何选择:定长速度快 考虑两个要素即可 选择原则: 用户名:varchar 造成速度变慢,宁可让字节多浪费几个,而选择用char,没有定死的规则,根据实际变通。 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个字节 关系型数据库中,不要出现一个列可以有多个值的现象,这样列的值就不唯一,还不如用一张表来表示各种字段 建表案例: 现在设计一张表: 姓名: 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 表中的数据,但要求 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 如下: 总结: 5种子句的使用顺序 where , group , having, order by , limit DML:对应CRUD 合并查询的结果(取select结果的并集) ❶ 对于重复的行,去掉 ❷ 如果不去重复,可以用union all Union的要求: ❶ 各select查出的列数一致 ❷ 如果子句中使用功能了 order by limit ,那么子句要用“()”包裹起来 ❸ 如果子句只用order by 没有limit ,Order by 会被优化掉,不起作用 左连接与右连接可以相互转换,join后的为主表 内连接:左右连接的交集 两张表能相互匹配上的行 增加列: ❶ 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 使用的时候,直接将其当做表来使用就可以了 视图是表的查询结果,那么表的数据改变了,自然影响视图的结果 视图增删改也会影响表,但是视图并不是总是能增删改的,当视图的数据与表的数据一一对应的时候,就可以改变,否则不能,对于视图的insert还应该注意,视图必须包含表中没有默认值的列,也就是视图中的列有个列没有默认值,那么插入的时候就要给他赋值 Algorithm = merge/temptable/undefined Merge :(合并)当引用视图时,引用视图的语句与定义视图的语句合并 Temptable:当引用视图的时候,根据视图的创建语句建立一个临时表 Undefined:未定义,自动,系统自动帮你选 意味着视图知识一个规则,语句规则,当查询视图的时候,把查询视图的语句(比如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语句进行前后叠加 是根据创建语句瞬间创建一张临时表,然后查询视图的时候从该临时表查询数据 创建视图的语句: 比如: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表,第二次查询查询的是临时表,第一个也就是默认的给你多展示一条创建语句罢了,并没有起到视图的效果 字符集的设置非常灵活 ❶ 可以设置服务器默认字符集 ❷ 数据库默认字符集 ❸ 表默认字符集 ❹ 列字符集 如果某一个级别没有指定字符集,则继承上一级 以表声明utf-8为例 存储的数据在表中,最终是utf-8字符集来存储的 set character_set_client=gbk;这个是设置客户端的编码 Set character_set_connection=gbk;这个是字符转换器的设置,告诉字符转换器将客户端传来的值转换成gbk Set character_set_result=gbk;这个是告诉字符转换器,最终返回的结果是gbk字符集 排序规则就是校对集 比如根据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 $$; 所以上面的语句就可以写成这样一个触发器 删除触发器的语法: Drop trigger triggerName; 接下来完成动态赋值 首先:如何在触发器中引用行的值 ❶ 对于insert而言,新增的行,用new来表示,行中的每一列的值,用new.列名来表示 ❷ 对于delete而言,原本有一行,后来被删除,想引用被删除的这一行,用old.列名来表示 ❸ 对于update而言,被修改的行,修改前的数据用old.列名表示,修改后的数据用new.列名表示 以上是下订单的时候用的 接下来可以是删除一个订单的时候,相应库存增加 监视的表:o表 监视的事件:delete 监视的时间:after 触发的操作:update 修改订单数量的时候,库存相应改变 触发器里的After与before 的区别: ❶ After是先完成数据的增删改再触发 ❷ 触发中的语句晚于增删改,不能对前面的增删改产生影响 Before是先完成触发,再增删改,触发的语句先于监视的增删改,也就是说我们有机会审查判断即将要发生的操作 典型案例: 对于所下订单进行判断,如果订单的数量大于5,就认为是恶意订单,强制把所订的商品数量改成5 监视地点:o表 监视事件:insert 触发事件:update 触发时间:before 目的,触发事件咸鱼监听事件,并判断监听事件的数据 查看触发器 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万行数据,如何快速导入? 索引的创建原则: 查看一张表上所有索引 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编程 存储过程没有返回值,函数有
Char M M i M i/M<=100%
Varchar M M i i字符+(1-2)字节 i/(i+1-2)<100%
日期时间类型:
Enum和set枚举型和集合类型:
二、五种子句:
三、三种子查询
SELECT s1,s2 FROM (SELECT s1, s2*2 AS s2 FROM table1) AS temp WHERE s1 > 1四、Union的用法:
五、左连接,右连接,内连接,MySQL没有外链接
六、DDL语句,列的增删改
七、视图:
视图的定义:
创建语句:
删除视图:
为什么要视图:
视图与表的关系:
视图的算法algorithm
Merge
temptable
八、MySQL的字符集和校对集设置
触发器
Create trigger tg1
After insert into o
For each row //固定写法,oracle和sql server 是可选的
Begin
Update g set num = num -3 where id = 2;
End$$
Create trigger tg2
After insert into on o
For each row
Begin
Update g set num = num - new.much where id = new.gid;
End$$
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$$
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$
常用的表引擎
事务
备份与恢复:
索引: