在一个没有互联网公司上班节奏的互联网公司上班,是一种什么样的体验?钱多活少少加班。
但是不要太羡慕我,因为,这家公司要撑不多久了。据总裁说,撑到年末,生则还罢,死则好散。刚毕业就进入一家走下坡路,到今日奄奄一息的公司,我也算是“走运”啦。感恩的是自己没有被裁掉,并且在这节奏不快的日子遇到好老大、好同事,做项目,不断学习着、进步着。
前几天看到得到招有代码洁癖的工程师,我联想到一起合作过几周的同事。他差不多就是有代码洁癖的人,注释写得……我说不明白,就是一切都很规范。我还写不了多高明的代码,至少做到规范吧。
本周总结就从代码规范之打日志开始:
1. 代码规范-日志打印
打日志的作用之一是方便排查问题(也有注释的作用,我还没想到其他作用……)。不然报个错、抛个异常,你都不知道是什么原因导致的,是传参错了,还是逻辑错了?不明所以会很抓狂。
首先,日志的打印格式是在Spring配置文件中定义的,例如:
<Pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:-} [%15.15t] %-40.40logger{39} : %m%n
</Pattern>
这里不详细解释其中参数的含义了(因为我还不懂==!)
其次,在源文件中注意在关键位置打日志
最基本的,要打日志的位置:入参,出参,异常信息……
注意日志要打得有信息含量。举例没有信息含量的日志:
log.info("交易成功。"); // 这对于我看前后的问题没有帮助好吧,至少打印一下返回什么消息了吧。
比较有信息量的日志:
log.info("交易成功, 返回的消息为:{}", msg.toString());
另外,定义打日志的规范,每个方法定义自己的日志前缀。什么意思?我们一般会打印日志生成的类的名字,如果再追加方法名,能将日志粒度细化,更容易定位问题。出现问题了,你一看方法名,能大概知道程序执行到哪个环节出问题了。
我现在打日志的套路:
public void myMethod(String aStr) {
// 日志前缀:方法名
String logPrefix = Thread.currentThread().getStackTrace()[1].getMethodName() +"(): ";
log.info("{}我是一行日志:{}", logPrefix, aStr);
// 其他代码
}
2. SQLException: Unknown error 1205
这周在测代码,到了更新表那步的时候,长时间不返回,抛了一个异常。抛异常不怕,怕的是你告诉我它是“Unknown error”。好吧,有用的信息是执行SQL语句的时候出现问题了,错误码是1205,现象是程序执行到一个位置长时间(好几秒的样子)不往下进行,最后抛了异常,debug结束。
从异常信息来看是MySQL数据库出现问题了,去Mysql直接执行这条sql也出现同样的现象,长时间不返回,最后报了一个1205。这种情况,要么是锁等待,要么是有没提交的事务,导致其他SQL语句无法执行。
(1)检查是否存在锁等待
select * from information_schema.innodb_lock_waits; -- 查看是否有锁等待
show full processlist; -- 查看所有运行着的进程,是否有跟锁有关的进程
(2)查看未提交事务
SELECT * FROM information_schema.INNODB_TRX; -- 查看未提交的事务
SELECT trx_mysql_thread_id FROM information_schema.INNODB_TRX; -- 查看事务的线程id
kill some_id; -- 停掉事务,kill的是trx_mysql_thread_id
在我这个问题场景中没有锁等待,但查出了未提交事务。我kill掉这个事务之后,SQL语句就可以执行了。至于这个事务是怎么冒出来的,我也不知道==!
如果抛的异常带有lock wait的信息,那就是有锁等待了。
3. 接着聊Mysql
顺着Mysql接着码一下这2个月以来自己写过的关于索引、事件、存储过程的SQL语句。这部分没什么,就是Mysql的语法,如果你很熟,此部分可以跳过:
(1)索引操作
-- 加索引
alter table TABLE_NAME add index idx_name (COLUMN_NAME);
-- 删索引
drop index idx_name on TABLE_NAME;
-- 查看索引
show index from TABLE_NAME;
加索引的讲究以后再说(这个我还是知道一点的)。
(2)事件调度器
Mysql的事件调度器,作用类似linux下的crontab,java中的scheduler。可以定时执行某项任务。
-- 查看事件调度器
SHOW VARIABLES LIKE 'event_scheduler';
-- 打开事件调度器
SET GLOBAL event_scheduler = ON;
-- 删除事件调度器
DROP EVENT IF EXISTS event_name;
-- 创建事件调度器
CREATE EVENT event_name ON SCHEDULE EVERY 1 DAY STARTS '2017-09-13 00:00:00' ENABLE DO update table_name set column_name = 0;
-- 修改事件调度器
ALTER EVENT event_name ON SCHEDULE EVERY 1 DAY;
事件调度器的时间格式这里就不介绍了~~
4. 存储过程
对!细心的你可能发现了,3没有介绍存储过程。对!我放到这里啦~
这里要讲,在Mysql中创建存储过程,以及如何在Mybatis中调用存储过程。
(1)Mysql创建存储过程
drop procedure if exists procedure_name; -- 删除存储过程
delimiter // -- 修改结束符为"//"
-- 创建存储过程
CREATE PROCEDURE procedure_name(OUT result INT) -- OUT 指定返回参数
BEGIN
update table_name set column_name = column_name+1;
SELECT column_name from table_name;
END;//
delimiter ; --把结束符改回";"
call procedure_name(@a); -- 调用存储过程,测一下
(2)在Mybatis中调用存储过程
dao.xml中的定义
<selectid="getId" parameterType="java.util.Map" statementType="CALLABLE" resultType="java.lang.Integer">
{call procedure_name(#{res, jdbcType=INTEGER, mode=OUT})}
</select>
dao.java中方法声明
Integer getId(@Param("res")Integer res);
service.java中调用
getId(null); // 因为传入的是返回参数,所以传null
本周总结了打印日志的规范,解决一个Mysql 1205问题,回顾Mysql索引、事件、存储过程的使用。小白在持续进步中...
希望本文对你也有一丢丢的帮助^^