本篇涉及到的问题:MySQL引擎; 什么是基本表、视图、游标; char和varchar的区别及使用场景; MySQL中,索引、主键、唯一索引、联合索引的区别,对数据库的读写性能的影响; 为数据库表建立索引的原则、目的及对DBS的负面影响; MySQL语句优化;MySQL执行计划分析;MySQL复制原理。
目录
MySQL引擎
什么是基本表、视图、游标
char和varchar的区别及使用场景
MySQL中,索引、主键、唯一索引、联合索引的区别,对数据库的读写性能的影响
为数据库表建立索引的原则、目的及对DBS的负面影响
提高sql语句效率的技巧:
1.大批量插入数据:
2.优化INSERT语句
3.查询优化
4.注意不使用索引的情况:
select Count (*)和Select Count(1)以及Select Count(column)区别:
索引列上计算引起的索引失效及优化措施以及注意事项:
怎么样执行计划分析?
MySQL复制原理:
MySQL的核心就是存储引擎。 MySQL的存储引擎是针对表进行设置的,一个数据库中不同的表可以设置成不同的存储引擎,以适用不同领域的数据库应用需要。主要的数据库引擎如下:
对于整个服务器或方案,并不一定要使用相同的存储引擎,可以为方案中的每个表使用不同的存储引擎。
下面详细介绍InnoDB引擎和MyISAM引擎:
Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于MySQL后台的完整数据库系统,MySQL运行时InnoDB会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。
MyIASM引擎是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和InnoDB不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。
两者主要区别:
1、MyIASM是非事务安全的,而InnoDB是事务安全的
2、MyIASM锁的粒度是表级的,而InnoDB支持行级锁
3、MyIASM支持全文类型索引,而InnoDB不支持全文索引
4、MyIASM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyIASM
5、MyIASM表保存成文件形式,跨平台使用更加方便
应用场景:
1、MyIASM管理非事务表,提供高速存储和检索以及全文搜索能力,如果再应用中执行大量select操作,应该选择MyIASM。
2、InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insert和update操作,应该选择InnoDB。
两种引擎所使用的索引的数据结构(都是B+树):
MyIASM引擎,B+树的数据结构中存储的内容实际上是实际数据的地址值。也就是说它的索引和实际数据是分开的,只不过使用索引指向了实际数据。这种索引的模式被称为非聚集索引。
Innodb引擎的索引的数据结构也是B+树,只不过数据结构中存储的都是实际的数据,这种索引有被称为聚集索引。
基本表是本身独立存在的表,在 SQL 中一个关系就对应一个表。 是一个虚表
视图是从一个或几个基本表导出的表。视图本身不独立存储在数据库中,所以视图是一种虚表。而具有和物理表相同的功能,可以对视图进行增,改,查,操作。另外,对视图的修改不影响基本表,相比多表查询,它使得我们获取数据更容易。
游标是对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。
char是一种固定长度的类型,varchar则是一种可变长度的类型,它们的区别是:
char(M)类型的数据列里,每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足(在检索操作中那些填补出来的空格字符将被去掉)。在varchar(M)类型的数据列里,每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为L+1字节)。
varchar得适用场景:
☆字符串列得最大长度比平均长度大很多 ☆字符串很少被更新,容易产生存储碎片 ☆使用多字节字符集存储字符串。
char的使用场景:
☆存储具有近似得长度(md5值,身份证,手机号) ☆长度比较短小得字符串(因为varchar需要额外空间记录字符串长度)
☆更适合经常更新的字符串,更新时不会出现页分裂得情况,避免出现存储碎片,获得更好的io性能。
索引是一种特殊的文件(InnoDB数据库表上的索引是表空间的一个组成部分),它们包含着数据库表里所有记录的引用指针。
普通索引的唯一任务是加快对数据的访问速度,有KEY或INDEX关键字定义。普通索引允许被索引的数据列包含重复的值,如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义成一个唯一索引,唯一索引保证数据记录的唯一性。
主键是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字PRIMARY KEY创建。
索引可以覆盖多个数据列,比如INDEX(columnA,columnB)索引,这就是联合索引。
索引对数据库的读写性能的影响:索引可以极大的提高数据的查询速度,但是会降低数据的插入、删除和更新表的速度,因为在执行写操作的时候,还要操作索引文件。
★ 建立索引的原则:
☆在最频繁使用的、用以缩小查询范围的字段上建立索引 ☆在频繁使用的,用以排序的字段上建立索引
注:这两种情况最好不要建立索引:一是对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引;二是对于一些特殊的数据类型,不宜建立索引,比如文本字段(text)等。
◆ 建立索引的目的:
◇快速访问数据表中的特定信息,提高检索速度 ◇创建唯一性索引,保证数据库表中每一行数据的唯一性
◇加速表和表之间的连接 ◇使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间
●索引对数据库系统的负面影响:
○ 创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;
○ 索引需要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间 ;
○ 当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速度。
▲大批量数据插入空表,可将表设置成MyISAM,并通过disable keys将唯一索引关闭;
▲大批量插入非空InnoDB表,可采取:
①导入数据时按照逐渐顺序排列;
②导入数据前使用set UNIQUE_CHECKS=0,关闭唯一性校验,导入后恢复;
③如果使用了自动提交(一条一条提交),建议在导入前执行SET AUTOCOMMIT=0(批量提交 ),关闭自动提交,导入后恢复。
▲尽量使用多个值表的insert语句,降低连接、关闭的消耗;例如:insert into user values(1,“张三”),(2,“李四”),(...),... 。
▲将索引文件和数据文件分在不同的磁盘上存放;
▲从一个文本文件装入一个表时,使用LOAD DATE INFILE,比一般的inser语句快20倍
▲ 尽量减少额外的排序,通过索引直接返回有序数据;where条件和order by使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序;
▲尽量只选择必要的字段,提高SQL性能;
▲ 能用关联查询就不要使用子查询;
▲对于包含or的查询语句,如果要利用索引,则or之间的每个条件都必须用到索引,否则应该考虑增加索引;
▲优化分页:①在索引上完成排序分页的操作,然后根据主键关联回原表查询所需的其他列 ②把limit查询转化为某个位置的查询。
▲如果MySQL估计使用索引比全表扫描更慢,则不用索引;
▲用or分隔开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到;
▲复合索引,如果索引列不是符合索引的第一部分,则不使用索引(即不符合最左前缀);
▲如果like是以“%”开始的,则该列上的索引不会被使用;
▲如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用;
▲not in、not exist、!=或<>这些操作符不走索引;
▲不要在where字句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
▲对查询进行优化,尽量避免全表扫描,首先考虑在where及order by涉及的列上建立索引。
▲应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
▲应尽量避免在where子句中进行null值的判断,否则引擎将放弃使用索引而进行全表扫描。例:select id from t where num is null; 可以在num上设置默认值为0,确保num列中没有null值,然后可以这样查询:select id from t where num=0;
▲应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num=10 or num=20;可以这样查询:select id from t where num=10 union all select id from t where num=20
▲模糊查询以%开头的查询也将导致全表扫描,例:select id from t where name like '%abc%'。
▲in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t where num in(1,2,3);对于连续的数值,能用 between 就不要用 in 了:select id from t where num between 1 and 3;
▲如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num; 可以改为强制查询使用索引: select id from t with(index(索引名)) where num=@num;
▲应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100; 应改为: select id from t where num=100*2;
▲应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc' ; --name以abc开头的id
select id from t where datediff(day,createdate,'2005-11-30')=0; --'2005-11-30'生成的id
应改为:
select id from t where name like 'abc%';
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1';
▲不要在 where 子句中的“=”【左边】进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
▲在使用索引字段作为条件时,如果该索引是【复合索引】,那么必须使用到该索引中的【第一个字段】作为条件时才能保证系统使用该索引,否则该索引将不会被使用。并且应【尽可能】的让字段顺序与索引顺序相一致。(字段顺序也可以不与索引顺序一致,但是一定要包含【第一个字段】。)
▲不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0; 这类代码不会返回任何结果集,但是会消耗系统资源的,应改成:create table #t(...);
▲很多时候用 exists 代替 in 是一个好的选择:select num from a where num in(select num from b);
替换成select num from a where exists(select 1 from b where num=a.num);
▲并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
▲索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
▲应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
▲尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
▲尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
▲.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
▲尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
▲避免频繁创建和删除临时表,以减少系统表资源的消耗。
▲.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
▲临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
▲尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
▲使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
▲与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
▲在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
▲尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
▲尽量避免大事务操作,提高系统并发能力。
一般情况下,Select Count (*)和Select Count(1)两着返回结果是一样的。
假如表沒有主键(Primary key), 那么count(1)比count(*)快;如果有主键的話,那主键作为count的条件时候count(主键)最快; 如果表中只有一个字段的话那count(*)就是最快的。count(*) 跟 count(1) 的结果一样,都包括对NULL的统计,而count(column) 是不包括NULL的统计 。
创建索引、优化查询以便达到更好的查询优化效果。但实际上,MySQL有时并不按我们设计的那样执行查询。MySQL是根据统计信息来生成执行计划的,这就涉及索引及索引的刷选率,表数据量,还有一些额外的因素。
Each table index is queried, and the best index is used unless the optimizer believes that it is more efficient to use a table scan. At one time, a scan was used based on whether the best index spanned more than 30% of the table, but a fixed percentage no longer determines the choice between using an index or a scan. The optimizer now is more complex and bases its estimate on additional factors such as table size, number of rows, and I/O block size.
简而言之,当MySQL认为符合条件的记录在30%以上,它就不会再使用索引,因为mysql认为走索引的代价比不用索引代价大,所以优化器选择了自己认为代价最小的方式。事实也的确如此,是MYSQL认为记录是30%以上,而不是实际MYSQL去查完再决定的。都查完了,还用什么索引啊!MYSQL会先估算,然后决定是否使用索引。
通过explain命令获取MySQL如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序;explain分析后的结果解析:
▲ select_type:查询的类型,主要区分普通查询、联合查询、子查询等复杂的查询
① SIMPLE:简单的select查询,查询中不包含子查询或者union;
② PRIMARY:查询中包含任何复杂的子部分,最外层查询被记为primary;
③ SUNBQUERY:在select或者where列表中包含了子查询;
④ UNION:若第二个select出现在union之后,则被标记为union;若union包含在from字句的子查询中,外层select将被标记为derived;
▲ type:访问类型,sql优化中很重要的一个指标,接过值从好到坏一次:system>const>eq_ref>ref>range>index>ALL
一般来说,好的sql查询至少达到range级别,最好能达到ref;
① system:表中只有一行记录(等于系统表),这是const的特例,平时不会出现,可以忽略;
② const:表示通过索引一次就找到了,const用于比较primary key或者unique索引;
③ seq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配;
④ ref:非唯一性索引扫描,返回匹配某个单独值的所有行;
⑤ range:索引范围扫描;
⑥ index:索引全扫描;
⑦ ALL:全表扫描;
▲ possible_keys :查询涉及到的字段上存在索引,则该索引将被列出,但不一定被查询实际使用 ;
▲ key :实际使用的索引,如果为NULL,则没有使用索引;
▲ key_len :索引中使用的字节数,查询中使用的索引的长度(最大可能长度),并非实 际使用长度,理论上长度越短越好;
▲ ref :显示索引的哪些列;
▲ rows :根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
▲ Extra :不适合在其他字段中显示,但是十分重要的额外信息
优化目标:
① 根据需求建立索引;② 每个查询都要使用索引以提高查询效率,至少达到range级别,最好能达到ref;③ 追求key_len和rows最小。
1.主库在数据提交时会把数据变更作为事件记录在二进制日志文件Binlog中,可通过参数sync_binlog控制binlog日志刷新到磁盘的频率;
2.主库推送二进制日志文件binlog中的事件到从库的中继日志Relay Log,之后从库根据中继日志RelayLog重做数据变更操作,通过逻辑复制达到主从库的数据一致;
3.MySQL通过3个线程来完成主从库之间的数据同步,其中binlog dump线程跑在主库上,I/O线程和sql线程跑在从库上。
当从库启动复制时,首先创建I/O线程连接主库,主库随后创建binlog
当从库启动复制时,首先创建I/O线程连接主库,主库随后创建binlog dump线程读取数据库事件并发送给I/O线程,I/O线程获取到的事件数据后更新到从库的中继日志replay log中去,之后从库上的sql线程读取中继日志中更新的数据库事件并应用;