使用
ddd
关于mysql程式的linux命令
启动mysqld
/etc/init.d/mysqld start
service mysqld start
检查端口是否运行
lsof -i :3306 netstat -lntup |grep 3306
查看数据库程式相关信息
查看mysql版本
查看配置文件位置
mysqld --help --verbose | less
登陆mysql
#单实例登陆 mysql -uroot -p123456 #多实例登陆 mysql -uroot -p123456 -S /data/3306/mysql.sock
修改密码
#方法一 mysqladmin -u root -p123456 password 'abc123' #比较常用 #方法二(sql语句修改) update mysql.user set password=password(123456) where user='root' and host='localhost'; flush privileges; #方法三(sql语句修改) set password=password('abc123');
实用场景
导入导出数据
任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景
原理
存储引擎
什么是存储引擎?
存储引擎,存储数据,建立索引,更新,查询的那一整套实现的方法原理。
innodb和myisam两个引擎,两者区别是
innodb支持事务,外键。myisam不支持,但是myisam支持全文索引
innodb使用场景是并发控制,由于拥有提交、回滚、崩溃恢复能力的事务的安全能力
myisam使用场景主要是用于插入和查询,由于提供较高的插入和查询记录的效率
B树是对二叉查找树的改进。它的设计思想是,将相关数据尽量集中在一起,以便一次读取多个数据,减少硬盘操作次数。
B树为系统最优化大块数据的读和写操作。B树算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。
假定一个节点可以容纳100个值,那么3层的B树可以容纳100万个数据,如果换成二叉查找树,则需要20层!假定操作系统一次读取一个节点,并且根节点保留在内存中,那么B树在100万个数据中查找目标值,只需要读取两次硬盘。
二叉查找树进化品种的红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。
为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存
什么是索引?
索引是对数据库表 中一个或多个列的值进行排序的结构。功能就是加速查找
索引将占用磁盘空间,并且影响数据更新的速度。但是在多数情况下 ,索引所带来的数据检索速度优势大大超过它的不足之处。
普通索引INDEX:加速查找 唯一索引: -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复) -唯一索引UNIQUE:加速查找+约束(不能重复) 联合索引: -PRIMARY KEY(id,name):联合主键索引 -UNIQUE(id,name):联合唯一索引 -INDEX(id,name):联合普通索引
索引有哪几种种类?
聚集索引和非聚集索引(辅助索引)
聚集索引(clustered index):按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据
非聚集索引:表中除了聚集索引外其他索引都是辅助索引,与聚集索引的区别是:辅助索引的叶子节点不包含行记录的全部数据。
并且每个叶子节点中的索引行中还包含一个'书签'。该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据
由于InnoDB存储引擎是索引组织表,该书签就是相应行数据的聚集索引键。
两者共同协作
辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引,但只能有一个聚集索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键,然后再通过主键索引来找到一个完整的行记录。
例如:如果在一棵高度为3的辅助索引树种查找数据,那需要对这个辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问才能得到最终的一个数据页。
怎样创建索引?
#方法一:创建表时 CREATE TABLE 表名 ( 字段名1 数据类型 [完整性约束条件…], 字段名2 数据类型 [完整性约束条件…], [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY [索引名] (字段名[(长度)] [ASC |DESC]) ); #方法二:CREATE在已存在的表上创建索引 CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 ON 表名 (字段名[(长度)] [ASC |DESC]) ; #方法三:ALTER TABLE在已存在的表上创建索引 ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 (字段名[(长度)] [ASC |DESC]) ; #删除索引:DROP INDEX 索引名 ON 表名字; 复制代码 #例子 create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index ix_name(name) #index没有key ); create index ix_age on t1(age); create unique inex ix_age on t1(age); alter table t1 add index ix_sex(sex); #查看索引 show index from 表名 ;
#因为显示的行很长,所以常使用竖直显示 show index from s1G;
- "一个顶三个"。建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销!
- 覆盖索引。同样的有复合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=1 and b = 1。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一
- 索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select * from table where a = 1 and b =2 and c = 3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W*10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是复合索引,通过索引筛选出1000w *10% *10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知
什么是覆盖索引?
覆盖索引(covering index)指一个查询语句的执行只需要从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。也可以称之为实现了索引覆盖。
那么,优点显而易见。辅助索引不包含一整行的记录,因此可以大大减少IO操作。覆盖索引是mysql dba常用的一种SQL优化手段
视图
视图是一个虚拟表(非真实存在),是根据SQL语句获取动态的数据集,并为其命名
使用场景:
使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,
并且修改视图会反映到真实表中,特别是插入。
#语法:CREATE VIEW 视图名称 AS SQL语句 create view teacher_view as select tid from teacher where tname='李平老师'; #创建视图 #语法:ALTER VIEW 视图名称 AS SQL语句 mysql> alter view teacher_view as select * from course where cid>3; #修改视图 #语法:DROP VIEW 视图名称 DROP VIEW teacher_view
但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用。
而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的sql过分依赖于数据库中存放的视图,那么意味着,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,你要想完成修改,必须付出大量的沟通成本DBA可能才会帮你完成修改,极其地不方便
事务
事务就是一组sql语句,若有其中一条不成功,那么整组都不生效,从而保证数据库数据完整性。
start transaction; #事务启动 savepoint a # 设置保存点 rollback to a # 取消保存点a之后事务 rollback # 取消全部事务 commit #提交事务
数据库事务的四大特性是什么?
原子性:要么都成功,要么都失败。(原子性是数据安全的代言词,出现这个基本可以认为多个用户改同一份数据改错的情况)
一致性:转账操作,先把我的钱扣走,然后打到你的卡里,两个操作必须同时成功,不存在我扣走了钱,而你没收到(违反了一致性),所以把两个操作做成一个事务
持久性 :一旦事务提交成功,数据修改是永存的。
隔离性:要用于实现并发控制, 隔离能够确保并发执行的事务能够顺序一个接一个执行,通过隔离,一个未完成事务不会影响另外一个未完成事务,隔离是通过用悲观或乐观锁机制实现的。
悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的
乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。
存储过程
存储过程说白了就是一堆 SQL 的合并。中间加了点逻辑控制。
存储过程不允许执行return语句,但是可以通过out参数返回多个值。
使用存储过程的优势?
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
2.当对数据库进行复杂操作时(如对多个表进行 Update,Insert,Query,Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
这些操作,如果用程序来完成,就变成了一条条的 SQL 语句,可能要多次连接数据库。而换成存储,只需要连接一次数据库就可以了。
3.存储过程可以重复使用,可减少数据库开发人员的工作量。
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权。
什么是触发器
触发器是一种特殊的存储过程,主要是通过事件来触发而被执行的。使用触发器可以定制用户对表进行【增、删、改】操作时前后的行为,注意:没有查询
他可以强化约束,来维护数据库的完整性和一致性,可以跟踪数据内的操作从而不允许未经许可的 更新和变化
只有表支持触发器,视图不支持触发器
函数
分为内置函数和自定义函数
基本使用
库操作
创建数据库
CREATE DATABASE 数据库名 charset utf8;
查看数据库
show databases; #查看所有库 show create database db1; #查看库的信息 select database(); #查看选中的库
选中数据库
USE 数据库名
删除数据库
DROP DATABASE 数据库名;
修改数据库
alter database db1 charset utf8;
表操作
创建表
#例子 CREATE TABLE lover ( id INT(11) AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20) NOT NULL, field_name int comment '字段的注释', FOREIGN KEY(id) REFERENCES teacher (tid)
on delete cascade on update cascade
)comment='表的注释'; #表的字段 数字 可以带unsigned和zerofill修饰 #整形常用: int # 4字节 tinyint # 1字节 tinyint和smallint的区别? tinyint(1) 和 tinyint(3) 没什么区别,占用字节都是一位,存储范围都是一样的,区别的是现实长度。 即tinyint(3) zerofill ,当插入的数据少于3位的时候,左边自动补零,这才是限制显示长度 int(1)和tinyint(1)的区别? int(1) 和 tinyint(1) ,够用的情况下,优先选择tinyint(1),因为占字节少、节省空间。tinyint一个字节 smallint 两个字节 MEDIUMINT三个字节 int 4个字节 BIGINT 8个字节。 基本数字类型都可以这样推导,但是字符类型就不同,varchar(5) 这里的5 限制的是储存字符的个数,字符不分贵贱(不分 中文、英文、数字...)。 #小数常用: decimal(4,1) # 总长4,小数部分长1,内部原理采用字符串存储,所以精确。而double和float若小数位一长就会不精确。 字符串 wchar与char有什么区别? CHAR的长度是固定的,而VARCHAR2的长度是可以变化的, 比如,存储字符串“abc",对于CHAR (10),表示你存储的字符将占10个字节(包括7个空字符),而同样的VARCHAR2 (10)则只占用3个字节的长度,10只是最大值,当你存储的字符小于10时,按实际长度存储。但是CHAR的存取速度比VARCHAR快。 时间 最常用:datetime 单独插入时间时,需要以字符串的形式,按照对应的格式插入: YEAR YYYY(1901/2155) DATE YYYY-MM-DD(1000-01-01/9999-12-31) TIME HH:MM:SS('-838:59:59'/'838:59:59') DATETIME YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59) 例如: insert into student values -> (1,'alex',"1995","1995-11-11","11:11:11","2017-11-11 11:11:11"), -> (2,'egon',"1997","1997-12-12","12:12:12","2017-12-12 12:12:12"), -> (3,'wsb',"1998","1998-01-01","13:13:13","2017-01-01 13:13:13"); 集合与枚举 enum 只能选择集合的单个 set 可以选择集合的多个,用括号扩起来 。 枚举例子: CREATE TABLE shirts ( name VARCHAR(40), size ENUM('x-small', 'small', 'medium', 'large', 'x-large') ); INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),('polo shirt','small'); 集合例子: CREATE TABLE myset (col SET('a', 'b', 'c', ' INSERT INTO myset (col) VALUES ('a,d'), ('d,a'), ('a,d,a'), ('a,d,d'), ('d,a,d'); 位类型 BIT(M)可以用来存放多位二进制数,M范围从1~64,如果不写默认为1位。 注意:对于位字段需要使用函数读取,bin()显示为二进制,hex()显示为十六进制 create table t9(id bit); insert into t9 values(8); #自动转化为位 select * from t9; #直接查看是无法显示二进制位的,需要bin()或者hex()显示 select bin(id),hex(id) from t9; #需要转换才能看到
复制表
create table new_service select * from service; #不会复制: 主键、外键和索引 create table t4 like employees; # 表结构和数据都复制
删除表
DROP TABLE 表名;
# 有时候存在一些约束使得表不能被删除,此时通过关闭外键检测,强制删除表
SET FOREIGN_KEY_CHECKS = 0;
删除完成后设置
SET FOREIGN_KEY_CHECKS = 1;
查看表信息
show crete table 表名; #查看表创建语句
一: 修改表信息 #修改表名 alter table test_a rename to sys_app; #修改表注释 alter table sys_application comment '系统信息表'; 二:修改字段层面 增加字段 alter table tmp add love int(10); alter table `t_app` add column gateway_id int not null default 0 AFTER `aid`; #(在哪个字段后面添加) 修改字段 alter table t_app modify column aid int(5) auto_increment ; #modify修改已有的字段 ,并且只能不能修改名称
alter table table1 change column1 column1 varchar(100) DEFAULT 1.2 COMMENT '注释'; #change 可以修改字段名,以及所有属性 alter table t_app change gateway_id gateway_id int not null after aid ; #调整字段顺序
alter table test1 modify column field_name int comment '修改后的字段注释'; #修改字段的注释
删除字段 alter table t_app drop aid; 主键不同特别,注意的是删除主键之前一定要移除表中任何字段存在的auto_increment alter table t_app add aid int(5) not null auto_increment ,add primary key (aid); #增加主键 alter table products drop primary key; #删除主键 外键也是不同,注意的是外键字段类型一定相同 alter table tmp add foreign key(aid) references teacher(tid); #增加外键 alter table tmp drop FOREIGN KEY tmp_ibfk_1; #删除外键,tmp_ibfk_1是外键名字,通过show create table 表名; 可以查看。
插入数据
插入数据
INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n);
插入多条记录
INSERT INTO 表名 VALUES
(值1,值2,值3…值n),
(值1,值2,值3…值n),
(值1,值2,值3…值n);
插入查询结果
INSERT INTO 表名(字段1,字段2,字段3…字段n)
SELECT (字段1,字段2,字段3…字段n) FROM 表2
WHERE …;
将一个表的数据插入另外一个表
insert into KindInfo(title,blog_id) (select title,blog_id from ErrorReport.KindInfo);
更新数据
UPDATE 表名 SET 字段1=值1, 字段2=值2, WHERE CONDITION; 示例: UPDATE mysql.user SET password=password(‘123’) where user=’root’ and host=’localhost’
删除数据
# 普通版 DELETE FROM mysql.user WHERE password=’’;
# 注意在django中,如果修改了表,之后migrate提示无改变,就需要删除对应的‘记录’
delete from django_migrations where app ='responsitory'
# 清空表的数据 truncate table 表名; #再插入时自增长id又从1开始
查询数据
1.条件查询关键字:between and ,is null,like,regexp, 2.若使用group by 一般只能查询分组关键字,和统计函数相关。也可以查询与分组条件具有一对一关系的字段。 3.若结果集当做临时被查询,一定要起as别名。例如select * from (select sid ,sname from student) as stu 4.可以查询一个嵌套结果集。 例如select sid,sname,(select number from score where sid = student_id order by number desc limit 0,1) 注意的是:里面的条件,可以引用外面的条件, 并且返回的结果只有‘一个’,不是一条,可以看作是一个函数 返回一个单一结果 5.limit 0,1 :两个参数的意义,第一个表示开始位置,第二个表示条数 6.left join tablename on xxx=xxxx,多表查询冲未如此简单,把多个表根据联系字段组成一个表,可以看作是 一张‘简单表’的‘详细说明’
7.执行优先级从高到低:where > group by > having
Where 发生在分组group by之前,因而Where中可以有任意字段,但是绝对不能使用聚合函数。
Having发生在分组group by之后,因而Having中可以使用分组的字段,无法直接取到其他字段,可以使用聚合函数
8.使用正则:select * from employee where name regexp '^jin.*[gn]$';
9.like的通配符
10.多表查询的EXISTS 和 NOT EXISTS等关键字
EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录。而是返回一个真假值。True或Fals
例如:mysql> select * from employee -> where exists -> (select id from department where id=200);
11.多表查询语句执行顺序:FROM->ON过滤->添加连接表的外部行->执行WHERE过滤->执行GROUP BY分组->执行HAVING过滤->执行HAVING过滤->执行ORDER BY子句->执行LIMIT子句
设计
如何找出两张表之间的关系
分析步骤: #1、先站在左表的角度去找 是否左表的多条记录可以对应右表的一条记录,如果是,则证明左表的一个字段foreign key 右表一个字段(通常是id) #2、再站在右表的角度去找 是否右表的多条记录可以对应左表的一条记录,如果是,则证明右表的一个字段foreign key 左表一个字段(通常是id) #3、总结: #多对一: 如果只有步骤1成立,则是左表多对一右表 如果只有步骤2成立,则是右表多对一左表 #多对多 如果步骤1和2同时成立,则证明这两张表时一个双向的多对一,即多对多,需要定义一个这两张表的关系表来专门存放二者的关系 #一对一: 如果1和2都不成立,而是左表的一条记录唯一对应右表的一条记录,反之亦然。这种情况很简单,就是在左表foreign key右表的基础上,将左表的外键字段设置成unique即可
如何设计索引?
设计索引阶段
1.如果知道数据的使用,从一开始就应该在需要处添加索引。 2.为经常出现在关键字order by、group by、distinct,where后面的字段,建立索引,比如select * from s1 where id = 333;就需要为id加上索引 3.在union等集合操作的结果集字段上,建立索引 4.为经常用作查询选择的字段,建立索引 5.在经常用作表连接的属性上,建立索引 6.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1。 例:性别不适合 7.若索引太多,应用程序的性能可能会受到影响。而索引太少,对查询性能又会产生影响,要找到一个平衡点 8.若需要执行很多的类似于 select * from test where a=10, b>50, c>20 这类的组合查询 那么,我们可能需要创建 包含[a,b,c] 的联合索引,而单独的[a][b] [c]上的索引是不够的。 (可以把一个索引想象成 sorted list).创建了 (a,b,c)的索引相当于 按照a,b,c 排序 9.避免重复索引 重复索引,是指在同一个列或者顺序相同的几个列建立了多个索引,称之为重复索引。 比如在文章表中,给主键(primary key)aid另增加了唯一索引(unique index)。或者文章标题title和作者author已经形成了一个联合索引,再去给title而外新增一个索引。 重复的索引对数据检索没提供帮助,只会增大索引文件,对于insert、update、delete操作时,还要额外的维护索引文件,拖慢更新速度。这样的重复索引应该优化掉。 10.避免冗余索引 冗余索引是指在一个表中,多个索引覆盖的列有重叠,称之为冗余索引。 比如在文章表中,文章名称title和作者author已经形成了联合索引,同时新增了作者author和文章名称title形成的联合索引。 这样在单独查询文章名称、单独查询作者、根据文章名称找作者、根据作者找文章时,分别会用到这两个联合索引,提升查询效率。
场景模拟
举个例子来说,比如你在为某商场做一个会员卡的系统。 这个系统有一个会员表 有下列字段: 会员编号 INT 会员姓名 VARCHAR(10) 会员身份证号码 VARCHAR(18) 会员电话 VARCHAR(10) 会员住址 VARCHAR(50) 会员备注信息 TEXT 那么这个 会员编号,作为主键,使用 PRIMARY 会员姓名 如果要建索引的话,那么就是普通的 INDEX 会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复) #除此之外还有全文索引,即FULLTEXT 会员备注信息 , 如果需要建索引的话,可以选择全文搜索。 用于搜索很长一篇文章的时候,效果最好。 用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。 但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
编写语句阶段(正确使用查询语句才能使得索引奏效)
1.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式 2.索引列不能参与计算。例如select count(*) from tmp where id*3>11111; 这个例子,由于索引列参与了计算,无法拿到一个明确的值去索引树中查找,每次都得临时计算一下。 3.条件中的索引字段使用大范围,无法加快速度。例如select count(*) from tmp where id>333;
4.条件中的判断类型不一致,例如如果列是字符串类型,传入条件是必须用引号引起来。
select
*
from
tb1
where
name
= 999;
还有like其实也适用于整形,但是若该整形字段增加了索引,也无法享受索引的好处。
5.or
条件中若有一条导致慢,则整体都慢,常见于索引字段和非索引字段混合使用
6.like '%xxxx' 无法享用索引, like ‘xxxx%‘则可以
7.当根据索引排序时候,选择的映射如果不是索引,则不走索引。但是
如果对主键排序,则还是走索引:避免使用
select
*
8.count
(1)或
count
(列) 代替
count
(*)
9.合索引代替多个单列索引(经常使用多个条件查询时)
10.使用连接(
JOIN
)来代替子查询(Sub-Queries),
连表时注意条件类型需一致
1.创建表时尽量时
char
代替
varchar
2.表的字段顺序固定长度的字段优先
sql语句应该考虑哪些安全性?
1.防止sql注入,对特殊字符进行转义,过滤或者使用预编译的sql语句绑定变量
2.最小权限原则,特别是不要用root账户,为不同的类型的动作或者组建使用不同的账户
3.当sql运行出错时,不要把数据库返回的错误信息全部显示给用户,以防止泄漏服务器和数据
sql优化技1.sql优化:创建表时,定长的类型往前放,变长的往后放。比如性别(定长)往前放,比如地址或描述信息(变长)往后放。
2.>255个字符,超了就把文件路径存放到数据库中。比如图片,视频等找一个文件服务器,数据库中只存路径或url
3.选择数据类型只要遵循小而简单的原则就好,越小的数据类型通常会更快,占用更少的磁盘、内存,处理时需要的CPU周期也更少。
越简单的数据类型在计算时需要更少的CPU周期,比如,整型就比字符操作代价低,因而会使用整型来存储ip地址,使用DATETIME来存储时间,而不是使用字符串。
4.通常来说把可为NULL的列改为NOT NULL不会对性能提升有多少帮助,只是如果计划在列上创建索引,就应该将该列设置为NOT NULL。
5.对整数类型指定宽度,比如INT(11),没有任何卵用。INT使用16为存储空间,那么它的表示范围已经确定,所以INT(1)和INT(20)对于存储和计算是相同的。
6.UNSIGNED表示不允许负值,大致可以使正数的上限提高一倍。比如TINYINT存储范围是通常来讲
7.没有太大的必要使用DECIMAL数据类型。即使是在需要存储财务数据时,仍然可以使用BIGINT。比如需要精确到万分之一,那么可以将数据乘以一百万然后
8.使用TIMESTAMP使用4个字节存储空间,DATETIME使用8个字节存储空间。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范围小得多,而且TIMESTAMP的值因时区不同而不同。
9.大多数情况下没有使用枚举类型的必要,枚举的七宗罪
enum这个东西不改的话还好,如果要修改就是个坑,建议用TINYINT代替,在程序里维护一组常量来对应。
10.列不要太多。原因是存储引擎的API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列,
这个转换过程的代价是非常高的。如果列太多而实际使用的列又很少的话,有可能会导致CPU占用过高。
11.大表ALTER TABLE非常耗时,
MySQL执行大部分修改表结果操作的方法是用新的结构创建一个张空表,从旧表中查出所有的数据插入新表,然后再删除旧表。
尤其当内存不足而表又很大,而且还有很大索引的情况下,耗时更久。当然有一些奇淫技巧可以解决这个问题,有兴趣可自行查阅。
12.正确设计索引,过多的索引可能会导致过高的磁盘使用率以及过高的内存占用,从而影响应用程序的整体性能。
应当尽量避免事后才想起添加索引,因为事后可能需要监控大量的SQL才能定位到问题所在,而且添加索引的时间肯定是远大于初始添加索引所需要的时间,可见索引的添加也是非常有技术含量的
13.使用truncate代替delete当删除表中记录时,要是回滚段来记录已经被删除的数据的信息,记录大量日志,用来做恢复使用这样在操作的时候,
对数据库来说是个不小的消耗,而truncate是ddl,直接收回表所使用的空间,不记录日志,速度快,而且可以回收空间
select句中避免使用 '*'。因为select *最大的问题是可能会多出一些不用的列,这些列可能正好不在索引的范围之内,或是针对*建索引成本过于高昂,并且select *杜绝了索引覆盖的可能性,而索引覆盖又是速度极快,效率极高,业界极为推荐的查询方式。
减少访问数据库的次数,将多条语句合并成一条'大语句'
删除重复记录
用where子句替代having子句,HAVING子句会在分组之后对分组形成的结果进行过滤。这个过程需要聚合、排序,因此如果通过WHERE子句限制记录而省略HAVING子句,是可以提升性能的。
减少对表的查询,在含有子查询的SQL语句中,尤其要减少对表的查询。
避免使用IN和NOT IN,即使建了索引,但是它两是不能命中索引的,使用EXISTS替代即可享用索引。
explain
SQL优化工具
1.执行计划
基础使用
explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
id 查询顺序标识 如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B; +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 9 | NULL | | 2 | DERIVED | tb1 | range | PRIMARY | PRIMARY | 8 | NULL | 9 | Using where | +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ 特别的:如果使用union连接气值可能为null select_type 查询类型 SIMPLE 简单查询 PRIMARY 最外层查询 SUBQUERY 映射为子查询 DERIVED 子查询 UNION 联合 UNION RESULT 使用联合的结果 ... table 正在访问的表名 type 查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const ALL 全表扫描,对于数据表从头到尾找一遍 select * from tb1; 特别的:如果有limit限制,则找到之后就不在继续向下扫描 select * from tb1 where email = 'seven@live.com' select * from tb1 where email = 'seven@live.com' limit 1; 虽然上述两个语句都会进行全表扫描,第二句使用了limit,则找到一个后就不再继续扫描。 INDEX 全索引扫描,对索引从头到尾找一遍 select nid from tb1; RANGE 对索引列进行范围查找 select * from tb1 where name < 'alex'; PS: between and in > >= < <= 操作 注意:!= 和 > 符号 INDEX_MERGE 合并索引,使用多个单列索引搜索 select * from tb1 where name = 'alex' or nid in (11,22,33); REF 根据索引查找一个或多个值 select * from tb1 where name = 'seven'; EQ_REF 连接时使用primary key 或 unique类型 select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid; CONST 常量 表最多有一个匹配行,因为仅有一行,在这行的列值可被优化器剩余部分认为是常数,const表很快,因为它们只读取一次。 select nid from tb1 where nid = 2 ; SYSTEM 系统 表仅有一行(=系统表)。这是const联接类型的一个特例。 select * from (select nid from tb1 where nid = 1) as A; possible_keys 可能使用的索引 key 真实使用的 key_len MySQL中使用索引字节长度 rows mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值 extra 该列包含MySQL解决查询的详细信息 “Using index” 此值表示mysql将使用覆盖索引,以避免访问表。不要把覆盖索引和index访问类型弄混了。 “Using where” 这意味着mysql服务器将在存储引擎检索行后再进行过滤,许多where条件里涉及索引中的列,当(并且如果)它读取索引时,就能被存储引擎检验,因此不是所有带where子句的查询都会显示“Using where”。有时“Using where”的出现就是一个暗示:查询可受益于不同的索引。 “Using temporary” 这意味着mysql在对查询结果排序时会使用一个临时表。 “Using filesort” 这意味着mysql会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。mysql有两种文件排序算法,这两种排序方式都可以在内存或者磁盘上完成,explain不会告诉你mysql将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。 “Range checked for each record(index map: N)” 这个意味着没有好用的索引,新的索引将在联接的每一行上重新估算,N是显示在possible_keys列中索引的位图,并且是冗余的。
2.慢日志查询
慢查询对于跟踪有问题的查询很有用,可以分析出当前程序里哪些sql语句比较耗费资源
慢查询定义:
指mysql记录所有执行超过long_query_time参数设定的时间值的sql语句,慢查询日志就是记录这些sql的日志。
mysql在windows系统中的配置文件一般是my.ini找到mysqld
log-slow-queries = F:MySQLlogmysqlslowquery.log 为慢查询日志存放的位置,一般要有可写权限
long_query_time = 2 2表示查询超过两秒才记录