存储过程使用过程中的注意事项

前言

近几年各公司在国产化浪潮的推动下,好多大公司都开始放弃国外的一些数据库并转战国产化数据库,目前为止国产化数据库有很多,但是大多是基于mysql或者postreSql衍生出来的,比如腾讯、阿里、华为等一些大厂自研的一些数据库。目前为止从摩天轮国产化数据库排名情况来看,已经有276款国产化数据库了,这些国产化数据库虽然大都遵循SQL标准,但是对于存储过程的实现可能还有一些区别。就大家耳熟能详的oracel和mysql的存储过程语法就大不相同。

什么情况下使用存储过程?

就依我的工作经验来看,存储过程的使用情况一般有以下几个常见的场景:
1、程序做批量数据处理后,一般会编写存储过程来稽核数据处理是否准确。
2、维护人员的一些告警数据监控。
3、比较老旧的业务。目前我接触的项目中一些老的需求都是通过存储过程实现的。
4、一些比较抽象的业务,涉及多种表且业务变更不频繁,一般使用存储过程封装后供程序调用。

使用存储过程的好处:

存储过程并没有网上一些网友所说的那样一无是处,对于特殊的场景可能使用存储过程还比较好。我目前接触到的电信、以及银行业务中有大量的业务就是通过存储过程实现的。当然了使用存储过程实现只是因为历史原因,比如电信行业属于软件行业中发展较早的,那时数据库可能都是oracle,所以完全不必考虑数据库迁移的问题,存储过程编写简单,语法也不复杂。发展到现在虽然好多业务已经使用java或c++重构,但是依然保留了许多存储过程,原因是这类业务基本上好多年没有变更过,但是业务逻辑复杂,比如银行查询用户的承诺消费总额,业务逻辑很复杂,涉及多个表的关联,如果使用java程序来实现首先就效率上而言不一定会比存储过程快,因为存储过程运行时省去了编译这一环节。综上可以总结出使用存储过程的一些优点:
1、逻辑相对复杂的存储过程在效率上要高于逻辑复杂的其他程序。
2、存储过程入门的门槛比较低,语法相对简单一些。
3、一些复杂业务使用存储过程封装后,避免了暴露表结构字段等信息给外部开发人员。因为一些表中可能存有敏感信息。

使用存储过程的缺点

就现在的大环境下,存储过程的缺点那显然已经完全暴露了,比如我所经历过的去oracle项目,最让人头疼就是存储过程的迁移,语法的变更,调试,编写,几千行的存储过程看的脑袋都大了。如果没有备注根本看不懂程序所表达的意思。另外就是存储过程对于游标的支持。比如oracle支持动态游标,而mysql并不支持动态游标,需要变相的去实现,于是这里就出现了问题,比如我之前一个存储过程迁移到mysql后,由于老的oracle使用的动态游标,而mysql由于不支持动态游标, 所以年轻的我使用了视图变相实现了存储过程,写完测试环境跑了一下完全没有问题,可是到了生产上需要并发跑的时候出现了问题,由于视图是数据库级别的,同一个存储过程在多个不同客户端同时运行后,都是先删除已有的视图,然后重新创建视图,这样导致视图中的数据反复变化,最终执行结果自然也就不准确了。综上可以总结出存储过程的缺点:

1、数据库迁移不方便。
2、对于动态游标的支持不友好。
3、缺少好用的idea。
4、调试不方便。

调试方式

oracle目前plsql已经支持debug模式了,可以调试oracle存储过程,但是mysql还不支持调试。所以mysql存储过程调试只能依赖日志记录表来观察数据的变化。以下是一个mysql存储过程调试的小工具,供读者参考:

使用方法
-- 存储过程:
-- 开始前调用
call test.debug_begin('存储过程名称', '断点名称'sql);

do soming .....

-- 结束后调用
call test.debug_end('存储过程名称', '断点名称’);


-- 查看性能统计数据
SELECT * FROM test.debug_log;

1、开始
DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `debug_begin`$$

CREATE DEFINER=`testuser`@`%` PROCEDURE `debug_begin`(proc_name VARCHAR ( 64 ), stat_name VARCHAR ( 64 ), sql_line VARCHAR ( 2048 ))
BEGIN
	DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    DECLARE v_err_count INT UNSIGNED DEFAULT 0;
    GET DIAGNOSTICS v_err_count = NUMBER;
  END;
	INSERT INTO test.debug_log(session_id, `proc_name`, `stat_name`, `sql_line`, `status`, begin_time)
					VALUES(CONNECTION_ID(), proc_name, stat_name, sql_line, 1, SYSDATE());
	COMMIT;
END$$

DELIMITER ;
2、结束
DELIMITER $$

USE `test`$$

DROP PROCEDURE IF EXISTS `debug_end`$$

CREATE DEFINER=`testuser`@`%` PROCEDURE `debug_end`(proc_name VARCHAR ( 64 ), stat_name VARCHAR ( 64 ))
BEGIN
	DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
    DECLARE v_err_count INT UNSIGNED DEFAULT 0;
    GET DIAGNOSTICS v_err_count = NUMBER;
  END;
	UPDATE test.debug_log SET end_time = SYSDATE(), cost = TIMESTAMPDIFF(SECOND, begin_time, SYSDATE()), `status` = 2
					WHERE STATUS = 1 AND session_id = CONNECTION_ID() AND `proc_name` = proc_name AND `stat_name` = stat_name;
	COMMIT;
END$$

DELIMITER ;
3、结果表:
CREATE TABLE `test`.`debug_log` (
  `session_id`	BIGINT(20)	NOT NULL comment '会话编号',
  `proc_name` varchar(64) COLLATE utf8_bin NOT NULL comment '存储过程名称',
  `stat_name` varchar(64) COLLATE utf8_bin NOT NULL comment '断点名称',
  `sql_line` varchar(1024) COLLATE utf8_bin comment 'SQL语句',
  `begin_time` DATETIME comment '开始时间',
  `end_time` DATETIME comment '结束时间',
  `cost`  BIGINT(20) comment '耗时(秒)',
  `status` smallint(6) NOT NULL	comment '运行状态: 1-运行中 2-运行结束',
  `sonbr`   BIGINT  not null   auto_increment comment 'auto increment',
   primary key (sonbr) using hash
) ;

你可能感兴趣的:(数据库,mysql)