到底该不该使用存储过程

 

看到《阿里巴巴java编码规范》有这样一条

关于这条规范,我说说我个人的看法

用不用存储过程要视所使用的数据库和业务场景而定的,不能因为阿里巴巴的技术牛逼,就视他们的手册里的每一项规范为圣经, 盲目的去遵循。

对于手册中的这项规范,我觉得使用MySQL的程序员认同占多数, 而使用SQL Server的程序员反对占多数。 原因在于MySQL对SQL编程和复杂查询性能优化的支持实在太烂。不过从手册的上下文来判断这条规范应该就是针对MySQL的。

记得MySQL支持存储过程是5.0版本开始的, 那时候已经是2006年了,而从新版本发布到用户全面升级,势必要延迟几年, 而同时期的Sql Server2005和Sql Server2008的Transact-SQL编程已经相当成熟,不是MySQL能够追赶的上的。 而业务逻辑这个东西,可以放在SQL端实现也可以放在语言端实现,恰恰MySQL对于SQL编程支持的不完善,造成使用MySql数据库的程序员都偏向于把逻辑放在语言中实现,加上互联网的蓬勃发展,MySQL使用场景越来越广,此消彼长, SQL编程技术也就越来越不招人待见。

然而,这并不能表明SQL编程无可取之处。

在微软技术栈程序员中,存储过程绝对神器级的解决问题手段,不管是存储过程, 视图、触发器、自定义函数这些都是极常用的技术。而这些在MySQL中基本上是见不到的,在开发基于MySQL的项目中,要是有人胆敢使用此类技术,绝对会被视为异端, 并毫不留情的遭受打压。在微软技术栈中,优先推荐将业务逻辑使用Transact-SQL编程实现,封装在数据库中, 供外部.Net程序调用。有的项目通过这种方式实现,外部的程序就是个空壳子,所有复杂的逻辑全在数据库里面。 拿MVC模式打比方,model层的内容全在数据库里, 程序中只剩Controller和View层的内容, 这会让人产生一种编程语言没鸟用的错觉。 但是这样做是有好处的。

首先,很多人说存储过程性能不行,其实也不一定, 尤其是Sql Server。大规模高并发分布式场景下SQL编程不占优势,但是并非所有应用都是如此的, 在普通应用场景下,SQL编程的性能优势就体现出来了。 因为编程语言对数据库的操作最终都会转化为SQL语句传递给数据库执行, 那在具备优秀的SQL编程能力的情况下,通过SQL编程实现业务逻辑显然比通过程序语言实现更底层, 粒度更细, 和数据库本身结合的更紧密,更利于性能调优。 虽然原本在程序端的计算量被转移至了数据库端, 程序端压力小, 数据库端压力大, 但是两者相加的总压力却变小了,因为把逻辑放在程序端实现,无法百发百的压榨数据库性能,从而导致资源浪费。 通过SQL编程实现业务逻辑, 数据库压力虽然大了,却可把原来属于程序端的服务器资源划给数据库端,这样在总体上来说资源不但不浪费,而且还节省了。 通过数据库实现业务逻辑的性价比更高。再者,大多数程序的业务逻辑无非是对数据库的增删查改,没有谁比SQL更适合干这个事情,包括编程语言,因此用SQL编程来实现业务逻辑最合适不过。 不要说什么SQL编程不支持面向对象,无法解决复杂问题,先不说大多数项目没有到达复杂的程度, 现在主流的ORM框架的实现都是不符合理论的,谁说关系表可以映射为对象的,荒唐。况且,SQL语句面向结果编程符合的函数式编程模式,而函数式编程是现代编程界的一股清流,牛逼之处不言而喻。 即使真碰到什么问题是SQL编程无法解决的,也可以把这部分问题提取出来通过程序实现, 但我相信这样的问题总是占少数的。

其次, 把逻辑封装在存储过程里,有一个好处是改动方便。 改程序需要重新编译、停服、发布, 存储过程是可以热更新的, 能减小发布程序所带来的影响。在以SQL Server为基础的程序中,光上面说的这些也足够成为用SQL编程实现业务逻辑的理由了。但在MySQL下是不成立的, 我曾经见过一个.net程序员强行把MySQL当SQL Server使,结果项目后期维护跟翔一样臭。

这个Java手册是从阿里巴巴的业务场景中提炼出来的, 照着它练能做出很厉害的项目来。但是在整个软件开发行业,不是所有的企业都是互联网公司, 即使在整个互联网行业中,也不是所有的公司都是阿里巴巴,不是所有的项目都要应对巨量请求,不是所有的项目都使用MySQL数据库。 诚然, 这个手册的规则是正确无疑的,可就像韦小宝说的,跟小皇帝打架用的着花几十年时间练化骨绵掌吗,花几小时练挤奶龙爪手就足够了。 所以, 是不是用存储过程还是视实际情况而定,一票否决是不明智的。

还有,那些说存储过程难以调式难以修改的, 要不就是没用对数据库, 要不就是SQL编程能力不足。以我个人的经验来说,存储过程是个好东西,尤其在SQL Server下。如果数据库仅仅是用来当存储数据的仓库,那像Oracle、IBM、Microsoft用的着费劲把它们产品的功能做这么强大吗, 那些已故的数库领域的先驱都要死不瞑目了。

--

MySQL查询性能优化一则

 

公司有一套Web系统, 使用方反馈系统某些页面访问速度缓慢, 用户体验很差, 并且偶尔还会出现HTTP 502错误。

这是典型的服务器端IO阻塞引发的问题,通过对访问页面的程序逻辑进行跟踪,发现问题应该是出在某个SQL查询上。

在页面程序运行的某个步骤中,有这样一段SQL

select distinct(server)  from user_record where type = 'GD0001'

user_record表中的数据大概有2000万条左右 , 字段type的值为GD0001的记录大概有500万,而这段SQL执行的结果大概有30多条。type字段上有索引,但是SQL语句的执行时间却要超过一分钟。

得到去重后server字段的值是导致页面访问缓慢的根本原因。

根据程序的要求, server字段的值需要实时求得,所以当初在设计程序的时候才会使用这段SQL去获得结果。数据量少的时候,不会出现问题,然而, 数据增长的速度超出当初的预期,于是就导致了性能问题的出现。

要解决这个问题不难,因为server字段值的范围相对是稳定的,可以想办法把值提取出来放到一个冗余的表里面,并且通过某种机制让这个新表的值与原表中server字段的值保持同步,查的时候查这个新表, 这样访问速度缓慢的问题也就迎刃而解了。

显然,使用这种方案解决问题需要不小的工作量。要使解决这个问题的成本最小化,最好的方法是优化这个查询,假如原本这个查询运行的时间是一分钟,那么能使运行这个查询的时间下降至一秒,问题也算解决。

这个目标看起来似乎难以实现,事实上却是可以做到的。

select distinct(server)  from user_record where type = 'GD0001'

因为这段SQL语句的筛选条件type字段有索引,所以整个SQL语句的逻辑查询步骤大致如下

  1. 通过type索引筛选出符合要求记录的主键字段的标识

  2. 通过主键标识定位到表中记录的源数据

  3. 拿到字段的值进行distinct去重得到最终的结果。

上面的三个步骤中,最消耗性能的是第二步。因为索引和表的实际数据其实是分开放置的,大概的样子如下面这个图。图中长的最大的那个其实就是数据表,表中所有的数据都在上面,只是看起来不像一张“表”而已。

第二步是通过索引筛选出符合条件的记录的主键标识定位到实际数据,过程大概如下面这张图

想象一下, 要优化的那段SQL,而type值为GD0001记录有500万条, 就算MySQL不会蠢到去查500万次才能得到结果,但也肯定不是轻轻松松就能完成的。 如果能优化掉这一步,整个查询的开销也就下去了。

select distinct(server) from user_record where type = 'GD0001'

对于这段SQL,我们的目标是并不是得到所有字段的值,仅仅server字段的值就足够了。

假如我们把server字段的值放在type字段的索引里,那么在第一步查索引的时候就能得到第二步的结果。执行过程如下图

在关系数据库中,有一种索引称为覆盖索引,就是为了满足这种优化需求而设计的。

针对这段SQL语句优化的覆盖索引创建语句如下

create index index_type_server  on user_record(type, server)

这个索引创建语句会将type和server两个字段的值组织在一个索引里面, 因此当

select distinct(server)   from user_record    where type = 'GD0001'

所有的查询步骤在索引中就能完成,而不用再去源数据表里提取数据,也就是在没建立这个索引时进行查询的第二步被消除了,因此查询的性能极大幅度的得到了提升。

在没建立覆盖索引前,查询的时间需要一分钟以上,在建立索引后,查询的时间下降到几百毫秒的级别。原本网页加载缓慢和偶尔报HTTP 502错误失去响应的问题也得到了解决。

让SQL语句合理的利用索引快速的得到查询结果是一门学问,值得深究。 合理利用索引,能让对程序性能的优化从代码层面转移到数据库层面, 让问题由最适合解决的工具和手段去解决,物尽其用,如此不但能减少代码复杂度,还能提高解决问题的效率。这是一个程序员必须要具备的一种技能。

延伸阅读:http://www.cnblogs.com/aspwebchh/p/6652855.html

原文链接:https://www.chhblog.com/article_view?id=385

本文首发于微信公众号 「带你撸出一手好代码」,欢迎关注获取更多原创分享

知乎:https://www.zhihu.com/people/aspwebchh

github:https://github.com/aspwebchh

 
 

到底该不该使用存储过程 MySQL查询性能优化一则的更多相关文章

  1. MySQL查询性能优化七种武器之索引下推

    前面已经讲了MySQL的其他查询性能优化方式,没看过可以去了解一下: MySQL查询性能优化七种武器之索引潜水 MySQL查询性能优化七种武器之链路追踪 今天要讲的是MySQL的另一种查询性能优化方式 ...

  2. MySQL查询性能优化(精)

    MySQL查询性能优化 MySQL查询性能的优化涉及多个方面,其中包括库表结构.建立合理的索引.设计合理的查询.库表结构包括如何设计表之间的关联.表字段的数据类型等.这需要依据具体的场景进行设计.如下 ...

  3. 170727、MySQL查询性能优化

    MySQL查询性能优化 MySQL查询性能的优化涉及多个方面,其中包括库表结构.建立合理的索引.设计合理的查询.库表结构包括如何设计表之间的关联.表字段的数据类型等.这需要依据具体的场景进行设计.如下 ...

  4. mysql查询性能优化

    mysql查询过程: 客户端发送查询请求. 服务器检查查询缓存,如果命中缓存,则返回结果,否则,继续执行. 服务器进行sql解析,预处理,再由优化器生成执行计划. Mysql调用存储引擎API执行优化 ...

  5. MySQL优化技巧之五(mysql查询性能优化)

    对于高性能数据库操作,只靠设计最优的库表结构.建立最好的索引是不够的,还需要合理的设计查询.如果查询写得很糟糕,即使库表结构再合理.索引再合适,也无法实现高性能.查询优化.索引优化.库表结构优化需要齐 ...

  6. MySQl 查询性能优化相关

    0. 1.参考 提升网站访问速度的 SQL 查询优化技巧 缓存一切数据,读取内存而不是硬盘IO 如果你的服务器默认情况下没有使用MySQL查询缓存,那么你应该开启缓存.开启缓存意味着MySQL 会把所 ...

  7. MySQL查询性能优化---高性能(二)

    转载地址:https://segmentfault.com/a/1190000011330649 避免向数据库请求不需要的数据 在访问数据库时,应该只请求需要的行和列.请求多余的行和列会消耗MySql ...

  8. 《高性能MySQL》之MySQL查询性能优化

    为什么查询会慢? 响应时间过长.如果把查询看做是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间.如果要优化查询,实际上优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数, ...

  9. MySQL查询性能优化七种武器之索引潜水

    有读者可能会一脸懵逼? 啥是索引潜水? 你给起的名字的吗?有没有索引蛙泳? 这个名字还真不是我起的,今天要讲的知识点就叫索引潜水(Index dive). 先要从一件怪事说起: 我先造点数据复现一下问 ...

随机推荐

  1. 拖拽功能-jquery

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. 最小生成树 Prim算法 Kruskal算法实现

    最小生成树定义 最小生成树是一副连通加权无向图中一棵权值最小的生成树. 在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即,而 w(u, v) 代表此边的 ...

  3. webpack 之 plugin

    loader和plugins的区别是:前者处理单个文件,而后者作用于整个打包过程 内置插件:BannerPlugin 可以在打包后的文件上方添加备注信息,如版权说明等 // webpack.confi ...

  4. 老男孩老师的博客地址 - 转自devops1992

    害怕他那天不让人看了,所以我就复制一份到我自己的博客里. http://www.bootcdn.cn/bootstrap/  bootstrap cdn在线地址 http://www.cnblogs. ...

  5. 免费开源《OdooERP系统部署架构指南》试读:第一章 Odoo架构概述

    文/开源智造联合创始人老杨 本文来自<OdooERP系统部署架构指南>的试读章节.书籍尚未出版,请勿转载.欢迎您反馈阅读意见. 从web浏览器到PostgreSQL,多层与其他层交互以处理 ...

  6. 虚拟机如何设置静态IP

    一.本机环境 Mac.VMware Fusion 10, CentOS6.8 二.设置静态IP地址 1.选择网络连接模式,选择NAT模式 注意: 1)必须要选择NAT模式,否则你的虚拟机与主机始终会在 ...

  7. Python中的类(2)

    一.使用类和实例 我们先编写一个学生的类,它存储了有关学生的信息,还有一个整合学生信息的方法: student.py class Student(): def __init__(self,name,a ...

  8. CentOS 7 设置开机自启动

    创建脚本:    #!/bin/bash    echo "hello!" # 启动虚拟环境    cd /data/env/crmenv/bin/    source activ ...

  9. win10和office2013激活

    1.去网上找kms,也可以在这下载————http://pan.baidu.com/s/1sjEAvwD————PS:找好对应的版本 2.首次运行时,只能点击激活windows VL和office 2 ...

  10. Uncaught ReferenceError: 板栗 is not defined at HTMLButtonElement.onclick (view:1)

    对JS传值一直以为都是随便传过去就行,直到今天遇到了中文传值的问题 中文传值不能够需要在调用的位置加 对于要传的值加单引号或者双引号 比如说下面这个样子,我这里还还记反斜杠注释 '<button ...