方法内统计耗时的几种方法

1、自己写,计时开始结束使用System.currentTimeMillis()

long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(3L);
System.out.println(“method finish , use time ” +(System.currentTimeMillis() – start) + “ms”);

2、使用StopWatch类来实现

StopWatch类有不同的实现,spring有一个,guava也有一个,具体使用根据自己项目的依赖情况。真正使用上基本一致。

使用方式:

System.out.println(“SLAMonitorThread.main() start”);
StopWatch sw = new StopWatch();
sw.start();
TimeUnit.SECONDS.sleep(1L);
sw.split();
System.out.println(
“SLAMonitorThread.main() end. split:” + sw.getSplitTime() + “, ” + sw.toSplitString());
TimeUnit.SECONDS.sleep(1L);
sw.split();
System.out.println(
“SLAMonitorThread.main() end. split:” + sw.getSplitTime() + “, ” + sw.toSplitString());
TimeUnit.SECONDS.sleep(1L);
sw.split();
System.out.println(
“SLAMonitorThread.main() end. split:” + sw.getSplitTime() + “, ” + sw.toSplitString());
TimeUnit.SECONDS.sleep(1L);
System.out.println(“SLAMonitorThread.main() end. end:” + sw.getTime() + “, ” + sw.toString());
long start = System.currentTimeMillis();
TimeUnit.SECONDS.sleep(1L);

两种方式的优缺点:
方式1是不用任何外部依赖就可以实现的,但是缺点也同样明显,每个位置要打印一次的时候,都需要自己计算,并且还要重新拿到开始时间。
方式2则正好相反,需要guava的依赖,但是好处是提供了比较常用的接口支持,在不同的位置统计,并且计算一些东西,比如某一段耗时占比之类的,都可以友好支持。

具体使用时根据自己情况即可。

链家小区数据爬取

这次爬取链家数据时爬取的链家北京区域的所有小区数据。
区域比较少,手动去把每个区的列表页面罗列了出来(所有分区全家起来288个)
没有去处理当前总共多少页,之类的数据,而是把这个数据导入到一张表里,表字段设计了页数和当前爬取的页码,这样方便重试,也不做无用功跑已经跑过的数据。
由于一开始只需要小区的名称,地址,当前均价等,这些信息都有了,所以也没有去爬详情。
列表页面的URL格式为:https://bj.lianjia.com/xiaoqu/guangqumen/
默认为第一页,首页之后的格式均为在当前URL后面添加 pg+pageNo+/ 。
String realUrl = baseUrl + “pg” + pageNo + “/”;
通过写好的可以使用代理的HttpClient发起请求,列表页还是很容易请求成功的。
然后使用Jsoup解析请求到的网页字符串(据大神说,用XPath更高端更牛皮,不过我用着Jsoup还很顺手,就暂时不换了 – 主要以前用jquery习惯,所以找起来也方便)
import org.jsoup.Jsoup;
Document doc = Jsoup.parse(html);
打开网页调试工具,找到翻页位置的元素,发现:翻页的所有链接都在li[class=house-lst-page-box]元素内的超链接标签<a>上,并且最后一个超链接标签就是最大页码的标签,标签上的属性 data-page 就是页码,也就是最大页数,正好提取出来
代码:
Elements pageList = doc.select(“li[class=house-lst-page-box]”).select(“a”);
Element a = Safes.first(Lists.reverse(pageList));
Integer pageCount = Integer.valueOf(a.attr(“data-page”));
至此最大页数和当前页都已经拿到了。接着是收集小区信息。
小区列表中每个小区的信息就在一堆class=xiaoquListItem的li元素里,包含了列表里需要的所有小区的信息,先把每个小区的块都拿到。(当时爬的时候,下面填充用的标签还用的是a标签,写文章时候已经改成div和a标签交叉的了,感觉链家也不是没有做反爬虫,只是做的比较简单,下面呈上原来的老代码)
ElementsxiaoquList=doc.select(“li[class=xiaoquListItem]”).select(“a”).select(“a[class=PageLink]”)
接下来看源码就比较清楚了,里面分了三大块,左侧是图片,中间是小区名称、最近的成交信息、地址信息、代理人信息和标签信息,放在class=info的div里,每一块信息是一个div,右侧是价格、在售信息等内容,放在class=xiaoquListItemRight的div里。
从里面分别取出来这些信息,并放到对应对象里
List<LianJiaXiaoquEntity> entityList = Lists.newArrayList();
Safes.of(xiaoquList).forEach(xiaoqu -> {
    LianJiaXiaoquEntity entity = new LianJiaXiaoquEntity();
    Element title = Safes.first(xiaoqu.select(“div[class=title]”)).selectFirst(“a”);
    entity.setName(title.text());
    entity.setUrl(title.attr(“href”));
    Optional.ofNullable(Safes.first(xiaoqu.select(“div[positionInfo]”))).ifPresent(positionInfo -> {
        positionInfo.text();// \r\n&nbsp;\r\n&nbsp;/板楼/塔板结合/r/n&nbsp;2002年建成
        entity.setNameDetail(StringUtils.join(positionInfo.select(“a”).stream().map(Element::text).collect(Collectors.toList()), “-“) + entity.getName());
    });
    Optional.ofNullable(Safes.first(xiaoqu.select(“div[class=xiaoquListItemRight]”))).ifPresent(price -> {
        entity.setAveaPrice(Safes.first(price.select(“span”)).text());
        entity.setPriceTime(Safes.first(price.select(“div[class=priceDesc]”)).text());
    });
    entityList.add(entity);
这样,就把列表里一些小区的基本信息保存下来了。
但是我需要的不只是这些,主要还需要小区的坐标,这个在列表里没有。找小区坐标的过程也是一波三折,接下来说。

在mybatis中清空/全部删除表数据

有个定时任务一直以来直接跑的增量,基于跑了一段时间的定时任务的情况来看,发现有些情况没法通过增量来处理,比如被扫描表里有数据删除掉了;这种情况在我的定时任务里就没法定位出来对相应数据做处理。而在跑了定时任务之后如果再扫描结果表把已经被删除的数据筛选出来感觉是个很糙很糟糕的做法。
于是决定,在跑定时任务之前先清空整个结果表中的数据。思考之后有以下四种方式。
1、删除表重建表(drop table , create table )
虽然这种方式速度也很快,但是在业务工程里去做DDL操作感觉不太合适,而且操作相对复杂的多,放弃这种方案。
2、使用delete from table语句删除所有数据。
从逻辑上来说这个完全没有问题。但是delete语句虽然我们在执行的时候是清空所有数据,不需要加where条件,但是实际上数据库做的操作还是一条一条删除数据。在这个过程中数据库需要对每一次操作记录事务日志。数据量比较小的时候,这个操作也很快,当数据量比较大的时候,这个操作将会耗费比较长的时间。而且delete操作并不释放空间。
3、使用 delete table 语句删除所有数据。
这种方式也是删除表中所有数据,速度也快,唯一的一点,就是不释放空间。如果没有更好的方式,我将选择这种方式来实现我的设计。显然,有更好的方式。
4、使用truncate语法清空表。
查询过truncate语法和truncate与delete语法之间的区别就会知道,不同之处就在于,truncate会直接删掉相应的数据文件,这样不仅清空数据,而且释放了空间。另外就是,速度非常快。truncate的实现方式也是通过系统直接删除文件,这样的方式基本没有更快而且更节省时间的了。
经过思考,决定选择第四种。那么在mybatis中如何去执行truncate语句呢?查询资料,篇博客说使用@Select注解 ,然后执行的脚本字符串写truncate就可以,于是按照这个方式做。做完之后单元测试的时候发现,进入方法之后既没有执行结果,也没有执行错误的异常,很是尴尬啊。。。
后来想想,按说这里是一个对表(数据)的操作,按说不应该使用Select注解啊,这是个只读的。于是尝试,将注解改为使用@Update,果然成功!
因此得出结论:在mybatis中执行truncate语句需要按照如下方式:

@Update("TRUNCATE TABLE tmp_truncate_table")
void truncate();
ps.我这里使用的是mybatis的全局注解的方式,如果是使用配置文件,应该只需要在mapper文件中添加Update标签并将语句写到里面即可。