问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何解决SQL Server查询速度缓慢的问题

发布网友 发布时间:2022-04-23 16:13

我来回答

2个回答

懂视网 时间:2022-04-10 03:22

12. Prepared Statements

Prepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用 prepared statements 获得很多好处,无论是性能问题还是安全问题。

Prepared Statements 可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击。当然,你也可以手动地检查你的这些变量,然而,手动的检查容易出问题,而且很经常会被程序员忘了。当我们使用一些framework或是ORM的时候,这样的问题会好一些。

在性能方面,当一个相同的查询被使用多次的时候,这会为你带来可观的性能优势。你可以给这些Prepared Statements定义一些参数,而MySQL只会解析一次。

虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势,所以这会使得网络传输非常有效率。

当然,也有一些情况下,我们需要避免使用Prepared Statements,因为其不支持查询缓存。但据说版本5.1后支持了。

在PHP中要使用prepared statements,你可以查看其使用手册:mysqli 扩展 或是使用数据库抽象层,如: PDO.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 创建 prepared statement if ($stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?")) {       // 绑定参数     $stmt->bind_param("s", $state);       // 执行     $stmt->execute();       // 绑定结果     $stmt->bind_result($username);       // 移动游标     $stmt->fetch();       printf("%s is from %s ", $username, $state);       $stmt->close(); }

13. 无缓冲的查询

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

关于这个事情,在PHP的文档中有一个非常不错的说明: mysql_unbuffered_query() 函数:

“mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”

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

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

14. 把IP地址存成 UNSIGNED INT

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

我们必需要使用UNSIGNED INT,因为 IP地址会使用整个32位的无符号整形。

而你的查询,你可以使用 INET_ATON() 来把一个字符串IP转成一个整形,并使用 INET_NTOA() 把一个整形转成一个字符串IP。在PHP中,也有这样的函数 ip2long() 和 long2ip()。

1 $r = "UPDATE users SET ip = INET_ATON(‘{$_SERVER[‘REMOTE_ADDR‘]}‘) WHERE user_id = $user_id";

15. 固定长度的表会更快

如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。

固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。

并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论你用不用,他都是要分配那么多的空间。

使用“垂直分割”技术(见下一条),你可以分割你的表成为两个一个是定长的,一个则是不定长的。

16. 垂直分割

“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。(以前,在银行做过项目,见过一张表有100多个字段,很恐怖)

示例一:在Users表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把他放到另外一张表中呢? 这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对于用户表来说,只有用户ID,用户名,口令,用户角色等会被经常使用。小一点的表总是会有好的性能。

示例二: 你有一个叫 “last_login” 的字段,它会在每次用户登录时被更新。但是,每次更新时会导致该表的查询缓存被清空。所以,你可以把这个字段放到另一个表中,这样就不会影响你对用户ID,用户名,用户角色的不停地读取了,因为查询缓存会帮你增加很多性能。

另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。

17. 拆分大的 DELETE 或 INSERT 语句

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

Apache 会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子进程,线程和数据库链接,这是极大的占服务器资源的事情,尤其是内存。

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

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

1 2 3 4 5 6 7 8 9 10 while (1) {     //每次只做1000条     mysql_query("DELETE FROM logs WHERE log_date <= ‘2009-11-01‘ LIMIT 1000");     if (mysql_affected_rows() == 0) {         // 没得可删了,退出!         break;     }     // 每次都要休息一会儿     usleep(50000); }

18. 越小的列会越快

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

参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。

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

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

19. 选择正确的存储引擎

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

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

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

下面是MySQL的手册

  • target=”_blank”MyISAM Storage Engine
  • InnoDB Storage Engine
  • 20. 使用一个对象关系映射器(Object Relational Mapper)

    使用 ORM (Object Relational Mapper),你能够获得可靠的性能增涨。一个ORM可以做的所有事情,也能被手动的编写出来。但是,这需要一个高级专家。

    ORM 的最重要的是“Lazy Loading”,也就是说,只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用,因为这很有可能会因为要去创建很多很多小的查询反而会降低性能。

    ORM 还可以把你的SQL语句打包成一个事务,这会比单独执行他们快得多得多。

    目前,个人最喜欢的PHP的ORM是:Doctrine。

    21. 小心“永久链接”

    “永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了,它会永远处在连接的状态,就算是数据库操作已经结束了。而且,自从我们的Apache开始重用它的子进程后——也就是说,下一次的HTTP请求会重用Apache的子进程,并重用相同的 MySQL 链接。

  • PHP手册:mysql_pconnect()
  • 在理论上来说,这听起来非常的不错。但是从个人经验(也是大多数人的)上来说,这个功能制造出来的麻烦事更多。因为,你只有有限的链接数,内存问题,文件句柄数,等等。

    而且,Apache 运行在极端并行的环境中,会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前,你需要好好地考虑一下你的整个系统的架构。

    避免写出不走索引的SQL, MySQL

    http://jaskey.github.io/blog/2016/01/19/mysql-bad-sql-with-no-index/

     

    避免写慢SQL

    标签:自己   而不是   zed   blog   程序   print   apach   var   soc   

    热心网友 时间:2022-04-10 00:30

    1.查询的模糊匹配
    尽量避免在一个复杂查询里面使用 LIKE '%parm1%'——红色标识位置的百分号会导致相关列的索引无法使用,最好不要用.
    解决办法:

    其实只需要对该脚本略做改进,查询速度便会提高近百倍。改进方法如下:
    a、修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了。
    b、直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联
    2.索引问题
    在做性能跟踪分析过程中,经常发现有不少后台程序的性能问题是因为缺少合适索引造成的,有些表甚至一个索引都没有。这种情况往往都是因为在设计表时,没去定义索引,而开发初期,由于表记录很少,索引创建与否,可能对性能没啥影响,开发人员因此也未多加重视。然一旦程序发布到生产环境,随着时间的推移,表记录越来越多
    这时缺少索引,对性能的影响便会越来越大了。
    这个问题需要数据库设计人员和开发人员共同关注
    法则:不要在建立的索引的数据列上进行下列操作:
    ◆避免对索引字段进行计算、函数、类型转换
    ◆避免在索引字段上使用not,<>,!=
    ◆避免在索引列上使用空值、IS NULL和IS NOT NULL
    3.复杂操作
    部分UPDATE、SELECT 语句写得很复杂(经常嵌套多级子查询)——可以考虑适当拆成几步,先生成一些临时数据表,再进行关联操作
    4.update
    同一个表的修改在一个过程里出现好几十次,如:
    update table1
    set col1=...
    where col2=...;
    update table1
    set col1=...
    where col2=...
    ......
    象这类脚本其实可以很简单就整合在一个UPDATE语句来完成(前些时候在协助xxx项目做性能问题分析时就发现存在这种情况)
    5.在可以使用UNION ALL的语句里,使用了UNION
    UNION 因为会将各查询子集的记录做比较,故比起UNIONALL ,通常速度都会慢上许多。一般来说,如果使用UNION ALL能满足要求的话,务必使用UNION ALL。还有一种情况大家可能会忽略掉,就是虽然要求几个子集的并集需要过滤掉重复记录,但由于脚本的特殊性,不可能存在重复记录,这时便应该使用UNION ALL,如xx模块的某个查询程序就曾经存在这种情况,见,由于语句的特殊性,在这个脚本中几个子集的记录绝对不可能重复,故可以改用UNION ALL)
    6.在WHERE 语句中,尽量避免对索引字段进行计算操作
    这个常识相信绝大部分开发人员都应该知道,但仍有不少人这么使用,我想其中一个最主要的原因可能是为了编写写简单而损害了性能,那就不可取了
    9月份在对XX系统做性能分析时发现,有大量的后台程序存在类似用法,如:
    ......
    where trunc(create_date)=trunc(:date1)
    虽然已对create_date 字段建了索引,但由于加了TRUNC,使得索引无法用上。此处正确的写法应该是
    where create_date>=trunc(:date1) andcreate_date
    或者是
    where create_date between trunc(:date1) andtrunc(:date1)+1-1/(24*60*60)
    注意:因between 的范围是个闭区间(greater than or equal to low value and less than or equal to highvalue.),
    故严格意义上应该再减去一个趋于0的小数,这里暂且设置成减去1秒(1/(24*60*60)),如果不要求这么精确的话,可以略掉这步。
    7.对Where 语句的法则
    7.1避免在WHERE子句中使用in,not in,or 或者having。
    可以使用 exist 和not exist代替 in和not in。
    可以使用表链接代替 exist。Having可以用where代替,如果无法代替可以分两步处理。
    例子
    SELECT * FROM ORDERS WHERE CUSTOMER_NAME NOT IN
    (SELECT CUSTOMER_NAME FROM CUSTOMER)
    优化
    SELECT * FROM ORDERS WHERE CUSTOMER_NAME not exist
    (SELECT CUSTOMER_NAME FROM CUSTOMER)
    7.2 不要以字符格式声明数字,要以数字格式声明字符值。(日期同样)否则会使索引无效,产生全表扫描。
    例子使用:
    SELECT emp.ename, emp.jobFROM emp WHERE emp.empno = 7369;
    不要使用:SELECT emp.ename,emp.job FROM emp WHERE emp.empno = ‘7369’
    8.对Select语句的法则
    在应用程序、包和过程中*使用select * from table这种方式。看下面例子
    使用SELECT empno,ename,categoryFROM emp WHERE empno = '7369‘
    而不要使用SELECT * FROM empWHERE empno = '7369'
    9. 排序
    避免使用耗费资源的操作,带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎 执行,耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序
    10.临时表
    慎重使用临时表可以极大的提高系统性能
    所谓的优化就是WHERE子句利用了索引,不可优化即发生了表扫描或额外开销。经验显示,SQL Server性能的最大改进得益于逻辑的数据库设计、索引设计和查询设计方面。反过来说,最大的性能问题常常是由其中这些相同方面中的不足引起的。其实SQL优化的实质就是在结果正确的前提下,用优化器可以识别的语句,充份利用索引,减少表扫描的I/O次数,尽量避免表搜索的发生。
    其实SQL的性能优化是一个复杂的过程,上述这些只是在应用层次的一种体现,深入研究还会涉及数据库层的资源配置、网络层的流量控制以及操作系统层的总体设计。
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    你能用一句话表达这个画面吗 专科学高铁乘务专业能找到工作吗 广西理工职业技术学院怎样啊?? 月亮星座怎么配对,月亮星座配对查询爱情 广西理工职业技术学院到底好不好的 最甜蜜的月亮星座组合,最冷血的月亮星座 包子怎么做才松软 包子如何做才松软 包子店的包子发酵怎么这么松软 专做俄罗斯业务的货代公司 大理石地板怎么拖干净又亮妙招 ipad上timelock怎么用 如何设定 sybase 的 lock time out DB2的lock timeout值是在哪儿设置的 怎么用Hql语句取今天的数据,是00.00.00-23.59.59之间的。 Oracle中Password_lock_time设置为 UNLIMITED 是不是不会自动解锁_百度... Time can&#39;t lock time 是什么意思 锁住时间英文怎么说? locktime是什么意思 还钱时没有约定是归还本金、还是归还利息,该怎么认定 没有约定保证期间怎么处理 合同没有约定 合同没有约定履行时间怎么办 “没有约定 没有诺言 没有时间 ”谁哪首歌的歌词 合同中没有约定违约金的怎么处理呢 合同没有约定地点怎么办? 何车无轮;何猪无嘴;何驴无毛;何屋无门;何书无字;何花无果?? 合同中没有约定履行期限怎么办 汉初三大名将分别都是谁,他们最后的结局如何? 没有约定,而意外地相遇.成语是哪个 分手不能做朋友,因为彼此伤害过;分手不能做敌人,因为彼此相爱过。是真的吗?? timelock加密的文件如何解出来 PostgreSQL存储过程转换SQLServer的 草莓酱不放柠檬可以吗 做果酱为什么要加柠檬汁 请UNIX高手看看,UNIX服务器究竟是何问题? 系统运行一段时间为何自动重启? 银行卡丢失,补办需要多长时间? 做草莓酱没有柠檬可以用橙汁代替吗? 为什么我的电脑按caps lock键会弹出游戏?就是会切换到别的页面 为什么我的笔记本 有的时候一按大写键电脑就自动锁定了 不用柠檬的做法草莓酱? 农村商业银行卡丢失了补办一张需要多久? 最近发现我一按大小写锁定键 就会弹到桌面上!头疼死了!!! 我的笔记本为什么切换大小写的时候都会切出操作界面啊? 草莓酱不放柠檬可以吗 做果酱为什么要加 为什么我的电脑DELL的大小写切换时总是跳动,除了禁用quickset之外还有什么办法? 自制草莓酱为什么要放柠檬?不放可以吗? 戴尔屏幕右下角“大写锁定”问题 做果酱不放柠檬汁行吗 win7任务栏任何消息不闪烁 草莓酱的做法? 银行卡丢了,补一张卡多长时间?