《高性能MySQL》读书笔记-第六章:查询性能优化

Acheron 按:
最近看了一些SQL优化的资料,这是《高性能MySQL》(第3版)(High Performance MySQL,Third Edition)第六章的读书笔记,整理于此。这本书可能更适合DBA或运维人员看,但第六章讲性能优化的部分,也适合我这样的后台开发人员。

一、为什么查询速度会慢

查询是一个任务,它有很多子任务构成,每个子任务都需要消耗一定的时间。若想优化查询,就要优化子任务,要么消除一些子任务,要么减少子任务的执行次数,要么减少子任务的执行时间。

查询的生命周期大致顺序:

从客户端,到服务器,然后在服务器上执行解析,生成执行计划,执行,然后将结构返回给客户端。

其中,执行是最重要的阶段,包括了大量为了检索数据到储存引擎的调用以及调用后的数据处理,包括排序,分组等。

在完成这些任务的时候,需要在不同的地方花费时间,包括网络IO,CPU计算,生成统计信息和执行计划 锁等待。

在调用底层数据引擎检索数据时,需要等待内存操作,CPU操作,和IO操作等待,这会产生大量的上下文切换和系统调用!

优化查询的目的就是减少和消除这些操作所花费的时间。

二、慢查询基础:优化数据访问

对于低效率的查询,一般使用下面的步骤来分析:

    1. 确认应用程序是否在检索大量超过需要的数据。这通常意味着访问了太多的行,但有时候也可能访问了太多的列。
    1. 确认Mysql服务器层是否在分析大量超过需要的数据行。

2.1 是否向数据库请求了不需要的数据

有些应用程序会请求超过实际需要的数据, 然后这些多余的数据会被应用程序丢弃。这会给Mysql服务器带来额外的负担,并增加网络开销(如果应用服务器和数据库不在同一台主机上,网络开销会显得很明显),另外也会消耗应用服务器的CPU和内存资源。

一些不好的操作:

  • 查询不需要的记录:只查询需要的记录,在相关查询后加上limit
  • 多表关联时返回全部列:多表关联时会从磁盘中读取全部列然后在内存中进行计算,非常低效。
  • 总是取出全部列:不要用select *,这会取出全部列,会让优化器无法完成索引覆盖扫描这类优化。
  • 重复查询相同的数据:当查询重复的数据时,可以考虑将数据缓存起来,需要是时候从缓存取。

2.2 Mysql是否在扫描额外的记录

对于Mysql,最简单的衡量查询开销的三个指标为:

  • 响应时间
  • 扫描的行数
  • 返回的行数

    这三个指标会记录到Mysql的慢日志中,所以检查慢日志记录是找出扫描行数过多的查询的好办法。

响应时间

响应时间是两个部分之后:服务时间和排队时间。

服务时间是指数据库处理这个查询真正花了多长时间。

排队时间是指服务器因为等待某些资源(I/O操作、行锁等)而没有真正执行查询的时间。

扫描的行数和返回的行数

理想情况下扫描的行数和返回的行数应该是相同的,但实际这种情况并不多,比如在做一个关联查询时,服务器心須要扫描多行才能生成结果集中的一行。

扫描的行数和访问类型

Explain语句中的type列反应了访问类型。访问类型从慢到快有:全表扫描(ALL),索引扫描,范围扫描,唯一索引查询,常数引用。

如果查询没有办法找到合适的访问类型,那最好的办法就是增加一个合适的索引。

Explain语句中的Extra如果是Using Where表示Mysql将通过where条件来筛选存储引擎返回的记录。

一般Mysql能够使用三种方式应用WHERE条件,从好到坏依次为:

  • 在索引中使用WHERE条件来过滤不匹配的记录,这是在储存引擎中完成的。
  • 使用索引覆盖扫描(explain语句的extra列出现了Using index)来返回记录。直接从索引中过滤不需要的记录并返回命中的结果,这是在Mysql服务器层完成的,但无须再回表查询记录。
  • 从数据表中返回数据,然后过滤不满足的条件(explain语句的extra列出现了Using where)。这在Mysql服务器层完成的,Mysql需要从数据表读取记录然后过滤。

如果发现查询需要扫描大量的数据但只返回少量的行,通常可以尝试下面的技巧去优化它:

  • 使用索引覆盖扫描,把所有用到的列都放到索引中,这样储存引擎无须回表获取对应行就可以返回结果了。
  • 改变库表结构,使用单独的汇总表,比如count()时。
  • 重写这个复杂的查询,让Mysql优化器能够以更优化的方式执行这个查询。

三、重构查询的方式

  • 1.一个复杂查询还是多个简单查询

有时候,将一个大的复杂查询分解为多个小查询往往效果会更好。Mysql从设计上让链接和断开链接都是轻量级的操作,在返回一个小的查询结果时很高效。

  • 2.切分查询

切分查询是对大查询”分而治之”,将大查询分为多个小查询,每个查询功能完全一样,只完成一小部分。

比如:定期的清楚大量数据时,如果一个大的语句一次性完成的话,则可能一次锁住很多数据,占满整个事物日志,耗尽系统资源,阻塞很多小的但重要的查询。这个时候可以分多次删除,每次删除一部分数据,删除一次暂停一会再接着做下一次删除,可以将服务器上一次性的压力分散到一个更长的时间段,性能会更好。

  • 3.分解关联查询

将一条关联多个表的sql语句通过应用程序拆分成多个操作单个表的语句。

分解关联查询方式的优势:

  • 让缓存效率更高,应用程序可以缓存了单表的结果对象,然后下次直接使用这个缓存的结果,这样就跳过了一次查询。
  • 将查询分解后,执行单个查询可以减少锁的竞争
  • 在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩展,为分布式做准备!
  • 查询本身效率也会有提升
  • 可以减少冗余记录的查询:在应用层做关联,意味着某条记录只需要查询一次,而在数据库做关联,则可以会重复返回一部分数据。
  • 这样分解相关于在应用中实现哈希索引,而不是使用数据库的嵌套循环关联

四、查询执行的基础

当向mysql发送一个请求的时候,mysql做了什么:

  • 1.客户端发送一条查询给服务器
  • 2.服务器先查询查询缓存,若缓存命中,则立刻返回,否则进入下一阶段
  • 3.服务器端进行SQL解析,预处理,再由优化器生成对应的执行计划
  • 4.Mysql根据优化器生成的执行计划,调用储存引擎的API执行查询
  • 5.将结果返回给客户端

4.1 Mysql客户端/服务器通信协议

Mysql客户端和服务器之间的通信协议是”半双工”的,这意味着,在任何一个时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据。这种简单的协议限制了Mysql,一个明显的限制是没办法进行流量控制,一旦一端开始发送数据,另一端中能接受完整的消息才能响应它。

查询状态:对于一个Mysql链接,或者一个线程,任何时候都有一个状态,该状态表示了Mysql当前正在做什么。查看当前状态可以用命令:

show full processlist

Mysql的状态值有:

  • Sleep:线程正在等待客户端发送新的请求
  • Query:线程正在执行查询,或者正在将结果发送到客户端
  • Locked:在Mysql服务器层,该线程正在等待表锁。在储存引擎级别实现的锁,例如InnoDB的行锁,并不会体现在线程状态中。对于MyISAM来说这是一个典型的状态,因为它没有行锁。
  • Analyzing and statistics:线程正在收集储存引擎的统计信息,并生成查询的执行计划。
  • Copying to tmp table [on disk]:线程正在执行查询,并且将结果集都复制到一个临时表中,这种状态要么是在执行GROUP BY操作,要么是文件排序操作,或者是UNION操作,[on disk]标记表示Mysql正在将一个内存临时表存放到磁盘上。
  • Sorting result:线程正在对结果集进行排序
  • Sending data:这表示多种情况,线程可能在多个状态之间传送数据,或者生成结果集,或者在向客户端返回数据。

4.2 查询缓存:

  • 在解析一个查询语句之前,要检查缓存是否命中。
  • 这个检查是通过一个对大小写敏感的哈希查找实现的
  • 若缓存命中了,那么在返回查询结果之前Mysql会检查一次用户权限。权限没问题,则直接从缓存中拿到结果返回客户端。

4.3 查询优化处理

查询生命周期的下一步是将一个SQL转换成一个执行计划,这个阶段包括多个子阶段:解析SQL,预处理,优化SQL执行计划。

语法解析器和预处理:Mysql通过关键字将SQL语句进行解析,并生成一颗对应的”解析树”,Mysql解析器将使用Mysql的语法规则验证和解析查询。比如:它将验证是否使用了错误的关键字,或者使用关键字顺序是否正确,或者验证引号能否正确匹配。

预处理器则根据一些Mysql规则进一步检查解析树是否合法,例如,这里将检查数据表和数据是否存在,还会解析名字和别名,看看他们是否有歧义。

下一步预处理器会验证权限。

4.4 查询优化器:

一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就好找到其中最好的执行计划。

Mysql使用基于成本的优化器,它尝试预测一下查询使用某种计划时的成本,并选择其中最小的一个。

Mysql能够处理的优化类型:

  • 重新定义关联表的顺序
  • 将外连接转化为内连接
  • 使用等价变换规则,如(5=5 and a>5)改为a>5
  • 优化COUNT() MIN() MAX():如要找某列的最小值,只需查B-Tree中最左端的记录
  • 预估并转化为常数表达式
  • 覆盖索引扫描
  • 子查询优化
  • 提前终止查询
  • 等值传播
  • 列表IN()的比较:mysql将in()列表中的数据先进行排序,然后通过二分查找的方式然定列表中的值是否满足条件

Mysql如何执行关联查询:在Mysql中,不仅仅是两个表匹配叫关联,其实单表查询,子查询都叫关联。

对于UNION查询,原理是Mysql先将一系列的单个查询放到一个临时表中,然后再重新读出临时表的数据来完成UNION查询。在Mysql概念中,每个查询都是一次关联,所以读取临时表的也是一次关联!

  • 执行计划:

和其他关系数据库不同,Mysql不会生成查询字节码来执行查询。Mysql生成查询的一颗指令树,然后通过储存引擎执行完成这颗指令树并返回结果。如果对某个查询执行explain extended后,再执行show warnings,就可以看到重构出的查询。

  • 排序优化:

排序是一个成本很高的操作,从性能考虑,应尽可能避免排序或尽可能避免对大量数据排序。

  • mysql两种排序算法:
    • 两次传输排序(旧版本使用):先读取行指针和需要排序的字段,进行排序,然后根据排序结果读取所需要的数据行。这需要进行两次数据传输,并且第二次是随机IO,所以成本很高,不过好处是在排序时使用的储存很少!
    • 单次传输排序(新版本使用):先查询所需要排序的列,然后根据给定列进行排序。

五、优化案例

5.1 优化count()查询

count()两种作用:

  • 统计某个列值的数量(不统计null)
  • 统计结果集的行数,用通配符count(*)

案例1:

如何快速查找到所有id>5的城市,首先想到这样查询:

select count(*) from City where id > 5;

通过show status的结果可以看到这个查询要扫描4000行数据,如果将条件反转一下,先查找id<=5的数,然后总数减去,可以将扫描行数减少到5行,大大提高效率,这是因为在查询优化阶段会将其中的子查询直接当作一个常数来处理:

select (select count(*) from City)-count(*) from City where id <=5;

案例2:

问题:如何在同一个查询中统计同一列的不同值的数量,以减少查询的语句量。

解决,用sum():

select sum(if(color='blue',1,0)) as blue,sum(if(color='red',1,0)) as red from items;

或者使用count(),只需要将满足条件设置真,不满足条件设置为null:

select count(color='blue' or null) as blue,count(color='red' or null) as red from items;

5.2 优化关联查询

  • 确保on或者using子句中列上有索引
  • 确保group by或order by表达式中只涉及到一个表中的列,这样mysql才有可能使用索引来优化这个过程

5.3 group by

如果没有通过order by子句显式地指定排序列,当查询使用group by子句的时候,结果集会自动按照分组的字段进行排序,如果不关心结果集顺序,则可以使用order by null,让mysql不再进行文件排序。

5.4 优化limit分页

问题:

当偏移量非常大的时候,例如可以是limit 10000,20,这里mysql需要查询10000条记录然后只返回最后20条,前面10000条将被抛弃,翻到很后面,性能会很差。

延迟查询:

select film_id,desc from sakila.film order by title limit 50,5;

如果这个表非常大,改成延迟查询将大大提高效率,它让mysql扫描尽可能少的页面,获取需要访问的记录后再根据关联列回原表查询需要的所有列:

select film.film_id,film.desc
from sakila.film
    innser join(
        select film_id from sakila.film order by title limit 50,5
    ) as lim using(film_id);

配置管理平台Apollo搭建指南

Acheron注:

Apollo 的官方文档写得非常友好全面,官方地址:https://github.com/ctripcorp/apollo/wiki

这里只是记录一下我在服务器上搭建的过程。

一、环境要求

  • Java: 1.8+
    • 检查:java -version
  • MySql:5.6.5+
    • 检查:SHOW VARIABLES WHERE Variable_name = 'version';

二、部署步骤

2.1 导入数据库生成脚本

官方提供的数据库脚本有两个,在scripts/sql下,apolloportaldb.sqlapolloconfigdb.sql

// 创建:ApolloPortalDB
source /your_local_path/sql/apolloportaldb.sql
// 创建:ApolloConfigDB
source /your_local_path/sql/apolloconfigdb.sql
// 检查是否导入成功:
select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limit 1;
select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;

2.2 Apollo自身的一些配置

2.2.1 配置ApolloPortalDB.ServerConfig

  • 1.apollo.portal.envs – 可支持的环境列表:

默认值是dev,多个以逗号分隔即可(大小写不敏感),如:

DEV,FAT,UAT,PRO

注意:只在数据库添加环境是不起作用的,需要配合修改scripts/build.sh,添加新增环境对应的meta server地址。

  • organizations – 部门列表:
[{"orgId":"TEST1","orgName":"样例部门1"},{"orgId":"TEST2","orgName":"样例部门2"}]
  • wiki.address: portal上“帮助”链接的地址,默认是Apollo github的wiki首页。

####2.2.2 配置ApolloConfigDB.ServerConfig

  • 1.eureka.service.url – Eureka服务Url,如有多个,用逗号分隔(注意不要忘了/eureka/后缀):
http://1.1.1.1:8080/eureka/,http://2.2.2.2:8080/eureka/

2.3 配置数据库连接信息

  • vim scripts/build.sh
#apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=用户名
apollo_config_db_password=密码(如果没有密码,留空即可)

# apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=用户名
apollo_portal_db_password=密码(如果没有密码,留空即可)

2.4 配置各环境meta service地址

  • vim scripts/build.sh:修改各环境meta service服务地址。 如果某个环境不需要,也可以直接删除对应的配置项
dev_meta=http://localhost:8080
fat_meta=http://localhost:8080
uat_meta=http://localhost:8080
pro_meta=http://localhost:8080

2.5 执行编译、打包

./build.sh 

2.6 部署运行

2.6.1 部署apollo-configservice

apollo-configservice/target/目录下的apollo-configservice-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

cd apollo-configservice/target/
scp apollo-configservice-x.x.x-github.zip 209:/opt/apollo/config
ssh 209
cd /opt/apollo/config
unzip apollo-configservice-x.x.x-github.zip
运行:./scripts/startup.sh
停止:./scripts/shutdown.sh

注:如要调整服务的监听端口,可以修改startup.sh中的SERVER_PORT。另外apollo-configservice同时承担meta server职责,如果要修改端口,注意要同时修改scripts/build.sh中的meta server url信息以及ApolloConfigDB.ServerConfig表中的eureka.service.url配置项。

2.6.2 部署apollo-adminservice

同上:

apollo-adminservice/target/目录下的apollo-adminservice-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

注:如要调整服务的监听端口,可以修改startup.sh中的SERVER_PORT

2.6.3 部署apollo-portal

同上:

apollo-portal/target/目录下的apollo-portal-x.x.x-github.zip上传到服务器上,解压后执行scripts/startup.sh即可。如需停止服务,执行scripts/shutdown.sh.

apollo-portal的默认端口是8080,和apollo-configservice一致,所以如果需要在一台机器上同时启动apollo-portal和apollo-configservice的话,需要修改apollo-portal的端口。直接修改startup.sh中的SERVER_PORT即可,如SERVER_PORT=8070

三、Java客户端使用

3.1 配置Appid

classpath:/META-INF/app.properties文件:

app.id=YOUR-APP-ID

3.2 配置Environment

  • 对于Mac/Linux,文件位置为/opt/settings/server.properties
  • 对于Windows,文件位置为C:\opt\settings\server.properties

保证settings目录文件权限:chmod 777 /opt/settings

文件内容形如:

env=DEV

3.3 配置本地缓存路径

本地缓存路径位于以下路径,所以请确保/opt/dataC:\opt\data\目录存在,且应用有读写权限。

  • Mac/Linux: /opt/data/{appId}/config-cache
  • Windows: C:\opt\data{appId}\config-cache

保证data目录文件权限:chmod 777 /opt/data

3.4 配置日志地址

保证logs目录文件权限:chmod 777 /opt/logs

3.5 Maven Dependency

<dependency>
        <groupId>com.ctrip.framework.apollo</groupId>
        <artifactId>apollo-client</artifactId>
        <version>0.7.0</version>
 </dependency>

3.6 使用

@Configuration
@EnableApolloConfig
public class AppConfig {
  
}
@ApolloConfig
private Config config;

@Test
public void testApollo(){
	String name = config.getProperty("name", "hello");
    assertEquals(name,"Acheron");
}

使用canal实现redis缓存刷新

背景

假设现有项目P1,是直连数据库(mysql)的。为了提高性能,现在要加上缓存(redis),但是另有项目P2,也对数据库有操作,如何保证当P1加上redis后,P2对数据库修改后,P1从redis中获取的数据是最新的。有两种方案:

  • 一种是P1的开发人员和P2开发人员协调,当P2对mysql有修改、增加等操作,必须清除P1使用到的相关缓存。当P1读redis时,发现缓存失效,再读mysql,获取最新数据。
  • 一种是利用阿里巴巴的canal,获得mysql-binlog,根据mysql-binlog清除相关的缓存。这种方法的好处是不需要修改原来的程序。

使用到的技术

部署配置canal

canal的部署可以参考官方文档:https://github.com/alibaba/canal/wiki/QuickStart 配置的时候要注意:

  • conf/canal.properties是canal本身的配置文件,基本上默认即可
  • conf/example/instance.properties:应用的配置参数,一般需要修改其中的数据库连接配置

使用

1.启动canal:

sh bin/startup.sh

canal 的官方例子: https://github.com/alibaba/canal/wiki/ClientExample

利用canal解析mysql-binlog,能够获取到执行的sql,修改的表数据等信息,我这里获取修改的表,根据表名去清除掉使用到这个表的相关缓存,所以这里有一个对应关系:表名--缓存key

这里表名和缓存key的对应关系,为了方便,我将其存在一个属性文件中:cache.properties,并提供一个web界面来维护这个文件。

2.配置cache.properties

在计算机上任一位置建一文件:cache.properties,在项目的application.properties中有一属性:cache.properties.path,指向cache.properties文件的位置:

cache.properties.path=/Users/acheron/cache.properties

2.配置redis

util/RedisUtil.java中配置redis相关信息,主要是以下三项:

// Redis的地址
private static String HOST = "127.0.0.1";
// Redis的端口号
private static int PORT = 6379;
// 访问密码
private static String PASS = null;

3.添加表-缓存key配置

运行项目,浏览器访问,添加一条记录,数据将会保存到cache.properties

table_name1=herohuang'skey
table_name2=user.name.haha
table_name3=hahaha,com.hero,com.herohuang*
table_name4=com.hahaha,user.name.key,test

注意cachekey的配置:

  • 如果有多个,则用英文逗号隔开
  • 如果要删除所有以com.herohuang开头的缓存,则可以com.herohuang*

源码地址:https://github.com/Ac-heron/hexo-canal

界面展示

canal1canal2

分布式跟踪工具Pinpoint技术入门

本文初衷

因为工作需要,这周研究了一下APM(Application Performance Management)相关技术,看了些许资料,对比了各种技术的特点,最终选择Pinpoint作为性能监控工具,本文的主要目的是记录本人部署Pinpoint的过程。

Pinpoint是什么

简单的说,Pinpoint是一款对Java编写的大规模分布式系统的APM工具,有些人也喜欢称呼这类工具为调用链系统分布式跟踪系统。我们知道,前端向后台发起一个查询请求,后台服务可能要调用多个服务,每个服务可能又会调用其它服务,最终将结果返回,汇总到页面上。如果某个环节发生异常,工程师很难准确定位这个问题到底是由哪个服务调用造成的,Pinpoint等相关工具的作用就是追踪每个请求的完整调用链路,收集调用链路上每个服务的性能数据,方便工程师能够快速定位问题。

同类工具

为什么要用Pinpoint

最重要的原因,对代码的零侵入,运用JavaAgent字节码增强技术,只需要加启动参数即可。

Pinpoint架构图

pinpoint-architecture

(图片出处:官网)架构说明:

  • Pinpoint-Collector:收集各种性能数据
  • Pinpoint-Agent:和自己运行的应用关联起来的探针
  • Pinpoint-Web:将收集到的数据显示成WEB网页形式
  • HBase Storage:收集到的数据存到HBase中

开始部署

前提条件

  • maven 3.2.x+
  • git
  • java 6+

0. 开始

1. 部署Hbase

Pinpoint以Hbase作为数据的存储。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home
  • 编辑hbase-site.xml:
<configuration>
    <property>
        <name>hbase.rootdir</name>
        <value>file:///Users/acheron/Tmp/hbase</value>
    </property>
    <property>
        <name>hbase.zookeeper.property.dataDir</name>
        <value>/Users/acheron/Tmp/zookeeper</value>
    </property>
    <property>
        <name>hbase.zookeeper.property.clientPort</name>
        <value>2182</value>
  </property>
</configuration>

这样配置是本地单实例模式启动,上面配置分别表示:数据存放地点、zookeeper数据存放地点、zookeeper端口号(默认2181)。当然这个文件可以不配置,那么会采取默认值

启动hbase的时候,hbase用得是自带的zk,在hbase的配置里可见 export HBASE_MANAGES_ZK=true;

  • 进入hbase/bin目录启动hbase:./start-hbase.sh
  • jps命令查看Hbase是否启动成功,如果启动成功的会看到”HMaster”的进程
  • 初始化pinpoint需要的表:./hbase shell hbase-create.hbase (这里的hbase-create.hbase在源码pinpoint/hbase/scripts/hbase-create.hbase)
  • 访问页面测试是否成功:http://localhost:16010/master-status ,如果成功在页面的tables标签下能看到导入的表。
  • 也可以用命令来查看是否导入表成功,进入hbase,输入”status ‘detailed'”可以查看初始化的表
./hbase shell
status 'detailed'

2. 部署Pinpoint-collector

  • 将pinpoint-collector-1.6.0-SNAPSHOT.war放到tomcat-collector-8086/webapps/下,并重命名为ROOT.war
  • 启动tomcat,配置ROOT/WEB-INF/classes/hbase.properties:
hbase.client.host=localhost
hbase.client.port=2181

指向zookeeper的地址和端口,如果是本机,端口默认,则这里不需更改。

  • 配置上面后,重启tomcat(端口8086

3. 部署Pinpoint-web

Pinpoint-web的配置和Pinpoint-collector相似:

  • 将pinpoint-web-1.6.0-SNAPSHOT.war放到tomcat-web-8085/webapps/下,并重命名为ROOT.war
  • 启动tomcat,配置ROOT/WEB-INF/classes/hbase.properties:
hbase.client.host=localhost
hbase.client.port=2181

指向zookeeper的地址和端口,如果是本机,端口默认,则这里不需更改。

  • 配置上面后,重启tomcat(端口8085

4. 部署Pinpoint-agent

  • 新建目录:mkdir pp-agent
  • 将pinpoint-agent-1.6.0-SNAPSHOT.tar.gz 拷贝到pp-agent目录并解压
  • 配置pinpoint.config:profiler.collector.ip=127.0.0.1这是指pinpoint-collector的地址,如果是同一服务器,则不用修改。其它默认。
  • 安装pinpoint-collector启动后,自动就开启了9994,9995,9996的端口了,这里默认即可。如果有端口需求,要去pinpoint-collector的配置文件(“pinpoint-collector/webapps/ROOT/WEB-INF/classes/pinpoint-collector.properties”)中,修改这些端口。

5. 部署TestApp

  • 官方提供了一个测试用的应用,pinpoint-quickstart-testapp-null.war,将其重命名为ROOT.war,部署到tomcat-testapp-8084
  • 修改此tomat的/bin/catalina.sh,在106行左右加入启动参数:
CATALINA_OPTS="$CATALINA_OPTS -javaagent:/Users/acheron/pinpoint/pp-agent/pinpoint-bootstrap-1.6.0.jar"
CATALINA_OPTS="$CATALINA_OPTS -Dpinpoint.agentId=myapp"
CATALINA_OPTS="$CATALINA_OPTS -Dpinpoint.applicationName=MyTestPP"

第一行:pinpoint-bootstrap-1.6.0.jar的位置

第二行:这里的agentId必须唯一,标志一个jvm。

第三行:applicationName表示同一种应用:同一个应用的不同实例应该使用不同的agentId,相同的applicationName。

6. springboot包部署

如果是jar包部署,直接在启动命令加启动参数:

nohup java -javaagent:/Users/acheron/pinpoint/pp-agent/pinpoint-bootstrap-1.6.0.jar -Dpinpoint.agentId=acheron-consumer -Dpinpoint.applicationName=acheron-consumer -jar myapp.jar &

7.效果演示

hbase和各应用都启动成功之后,访问Pinpoint-web,效果:

pinpoint-page

结束语

各大APM工具,几乎都是根据google这篇经典的Dapper论文而来,一定要读一读。这里是它的源文地址:https://research.google.com/pubs/pub36356.html,感谢这位同学的翻译:http://bigbully.github.io/Dapper-translation/

参考文章

SSH免密码及别名登录设置

问题

众所周知,在用SSH远程登录linux服务器时,可以用下面的命令:

ssh root@192.168.1.209

但麻烦的是,每次都要输入服务器地址和密码,有没有方法不用每次都输入密码,甚至可以给服务器设置一个容易记住的别名,例如可以ssh 209直接登录。

解决

使用”公钥登录”的方法可以解决这个问题。“公钥登录”的原理很简单,用户将自己的公钥储存在远程服务器上。登录的时候,远程服务器会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录。

1. 免密登录

在用户的~/.ssh目录下有文件id_rsaid_rsa.pub,前者表示本机的私钥,后者表示本机的公钥。如果没有这两个文件,则可以运行下面的命令生成:

$ ssh-keygen

运行上面的命令以后,系统会出现一系列提示,一路按回车即可,运行结束在.ssh目录会生成上述两个文件。

然后用ssh-copy-idscp命令将生成的公钥id_rsa.pub传到远程机器上:

$ ssh-copy-id root@192.168.1.209

因为公钥只是一串字符器,所以可以不用上面这个命令,可以直接拷贝本机的公钥里的字符串,附加到远程主机的~/.ssh/authorized_keys里,这样就可以免密登录了。

2. 别名登录

按上面第一步设置后,可以用ssh root@192.168.1.209不用输入密码就可以直接登录了,现在设置将root@192.168.1.209209代替,直接用ssh 209就可以登录。

在本机的 ~/.ssh目录下创建 config文件:

$ vim ~/.ssh/config

输入以下内容:

Host 209
User root
HostName 192.168.1.209
IdentityFile ~/.ssh/id_rsa

其中内容说明如下(端口默认为22):

  • Host 209 :自己定的别名
  • User root :远程主机的用户名
  • HostName 192.168.1.209:远程主机的地址
  • IdentityFile ~/.ssh/id_rsa:本地私钥

当前如果要登录多个主机,可以设置多个:

Host herohuang
HostName herohuang.com
User acheron
IdentityFile ~/.ssh/id_rsa_a
Host github
HostName github.com
User github
IdentityFile ~/.ssh/id_rsa_b