栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

MYSQL数据库性能优化总结

MYSQL数据库性能优化总结

1.为查询缓存你的查询

        大多数的MySQL服务器都开启了查询缓存。这是提高性能最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了很多次的时候,这些查询结果会被放到一个缓存中,这样后续相同的查询就不用操作表而直接访问缓存结果了。

        这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的,因为我们某些查询语句会让MySQL不使用缓存。请看下面的示例:

         上面两条SQL语句的差别就是CURDATE(),MySQL的查询缓存对这个函数不起作用。所以像NOW()和RAND()或是其他的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是不定的易变的。所以你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。

2.EXPLAIN你的SELECT查询

        使用EXPLAIN关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。

        EXPLAIN的查询结果还会告诉你,你的索引主键被如何利用的,你的数据表是如何被搜索和排序的 ……等等。

        挑一个你的SELECT语句(推荐挑选那个最复杂的,有多表关联的),把关键字EXPLAIN加到前面。下面的这个示例,我们忘记加上group_id索引,并且有表联结:

         当我们为 group_id 字段加上索引后:

        我们可以看到,前一行结果显示搜索了7883行,而后一个只是搜索了两个表的9和16行。查看rows列可以让我们找到潜在的性能问题。 

3.当只有一行数据时使用LIMIT 1

        当你查询表的时候,有时你已经知道结果只会有一条结果,在这种情况下,加上LIMIT 1可以增加性能。这样的话MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往下查找下一条符合记录的数据。

 4.为搜索字段建索引

        索引并不一定只是给主键或是唯一的字段建立。如果在你的表中,有某个字段你总要会经常用来做搜索,那么,请为其建立索引吧。

         从上图你可以看到那个搜索字串“last_nameLIKE‘a%’”,一个是建了索引,一个是没有索引,性能差了4倍左右。

        另外,你应该也需要知道什么样的搜索是不能使用正常的索引的。例如,当你需要在一篇大的文章中搜索一个词时,如:“WHERepost_contentLIKE‘%apple%’”,索引可能是没有意义的。你可能需要使用MySQL全文索引或是自己做一个索引(比如说:搜索关键词或是Tag什么的)。

5.在Join表的时候使用相当类型的列,并将其索引

        如果你的应用程序有很多JOIN查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。

        而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把DECIMAL字段和一个INT字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)。

 6.千万不要ORDER BY RAND()         想打乱返回的数据行? 随机挑一个数据 ? 真不知道谁发明了这种用法,但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。         如果你真的想把返回的数据行打乱了,你有N 种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL 会不得不去执行RAND() 函数 ( 很耗 CPU 时间 ) ,而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit1 也无济于事(因为要排序)。下面的示例是随机挑一条记录:

 7.避免SELECT *         从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。         所以,你应该养成一个需要什么就取什么的好的习惯。

8.永远为每张表设置一个ID         我们应该为数据库里的每张表都设置一个ID 做为其主键,而且最好的是一个INT 型的 ( 推荐使用 UNSIGNED) ,并设置上自动增加的 AUTO_INCREMENT 标志。                  就算是你users表有一个主键叫“email”的字段,你也别让它成为主 键。使用VARCHAR类型来当键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区……                  在这里,只有一个情况是例外,那就是“关联表 ” 的 “ 外键 ” ,也就是 说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做 “ 外键” 。比如:有一个 “ 学生表 ” 有学生的 ID ,有一个 “ 课程表 ” 有课程 ID ,那么,“ 成绩表 ” 就是 “ 关联表 ” 了,其关联了学生表和课程表,在成绩表中,学生ID 和课程 ID 叫 “ 外键 ” 其共同组成主键。 9.使用ENUM而不是VARCHAR

        ENUM类型是非常快和紧凑的。在实际上,其保存的是TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。

        如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或 “部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用ENUM而不是VARCHAR。

        MySQL也有一个“建议”(见第十条)告诉你怎么去重新组织你的表结构。当你有一个VARCHAR字段时,这个建议会告诉你把其改成ENUM类型。使用PROCEDUREANALYSE()你可以得到相关的建议。

10.从PROCEDURE ANALYSE()取得建议

        PROCEDUREANALYSE()会让MySQL帮你去分析你的字段和其实际的数据,并会给你一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。

        例如,如果你创建了一个INT字段作为你的主键,然而并没有太多的数 据,那么, PROCEDURE ANALYSE() 会建议你把这个字段的类型改成 MEDIUMINT 。或是你使用了一个VARCHAR字段,因为数据不多,你可能会得到一个让你把它改成ENUM的建议。这些建议,都是可能因为数据不够多,所以决策做得就不够准。

         一定要注意,这些只是建议,只有当你的表里的数据越来越多时,这些建议才会变得准确。一定要记住,你才是最终做决定的人。

11.尽可能的使用NOT NULL

        除非你有一个很特别的原因去使用NULL值,你应该总是让你的字段保持NOTNULL。这看起来好像有点争议,请往下看。

        首先,问问你自己“Empty”和“NULL”有多大的区别(如果是INT,那就是0和NULL)?如果你觉得它们之间没有什么区别,那么你就不要使用NULL。(你知道吗?在Oracle里,NULL和Empty的字符串是一样的!)

        不要以为NULL不需要空间,其需要额外的空间,并且,在你进行比较的时候,你的程序会更复杂。当然,这里并不是说你就不能使用NULL了,现实情况是很复杂的,依然会有些情况下,你需要使用NULL值。

12.Prepared Statements         PreparedStatements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用preparedstatements 获得很多好处,无论是性能问题还是安全问题。
        PreparedStatements可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击。当然,你也可以手动地检查你的这些变量,然而,手动的检查容易出问题,而且很经常会被程序员忘了。当我们使用一些 framework或是ORM的时候,这样的问题会好一些。         在性能方面,当一个相同的查询被使用多次的时候,这会为你带来可观的性能优势。你可以给这些PreparedStatements定义一些参数,而 MySQL 只会解析一次。
        虽然最新版本的MySQL在传输PreparedStatements是使用二进制形势,所以这会使得网络传输非常有效率。         当然,也有一些情况下,我们需要避免使用PreparedStatements ,因为其不支持查询缓存。但据说版本5.1 后支持了。

13.无缓冲的查询

        正常的情况下,当你在当你在你的脚本中执行一个SQL语句的时候,你的程序会停在那里直到没这个SQL语句返回,然后你的程序再往下继续执行。你可以使用无缓冲查询来改变这个行为。

        mysql_unbuffered_query()发送一个SQL语句到MySQL而并不像 mysql_query()一样去自动fethch和缓存结果。这会相当节约很多可观的内 存,尤其是那些会产生大量结果的查询语句,并且,不需要等到所有的结果都返回,只需要第一行数据返回的时候,你就可以开始马上开始工作于查询结果了。

        然而,这会有一些限制。因为你要么把所有行都读走,或是你要在进行下一次的查询前调用mysql_free_result()清除结果。而且,mysql_num_rows()或mysql_data_seek()将无法使用。所以,是否使用无缓冲的查询你需要仔细考虑。

14.把IP地址存成UNSIGNEDINT

        很多程序员都会创建一个VARCHAr(15)字段来存放字符串形式的IP而不是整形的IP。如果你用整形来存放,只需要4个字节,并且你可以有定长的字 段。而且,这会为你带来查询上的优势,尤其是当你需要使用这样的WHERe条件:IPbetweenip1andip2。

        我们必需要使用UNSIGNEDINT,因为IP地址会使用整个32位的无符号整形。而你的查询,你可以使用INET_ATON()来把一个字符串IP转成一个整形,并使用INET_NTOA()把一个整形转成一个字符串IP。

15.固定长度的表会更快         如果表中的所有字段都是“固定长度” 的,整个表会被认为是 “static”或“fixed-length” 。例如,表中没有如下类型的字段: VARCHAR , TEXT , BLOB。只要你包括了其中一个这些字段,那么这个表就不是 “ 固定长度静态 表 ” 了,这样, MySQL 引擎会用另一种方法来处理。         固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。         并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。         使用“ 垂直分割 ” 技术 ( 见下一条 ) ,你可以分割你的表成为两个一个是定长的,一个则是不定长的。 16.垂直分割         “垂直分割 ” 是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。( 以前,在银行做过项目,见过一张表有100 多个字段,很恐怖 )         示例一:在Users 表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把他放到另外一张表中呢?这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对于用户表来说,只有用户 ID,用户名,口令,用户角色等会被经常使用。小一点的表总是会有好的性 能。         示例二:你有一个叫“last_login”的字段,它会在每次用户登录时被更新。但是,每次更新时会导致该表的查询缓存被清空。所以,你可以把这个字段放到另一个表中,这样就不会影响你对用户ID,用户名,用户角色的不停地读取了,因为查询缓存会帮你增加很多性能。         另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。 17.拆分大的DELETE或INSERT语句

        如果你需要在一个在线的网站上去执行一个大的DELETE或INSERT查询,你需要非常小心,要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。

        如果你把你的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你泊WEB服务Crash,还可能会让你的整台服务器马上掛了。

        所以,如果你有一个大的处理,你定你一定把其拆分,使用LIMIT条件是一个好的方法。下面是一个示例:

 18.越小的列会越快

        对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。

        如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用INT来做主键,使用MEDIUMINT,SMALLINT或是更小的TINYINT会更经济一些。如果你不需要记录时间,使用DATE要比DATETIME好得多。

        当然,你也需要留够足够的扩展空间,不然,你日后来干这个事,你会死的很难看,参看Slashdot的例子(2009年11月06日),一个简单的ALTER TABLE语句花了3个多小时,因为里面有一千六百万条数据。

19.选择正确的存储引擎

        在MySQL中有两个存储引擎MyISAM和InnoDB,每个引擎都有利有弊。酷壳以前文章《MySQL:InnoDB还是MyISAM?》讨论和这个事情。

        MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM对于SELECTCOUNT(*)这类的计算是超快无比的。

        InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。他是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。

请简述常用的索引有哪些种类 ?
  • 普通索引,即针对数据库表创建索引
  • 唯一索引,与普通索引类似,不同的是,MySQL数据库索引列的值必须唯一,但允许有空值。
  • 主键索引,它是一种特殊的唯一索引,不允许有空值,一般在建表的时候同时创建主键索引。
  • 组合索引,为了进一步榨取MySQL的效率,就要考虑建立组合索引,即将数据库表中的多个字段联合起来作为一个组合索引。
  • 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据,索引的实现通常使用B树以及其变种B+树。
mysql 的复制原理以及流程
  • mysql内建的复制功能是构建大型、高性能应用程序的基础。
  • 将MySQL的数据分布到多个系统上,这种分布机制是通过MySQL的某一台的数据赋值到其他主机上(slaves),并重新执行一遍来实现的。
  • 复制过程中一个服务器充当主服务器,而一个或多个服务器充当从服务器。
  • 主服务器将更新写入二进制日志文件中,当一个从服务器连接主服务器时,它通知主服务器在日志中读取的最后一次成功的位置。
  • 从服务器接收从那一刻起发生的任何更新,然后封锁并等待主服务器通知新的更新。
  • 主服务将更新记录到二进制日志文件中
  • 从服务器把主服务器的二进制文件拷贝到自己的中继日志中
  • 从服务器重做中继日志中的时间,把更新应用到自己的数据库中。
mysql 支持的复制类型
  • 基于语句的赋值,在主服务上执行的SQL语句,在从服务器上执行相同的语句,MySQL默认采用基于语句的复制,效率比较高,一旦发生没办法精确复制时,会自动采用基于行的复制。
  • 基于行的复制,把改变的内容复制过去,,而不是把命令从服务器上执行一遍,从MySQL5.0开始支持。
  • 混合类型的复制,默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。
mysql 中 myisam 与 innodb 的区别
  • 事务支持,myisam强调的是性能,每次查询都具有原子性,其执行速度比innodb更快,但是不提供事务支持,innodb提供事务支持事务,外键等高级数据库功能,具有事务、回滚、崩溃修复邓丽的事务安全性表。
  • innodb支持行级锁,而myisam支持表级锁。用户在操作myisam表时,select、update、delete、insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。
  • innodb支持mvcc,myisam不支持
  • innodb支持外键,myisam不支持
  • 表主键,myisam允许没有任何索引和主键的表存在,索引都是保存行的地址。innodb如果没有设定主键或者非空唯一索引,就会自动生成一个6字节的主键(用户不可见),数据是主索引的一部分,附加索引保存的是主索引的值。
  • innodb不支持全文索引,而myisam支持
  • 可移植性、备份及恢复。myisam数据是以文件的形式存储,所以在跨平台的数据转移中会很方便,在备份和恢复时可单独针对某个表进行操作。innodb免费的方案是拷贝数据文件、备份binlog、或者使用mysqldump,在数据量达到几十G的时候相对痛苦
  • 存储结构,myisam,每个myisam在磁盘上存储成三个文件,第一个文件的名字以表的名字开头,,扩展名指出文件的类型。.frm文件存储表定义,数据文件的扩展名为.myd,索引的扩展名是myi。innodb,所有的表都保存在同一个文件中(也可能是多个文件,或者是独立的表空间文件),innodb表的大小只受限于操作系统文件的大小,,一般为2GB

 

mysql 中 varchar 与 char 的区别以及 varchar(50) 中的 50 代表的涵 义?
  • varchar与char的区别,char是一种固定长度的类型,varchar则是一种可变长度的类型。
  • varchar(50)中50的含义,最多放50个字节
  • int(20)中20的含义,int(M)中M,最大显示宽度。
表中有大字段 X (例如: text 类型),且字段 X 不会经常更新,以读为 为主,将该字段拆成子表好处是什么?
  • 如果字段里面有大字段(text、blob)类型的,而且这些字段的访问并不会多,这时候放在一起就会变成缺点。
  • mysql数据库的记录存储是按照行存储的,数据块大小又是固定的(16k),每条记录越小,相同的块存储的记录越多。此时应该把大字段拆走,这应应付大部分小字段查询的时候,就能提高效率
  • 当需要查询大字段的时候,此时的关联查询是不可避免的,但也是值得的。拆分开后,对字段的update就要update多个表了。
MySQL 中 InnoDB 引擎的行锁是通过加在什么上完成(或称实现) 的?

 innodb行锁是通过索引上的索引项来实现的,这一点mysql与oracle不同,后者是通过在数据块中对应数据行加锁来实现的。innodb这种行锁实现特点意味着,只有通过索引条件检索数据,innodb才是用行级锁,,否则innodb将使用表锁。

若一张表中只有一个字段 VARCHAr(N) 类型, utf8 编码,则 N 最大值 为多少 ( 精确到数量级即可 )?

 由于utf8中每个字符最多占用3个字节,而mysql定义行的长度不超过65535,因此n=(65535-1-2)/3。1是实际存储是从第二个字节开始的,2的原因是要在列表长度存储实际的字符长度,除以3是因为utf限制,每个字符最多占用3个字节。

[SELECT *] 和 [SELECT 全部字段 ] 的 2 种写法有何优缺点 ?
  • 前者要解析数据字段,后者不需要
  • 结果输出顺序,前者与建表列顺序相同,后者按指定字段顺序
  • 表字段改名,前者不需要修改,后者需要改
  • 后者可以通过建立索引进行优化,前者不可以
  • 后者的可读性比前者高

 

 HAVNG 子句 和 WHERe 的异同点?

  •  语法上,where用表中列表,having用select结果别名
  • 影响结果范围,where从表中读出数据的行数,having返回客户端的行数
  • 索引,where可以使用索引,having不能使用索引,只能在临时结果集操作
  • where后面不能使用聚集函数,having是专门使用聚集函数的
MySQL 当记录不存在时 insert, 当记录存在时 update ,语句怎么写?

 insert into table(a,b,c)values(1,2,3)on duplicate key update c=c+1;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/336311.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号