分类目录归档:mysql

MySQL事务隔离级别

MySQL的InnoDB存储引擎实现了事务,行级锁,优化主键查询,支持外键等等。事务遵循ACID特征:

  • A:原子性(atomicity),一个事务要么完全成功,要么完全失败
  • C:一致性(consistency),一个事务对数据的修改必须符合数据库预定义的约束,比如字段类型、外键约束等等
  • I:隔离性(isolation),多个事务同时进行时,事务之间数据的读写的隔离
  • D:持久性(Durability),事务提交成功后,对数据的修改即永久保存的,不会因为系统故障丢失

MySQL默认开启autocommit,除非碰到start transaction/commit/rollback等主动事务管理。事务保证了对于写的先后顺序问题,即A事务先开始变更,B事务的变更必须等A事务完成。此外事务也对读提供不容程度的隔离,与隔离级别有关,包括不同的等级:读未提交(Read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。串行化为最高隔离级别,在这个隔离级别下面,读写都是顺序的,不会出现这些问题,但效率低下,MySQL默认隔离级别为可重复读。在其他隔离级别下面可能会出现不同的问题

隔离级别 脏读 不可重复读 幻读
读未提交(Read Uncommitted) yes yes yes
读已提交(Read Committed) no yes yes
可重复读(Repeatable Read) no no yes
串行化(Searializable) no no no

假设有一个表test结构为

CREATE TABLE `test` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `total` int(11) DEFAULT 0,
  `status` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `test` (`id`, `total`, `status`) VALUES (1, 0, 1);

脏读是指A事务读到B事务未提交的事务,B事务可能取消更改,那么A事务读取的数据不可靠

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 update total set total=100 where id=1;
3  select total from test where id=1;
4 rollback;
5  commit;

结果

隔离级别 事务A 事务B
读未提交(Read Uncommitted) 由于可以读取到未提交的数据,total值为100 数据未更新
读已提交(Read Committed) 由于A事务读取数据时,事务B尚未提交,total值为原始数据 数据未更新
可重复读(repeatable read) 由于A事务开始前total值未发生改变,total值为原始数据 数据未更新

不可重复读是指A事务内,读取的记录数据不恒定,两次读取同一记录的值不一样,

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select total from test where id=1;
3 update test set total=100 where id=1;
4 commit;
5 select total from test where id=1;
6  commit;

结果

隔离级别 事务A 事务B
读未提交(Read Uncommitted) 由于读取数据时不管是否提交,两次读取的total值不一样 数据已更新
读已提交(Read Committed) 由于A事务读取数据时,事务B已提交,两次读取的total值不一样 数据已更新
可重复读(repeatable read) 由于A事务开始前total值未发生改变,total值为原始数据,两次读取的total值一样 数据已更新

幻读是指A事务内读取的记录跟实际更改的记录不一致,比如查询表内所有符合条件的记录,并使结果加100

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select * from test;
3 insert into `test` (`id`, `total`, `status`) values (2,0, 1);
4 commit;
5 update test set total=total+100;
6 select * from test;
7  commit;

结果

隔离级别 事务A 事务B
读未提交(Read Uncommitted) 由于事务A开始时id为2的记录尚未存在,于是更新所有数据;但由于事务B插入数据,于是事务A也更新事务B插入的数据;A事务第二次读取数据时,读出了事务B的数据   数据插入成功,但随后被事务A更新
读已提交(Read Committed) 由于A事务第一次读取数据时,id为2的记录尚未存在,于是更新所有数据;但由于事务B插入数据,于是事务A也更新事务B插入的数据;A事务第二次读取数据时,读出了事务B的数据 数据插入成功,但随后被事务A更新
可重复读(repeatable read) 由于A事务开始前total值未发生改变,id为2的记录尚未存在,于是更新所有数据;但由于事务B插入数据,于是事务A也更新事务B插入的数据;A事务第二次读取数据时,并没有读到事务B的数据 数据插入成功,但随后被事务A更新

或者当数据库里面没有记录时则增加

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select * from test;
3 insert into `test` (`id`, `total`, `status`) values (3, 0, 1);
4 commit;
5 insert into `test` (`id`, `total`, `status`) values (3, 0, 1);
6  select * from test;
7  commit;

结果

隔离级别 事务A 事务B
读未提交(Read Uncommitted) 由于事务A读取记录时,id为3的记录不存在,于是插入数据;但由于事务B插入数据,于是事务A插入失败;A事务第二次读取时,读取到id为3的数据 数据插入成功
读已提交(Read Committed) 由于A事务读取数据时,id为3的记录不存在,于是插入数据;但由于事务B插入数据,于是事务A插入失败;A事务第二次读取时,读取到id为3的数据 数据插入成功
可重复读(repeatable read) 由于A事务开始前id为3的记录尚未存在,于是插入数据;但由于事务B插入数据,于是事务A插入失败;A事务第二次读取时,未读取到id为3的数据 数据插入成功

可重复读与读已提交是互斥的,区别在于是否可以读取到已提交的变更,譬如Oracle的隔离级别为读已提交,可以通过 SET TRANSACTION ISOLATION LEVEL READ COMMITTED更改。

除了串行化,其他隔离级别对读是不加锁的,这可能会造成一种情况:事务A读取了数据然后做其他事情了(进入循环或耗时计算),事务B也读取了同一行数据并先提交变更,此后事务A也提交变更但没有重新获取最新值,此时事务B的变更丢失(被覆盖),即更新丢失

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select id,total,status from test where id=1;  select status from test where id=1;
3 update test set status=2 where id=1;
4 commit;
5 update test set total=200, status=1 where id=1;
6  select * from test where id=1;
7  commit;

结果

隔离级别 事务A 事务B
读未提交(Read Uncommitted) 由于事务A第一次读取记录时status为1,A事务更新时,事务B已提交,A事务提交变更重新改写status为1。重新读取记录最新数据可以解决 更新丢失
读已提交(Read Committed) 由于A事务第一次读取记录时status为1,A事务更新时,事务B已提交,A事务提交变更重新改写status为1。重新读取记录最新数据可以解决 更新丢失
可重复读(repeatable read) 由于A事务开始前id为1的status为1,A事务提交变更覆盖B事务的变更,重新改写status为1。重新读取记录最新数据不能解决。 更新丢失

在隔离级别为串行化的情况下可解决更新丢失,因为它对读也加锁,另一事务的读操作必须等待当前事务完成。可以在查询里面使用SELECT…FOR UPDATE为当前事务记录加锁,这样其他事务读取该记录也必须等待,即拿到最新值。其他事务也可以使用NOWAIT快速失败或者SKIP LOCKED跳过对该记录的查询。

SELECT…FOR UPDATE申请的锁为排他锁,锁有不同类型,比如行锁,区间锁,next-key锁等。此外SELECT…FOR SHARE可以加读锁(共享锁),即本次事务获得锁了,其他事务可以读,但是不能更新;如果其他事务先获得锁并开始更新,则本次事务等待。

对于更新丢失问题,可以将表拆小,这样更新的操作更加细致,比如status和total分开,减少不同进程更新数据的冲突;更新时带上更多条件,比如status=1条件,失败则返回或重试;更前再次刷新数据,比较cache中的版本;其他手段加锁等等。

SELECT…FOR UPDATE对记录加锁,要注意不同事务之间避免互相等待,造成死锁,比如不同先后为不同记录申请锁

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select * from test where id=1 for update;
3 select * from test where id=2 for update;
4 select * from test where id=2 for update;
5 select * from test where id=1 for update;
6  deadlock; deadlock;

这样会造成事务A、B互相等待对方事务完成(释放)超时。或者A事务对记录加读锁后等待写锁,而B事务获得写锁了,等待读锁,也会造成死锁

操作顺序 事务A 事务B
1 start transaction; start transaction;
2 select * from test where id=1 for update;
3 insert into `test` (`id`, `total`, `status`) values (4, 0, 1);
4 insert into `test` (`id`, `total`, `status`) values (5, 0, 1);
5 select * from test where id=1 for update;
6  deadlock; deadlock;

这个问题应该通过合理的程序设计,先避免不同程序对同一记录竞争加锁;分解大事务为小事务,加速事务提交;减少不必要的锁的申请;当需要排他锁时不应先申请共享锁;降级事务隔离等级为读已提交等等。

参考资料:
深入学习MySQL事务:ACID特性的实现原理
解析MySQL事务隔离级别
MySQL 四种事务隔离级的说明
MySQL 锁机制
【BAT面试题系列】面试官:你了解乐观锁和悲观锁吗?

Let’s encrypt

最近域名主机双双到期了,原来的服务商建议域名迁出,于是转移到了Godaddy,过程很顺利。主机迁到了linode,一个是因为它便宜,另一个是因为想给自己的网站加个SSL证书。
首先是服务器环境Apache, PHP, MaraiDB(MySQL)的配置。 linode 创建主机很简单,点点就好了,然后可以去启动机器,设置SSH访问。

yum update
yum install httpd php php-cli php-mbstring php-pdo php-mysql php-gd php-tidy

启动Apache

systemctl start httpd.service
systemctl enable httpd.service

然后直接访问你的服务器ip,可以看到默认的欢迎界面

ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
curl 1.139.x.x

CentOS 7目前默认不提供Mysql Server,而是Mariadb。MySQL的命令行仍然可以使用并兼容, PHP仍然可以使用PDO及MySQL扩展访问它。

yum install mariadb-server mariadb
systemctl start mariadb

然后设置管理员密码及安全设置

mysql_secure_installation

开机启动Mariadb服务

systemctl enable mariadb.service

可以在/var/www/html目录下创建一个测试脚本来验证安装情况

<?php
phpinfo();

WordPress则是从原本的数据库全部导出,文件全部打包回来。
在Linode上创建对应的数据库,用户及导入脚本

MariaDB [(none)]> CREATE database courages_wordpress;

MariaDB [(none)]> CREATE USER courages_wp IDENTIFIED BY '*************';

MariaDB [(none)]> grant all privileges on courages_wordpress.* to courages_wp@localhost identified by '*************';

导入数据库

mysql -uroot -p courages_wordpress < /tmp/wordpress.sql

将文件解压并复制到/var/www/html目录

tar -xzvf backup.tar.gz
cp -R backup/public_html/* /var/www/html/*
chown -R apache:apache /var/www/html

更改Apache设置AllowOverride 为all,以便支持WordPress的链接重定向。

vim /etc/httpd/conf/httpd.conf

<Directory "/var/www/html">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    AllowOverride All

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

重启Apache

systemctl restart httpd.service

在linode的DNS manager那里新增一个新的domain,在服务器列表里面选中对应的服务器就可以了,然后就可以看到对应的域名解析信息。
域名转移会要求一个key,从原注册商那里解锁并获得,在Godday输入Key后,它会发邮件与你确认,然后将DNS域名服务器改为linode的域名服务器就好了。

Let’s Encrypt提供免费90天的SSL证书,如果证书到期了就需要再次更新下。如果你有shell权限,它推荐使用Cerbot来安装和更新证书。CentOS 7 + Apache的安装非常简单。首先安装EPEL源,要不然找不到对应的安装包

yum install epel-release
yum install certbot-apache
certbot --authenticator webroot --installer apache

设置一下域名,网站目录及域名重定向

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator webroot, Installer apache
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): <[email protected]>
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v01.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: Y
Starting new HTTPS connection (1): supporters.eff.org
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated)  (Enter 'c' to cancel): courages.us
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for courages.us
Input the webroot for courages.us: (Enter 'c' to cancel): /var/www/html
Waiting for verification...
Cleaning up challenges

We were unable to find a vhost with a ServerName or Address of courages.us.
Which virtual host would you like to choose?
(note: conf files with multiple vhosts are not yet supported)
-------------------------------------------------------------------------------
1: ssl.conf                       |                       | HTTPS | Enabled
-------------------------------------------------------------------------------
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1
Deploying Certificate for courages.us to VirtualHost /etc/httpd/conf.d/ssl.conf

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Created redirect file: le-redirect-courages.us.conf
Rollback checkpoint is empty (no changes made?)

-------------------------------------------------------------------------------
Congratulations! You have successfully enabled https://courages.us

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=courages.us
-------------------------------------------------------------------------------

在浏览器访问一下域名即有小绿钥匙。可以在/etc/httpd/conf.d/ssl.conf查看相应的SSL证书配置

<VirtualHost _default_:443>
ServerName courages.us
SSLCertificateFile /etc/letsencrypt/live/courages.us/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/courages.us/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateChainFile /etc/letsencrypt/live/courages.us/chain.pem
</VirtualHost>

由于证书在90天后即将失效,可以加入crontab自动更新

certbot renew --dry-run
crontab -e

0 0,12 * * * python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew 

另外一种更新证书的方法是使用acme.sh,不依赖python,同样支持DNS验证,并且还可以获取其他CA颁发的证书,比如ZeroSSL
安装acme。sh脚本非常简单

➜ ~ curl https://get.acme.sh | sh -s [email protected]
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1032 0 1032 0 0 608 0 --:--:-- 0:00:01 --:--:-- 608
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 216k 100 216k 0 0 104k 0 0:00:02 0:00:02 --:--:-- 104k
[2023年 04月 07日 星期五 22:11:22 CST] Installing from online archive.
[2023年 04月 07日 星期五 22:11:22 CST] Downloading https://github.com/acmesh-official/acme.sh/archive/master.tar.gz
[2023年 04月 07日 星期五 22:11:25 CST] Extracting master.tar.gz
[2023年 04月 07日 星期五 22:11:26 CST] It is recommended to install socat first.
[2023年 04月 07日 星期五 22:11:26 CST] We use socat for standalone server if you use standalone mode.
[2023年 04月 07日 星期五 22:11:26 CST] If you don't use standalone mode, just ignore this warning.
[2023年 04月 07日 星期五 22:11:26 CST] Installing to /root/.acme.sh
[2023年 04月 07日 星期五 22:11:26 CST] Installed to /root/.acme.sh/acme.sh
[2023年 04月 07日 星期五 22:11:26 CST] Installing alias to '/root/.zshrc'
[2023年 04月 07日 星期五 22:11:26 CST] OK, Close and reopen your terminal to start using acme.sh
[2023年 04月 07日 星期五 22:11:26 CST] Installing alias to '/root/.cshrc'
[2023年 04月 07日 星期五 22:11:26 CST] Installing alias to '/root/.tcshrc'
[2023年 04月 07日 星期五 22:11:26 CST] Installing cron job
[2023年 04月 07日 星期五 22:11:26 CST] Good, bash is found, so change the shebang to use bash as preferred.
[2023年 04月 07日 星期五 22:11:28 CST] OK
[2023年 04月 07日 星期五 22:11:28 CST] Install success!

这里采用DNS验证获取证书,设置一下DNS厂商信息,比如DNSPOD,默认获取ZeroSSL证书,可以通过–server letsencrypt切换

➜ ~ export DP_Id="95731"
➜ ~ export DP_Key="***"
➜ ~ acme.sh --issue --dns dns_dp -d example.com -d "*.example.com"
[2023年 04月 07日 星期五 22:14:24 CST] Using CA: https://acme.zerossl.com/v2/DV90
[2023年 04月 07日 星期五 22:14:24 CST] Create account key ok.
[2023年 04月 07日 星期五 22:14:24 CST] No EAB credentials found for ZeroSSL, let's get one
[2023年 04月 07日 星期五 22:14:29 CST] Registering account: https://acme.zerossl.com/v2/DV90
[2023年 04月 07日 星期五 22:14:38 CST] Registered
[2023年 04月 07日 星期五 22:14:38 CST] ACCOUNT_THUMBPRINT='AY5noJZbSullM7fVQOnluxQJmWiiKJHl0vzVsYwwGsg'
[2023年 04月 07日 星期五 22:14:38 CST] Creating domain key
[2023年 04月 07日 星期五 22:14:38 CST] The domain key is here: /root/.acme.sh/example.com_ecc/example.com.key
[2023年 04月 07日 星期五 22:14:38 CST] Multi domain='DNS:example.com
[2023年 04月 07日 星期五 22:14:39 CST] Getting domain auth token for each domain
[2023年 04月 07日 星期五 22:14:56 CST] Getting webroot for domain='example.com'
[2023年 04月 07日 星期五 22:14:56 CST] Getting webroot for domain='*.example.com'
[2023年 04月 07日 星期五 22:14:56 CST] Adding txt value: q-pQccpOPbYbC4h_veQTSvj2OgUpgg9kBUFpms9fa9k for domain: _acme-challenge.example.com
[2023年 04月 07日 星期五 22:14:57 CST] Adding record
[2023年 04月 07日 星期五 22:14:57 CST] The txt record is added: Success.
[2023年 04月 07日 星期五 22:15:01 CST] Let's check each DNS record now. Sleep 20 seconds first.
[2023年 04月 07日 星期五 22:15:23 CST] You can use '--dnssleep' to disable public dns checks.
[2023年 04月 07日 星期五 22:15:23 CST] See: https://github.com/acmesh-official/acme.sh/wiki/dnscheck
[2023年 04月 07日 星期五 22:15:23 CST] Checking example.com for _acme-challenge.example.com
[2023年 04月 07日 星期五 22:15:23 CST] Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: 35
[2023年 04月 07日 星期五 22:15:27 CST] Domain example.com '_acme-challenge.example.com' success.
[2023年 04月 07日 星期五 22:15:34 CST] All success, let's return
[2023年 04月 07日 星期五 22:15:34 CST] Verifying: example.com
[2023年 04月 07日 星期五 22:15:46 CST] Processing, The CA is processing your order, please just wait. (1/30)
[2023年 04月 07日 星期五 22:15:51 CST] Success
[2023年 04月 07日 星期五 22:15:51 CST] Verifying: *.example.com
[2023年 04月 07日 星期五 22:15:55 CST] Processing, The CA is processing your order, please just wait. (1/30)
[2023年 04月 07日 星期五 22:16:28 CST] Success
[2023年 04月 07日 星期五 22:16:28 CST] Removing DNS records.
[2023年 04月 07日 星期五 22:16:28 CST] Removing txt: q-pQccpOPbYbC4h_veQTSvj2OgUpgg9kBUFpms9fa9k for domain: _acme-challenge.example.com
[2023年 04月 07日 星期五 22:16:30 CST] Removed: Success
[2023年 04月 07日 星期五 22:16:34 CST] Verify finished, start to sign.
[2023年 04月 07日 星期五 22:16:35 CST] Lets finalize the order.
[2023年 04月 07日 星期五 22:16:40 CST] Order status is processing, lets sleep and retry.
[2023年 04月 07日 星期五 22:16:40 CST] Retry after: 15
[2023年 04月 07日 星期五 22:17:00 CST] Downloading cert.
[2023年 04月 07日 星期五 22:17:00 CST] Le_LinkCert='https://acme.zerossl.com/v2/DV90/cert/pcAU9PLEoObR7xYPTb9x7w'
[2023年 04月 07日 星期五 22:17:03 CST] Cert success.
-----BEGIN CERTIFICATE-----
MIIELzCCA7agAwIBAgIRAOO4Gyu...
QRwR2SSrAlEIeJXua7aYyyEEJQ==
-----END CERTIFICATE-----
[2023年 04月 07日 星期五 22:17:03 CST] Your cert is in: /root/.acme.sh/example.com_ecc/example.com.cer
[2023年 04月 07日 星期五 22:17:03 CST] Your cert key is in: /root/.acme.sh/example.com_ecc/example.com.key
[2023年 04月 07日 星期五 22:17:03 CST] The intermediate CA cert is in: /root/.acme.sh/example.com_ecc/ca.cer
[2023年 04月 07日 星期五 22:17:03 CST] And the full chain certs is there: /root/.acme.sh/example.com_ecc/fullchain.cer

最后安装证书

➜  ~ acme.sh --install-cert -d example.com -d "*.example.com"--key-file /etc/letsencrypt/live/example.com/privkey.pem --fullchain-file /etc/letsencrypt/live/example.com/chain.pem --reloadcmd "systemctl restart nginx"
[2023年 04月 07日 星期五 23:00:13 CST] The domain 'example.com' seems to have a ECC cert already, lets use ecc cert.
[2023年 04月 07日 星期五 23:00:13 CST] Installing key to: /etc/letsencrypt/live/example.com/privkey.pem
[2023年 04月 07日 星期五 23:00:13 CST] Installing full chain to: /etc/letsencrypt/live/example.com/chain.pem
[2023年 04月 07日 星期五 23:00:13 CST] Run reload cmd: systemctl restart nginx
[2023年 04月 07日 星期五 23:00:13 CST] Reload success

以后就可以自动更新了

➜  ~  crontab -l  | grep acme 
26 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

最近登录后台发现WordPress提示升级到PHP 7.3,按照它的指示

在CentOS 7上升级PHP5.4 到PHP 7.3很简单:
首先安装Remi和EPEL仓库

yum install wget
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm 
rpm -Uvh epel-release-latest-7.noarch.rpm

yum install yum-utils

启用remi-php73的源,yum update升级会自动升级PHP及扩展

[root@li846-239 ~]# yum-config-manager --enable remi-php73
[root@li846-239 ~]# yum repolist
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.linode.com
 * epel: kartolo.sby.datautama.net.id
 * extras: mirrors.linode.com
 * remi-php73: mirror.xeonbd.com
 * remi-safe: mirror.xeonbd.com
 * updates: mirrors.linode.com
repo id                                                                                                   repo name                                                   status
base/7/x86_64                                                                                             CentOS-7 - Base                                              10,019
epel/x86_64                                                                                               Extra Packages for Enterprise Linux 7 - x86_64               13,051
extras/7/x86_64                                                                                           CentOS-7 - Extras                                               385
remi-php73                                                                                                Remi's PHP 7.3 RPM repository for Enterprise Linux 7 - x86_64   305
remi-safe                                                                                                 Safe Remi's RPM repository for Enterprise Linux 7 - x86_64    3,188
updates/7/x86_64                                                                                          CentOS-7 - Updates                                            1,511
repolist: 28,825


yum update -y

检查PHP版本,重启Apache

[root@li846-239 ~]# php -v
PHP 7.3.4 (cli) (built: Apr  2 2019 13:48:50) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies
[root@li846-239 ~]# systemctl restart httpd

也可以禁用对应PHP版本的源,选择性升级PHP到对应版本

yum-config-manager --disable remi-php72

顺便升级下php-mcrypt和ZipArchive

yum install php-mcrypt
yum install php-pecl-zip

参考链接:
How To Install Linux, Apache, MySQL, PHP (LAMP) stack On CentOS 7
How to enable EPEL repository?
How to Secure Your Server
Introduction to FirewallD on CentOS
How to Upgrade PHP 5.6 to PHP 7.2 on CentOS VestaCP

使用Docker Compose管理Docker容器

上一篇创建了一个PHP的运行环境,现在需要一个MySQL服务,直接运行:

root@thinkpad:~# docker run --name rocket-mysql -v /home/rocketfish/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7

就自动下载并得到一个mysql版本为5.7的服务镜像。
这时候,要运行一个Web服务我们需要运行两次的docker run才可以,如果还有更多的Web容器或其他的服务容器呢?
Docker官方提倡一个容器仅提供一个服务,多个服务/容器可以使用docker-compose来管理。
docker-compose本身是一个Python写的工具,可以直接通过pip安装:

root@thinkpad:~# sudo pip install --upgrade pip

如果你本地并没有Python环境,也可以采用docker-compose的docker镜像来运行:

root@thinkpad:/home/compose-web# curl -L https://github.com/docker/compose/releases/download/1.8.0/run.sh > /usr/local/bin/docker-compose
root@thinkpad:/home/compose-web# docker-compose --version
#开始下载镜像,建议用第一种

查看帮助

root@thinkpad:~# docker-compose -h
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name (default: directory name)
  --verbose                   Show more output
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the name specified
                              in the client certificate (for example if your docker host
                              is an IP address)

Commands:
  build              Build or rebuild services
  bundle             Generate a Docker bundle from the Compose file
  config             Validate and view the compose file
  create             Create services
  down               Stop and remove containers, networks, images, and volumes
  events             Receive real time events from containers
  exec               Execute a command in a running container
  help               Get help on a command
  kill               Kill containers
  logs               View output from containers
  pause              Pause services
  port               Print the public port for a port binding
  ps                 List containers
  pull               Pulls service images
  push               Push service images
  restart            Restart services
  rm                 Remove stopped containers
  run                Run a one-off command
  scale              Set number of containers for a service
  start              Start services
  stop               Stop services
  unpause            Unpause services
  up                 Create and start containers
  version            Show the Docker-Compose version information

这些命令包括了创建/启动/停止/暂停/继续运行容器。
首先要创建docker-compose.yml,这是一个YAML格式的文档

mkdir docker
cd docker
mkdir web
mkdir db
vim docker-compose.yml

内容如下:

version: '2'
services:
  db:
    image: mysql:5.7
    ports:
    - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: web
      MYSQL_USER: root
      MYSQL_PASSWORD: rootweb
    volumes:
    - ./db/data:/var/lib/mysql
  web:
    depends_on:
      - db
    image: nginx-php-fpm:phalcon
    ports:
    - "80:80"
    restart: always
    environment:
      WEB_DB_HOST: db:3306
      WEB_DB_PASSWORD: root
    volumes:
    - ./web/html:/var/www/html/
    links:
    - db

这里的environment定义了环境变量,比如host的MAC,就可以在docker容器的系统变量里面得到,各个编程语言都有获取系统环境变量的方法。这些变量也可以组织起来放在文件里面加载进去,参考这里
这里的volumes定义了要映射进容器的文件或文件夹或者数据容器,可以参考这里。注意,多个容器共享同一个目录,会出现写冲突,比如MySQL,多个实例,数据目录需要分开。所以设计好你的程序,哪一部分是需要只读的(可以共享),哪一部分是需要写的;写的部分是否可以放到各自临时目录,或者其他公共服务里面。
运行并查看

root@thinkpad:/home/compose-web# docker-compose up -d
Creating network "composeweb_default" with the default driver
Creating composeweb_db_1
Creating composeweb_web_1
root@thinkpad:/home/compose-web# docker-compose ps
      Name                   Command             State              Ports            
------------------------------------------------------------------------------------
composeweb_db_1    docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp      
composeweb_web_1   /start.sh                     Up      443/tcp, 0.0.0.0:80->80/tcp 
#也可以使用原来的命令
root@thinkpad:/home/compose-web# docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                         NAMES
efbdaf257748        nginx-php-fpm:phalcon   "/start.sh"              13 seconds ago      Up 11 seconds       0.0.0.0:80->80/tcp, 443/tcp   composeweb_web_1
a6935d20911e        mysql:5.7               "docker-entrypoint.sh"   14 seconds ago      Up 13 seconds       0.0.0.0:3306->3306/tcp        composeweb_db_1

docker-compose up -d ,将会在后台启动并运行所有的容器。
docker-compose仅仅提供了对多个docker服务的管理,仍然可以在这些容器上运行原来的docker命令。检查下web容器的IP:

root@thinkpad:/home/compose-web# docker inspect composeweb_web_1 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.18.0.3",

然后访问:http://172.18.0.3/ 就可以看到web页面了。
如果想要停止服务,可以用docker-compose stop:

root@thinkpad:/home/compose-web# docker-compose stop db
Stopping composeweb_db_1 ... done
root@thinkpad:/home/compose-web# docker-compose stop web
Stopping composeweb_web_1 ... done

再次启动,得到相同名称的服务:

root@thinkpad:/home/compose-web# docker-compose up -d
Starting composeweb_db_1
Starting composeweb_web_1

各个容器的名称也可以在配置文件里面通过参数container_name来指定。
docker-compose的配置还有很多其他的参数,可以参考这里。比如刚才我们通过docker inspect来查找容器IP,也可以配置成静态IP:

version: '2'

services:
  app:
    image: nginx-php-fpm:phalcon
    networks:
      app_net:
        ipv4_address: 172.18.0.10
        ipv6_address: 2001:3984:3989::10

networks:
  app_net:
    driver: bridge
    driver_opts:
      com.docker.network.enable_ipv6: "true"
    ipam:
      driver: default
      config:
      - subnet: 172.18.0.0/24
        gateway: 172.18.0.1
      - subnet: 2001:3984:3989::/64
        gateway: 2001:3984:3989::1

也可以自定义网络
刚才我们的配置文件里面直接指定了镜像,也可以指定Dockerfile镜像构建,假如Dockerfile在web目录下面:

build:
  context: ./web

或者直接指明Dockerfile:

build:
  context: .
  dockerfile: Dockerfile-alternate

实际上,docker-compose的配置已经覆盖了Dockerfile的配置项,也可以直接用于构建多个docker服务,比如指定运行的命令

command: [/bin/bash]

这里的入口程序,如果不能持续运行的话,运行完成后,docker就会退出。所以如果你需要可以后台运行的docker容器,那么入口程序,就必须保持一个程序在前台运行,不退出,比如运行crontab的镜像,入口程序可以是:

cron && bash

cron这个程序是在后台运行的,如果不配合前台程序bash,容器执行玩cron就会退出了。其他运行crontab的容器,大都是强制一个程序在前台不退出,比如这个

cron && tail -f /var/log/cron.log

这里需要注意一下,通过docker设置的environment values在容器内的cli下面执行时取不到的,比如printenv,使用cron后台运行时取不到外部设置的变量,但是手动执行时又可以。这些外部环境变量只能在ENTRYPOINT或者CMD所执行的shell里面去设置,比如在CMD执行start.sh:

printenv | grep -E "^HOST" > /root/env.conf && cron && bash

Docker compose还可以跟Docker Swarm结合。

参考链接:
Install Docker Compose
Quickstart: Docker Compose and WordPress
YAML 模板文件
Introduction to Docker Compose Tool for Multi-Container Applications
Dockerfile基本结构
How to Get Environment Variables Passed Through docker-compose to the Containers
Access environment variables from crontab into a docker container
How can I access Docker set Environment Variables From a Cron Job
Dockerfile里指定执行命令用ENTRYPOING和用CMD有何不同?
What is the difference between CMD and ENTRYPOINT in a Dockerfile?
Docker difference between run, cmd, entrypoint commands

MySql 慢日志分析

最近老是碰上MySql报错:1203:User already has more than ‘max_user_connections’ active,之前都没出现过,感觉应该是慢查询导致的。向运维拷贝慢日志分析,慢日志开、启配置参考这里
拷贝出来的日志很大,需要按故障时间点做一下切割,以便缩小排查范围。按照这里提供的cutlogbytime.pl脚本运行却报错

[vagrant@centos64 mysql-log-filter-1.9]$ ./cutlogbytime.pl slow.log 1443103200 1443117600 > yestoday.log
: command not foundline 1:
: command not foundline 4:
./cutlogbytime.pl: line 5: use: command not found
: command not foundline 5:
./cutlogbytime.pl: line 6: use: command not found
: command not foundline 6:
: command not foundline 7:
'/cutlogbytime.pl: line 8: syntax error near unexpected token `{
'/cutlogbytime.pl: line 8: `if (@ARGV<2){

去掉顶行的空格后再运行,还是报错

[vagrant@centos64 mysql-log-filter-1.9]$ ./cutlogbytime.pl slow.log 1443103200 1443117600 > today.log
-bash: ./cutlogbytime.pl: /usr/bin/perl^M: bad interpreter: No such file or directory

最后参考stackoverflow上面的答案更改运行方式为Perl(而不是shell),就可以了。

[vagrant@centos64 mysql-log-filter-1.9]$ perl cutlogbytime.pl slow.log 1443103200 1443117600 > today.log

利用mysqlslowdump(perl脚本)来分析日志,-s参数表示排序方式:r表示影响行数(Rows),t表示耗时(Time),c表示查询次数(Count)

[vagrant@entos64 mysql-log-filter-1.9]$  perl mysqldumpslow.pl -s r -t 10 today4.log

Reading mysql slow query log from today4.log
Count: 1  Time=190.48s (190s)  Lock=0.00s (0s)  Rows=21829854.0 (21829854), xx[xxxx]@[192.168.10.139]
  SELECT /*!N SQL_NO_CACHE */ * FROM `errormessage`

Count: 32791  Time=40.95s (1342865s)  Lock=0.05s (1512s)  Rows=1.0 (32791), xx[xxxx]@10hosts
  select  *  from connectinfo where  ID=N  and AppType=N  ORDER BY CreateDatetime DESC LIMIT N

Count: 3  Time=3.71s (11s)  Lock=0.02s (0s)  Rows=300.0 (900), xx[xxxx]@2hosts
  select SeverName from errormessage where  ID='S'  and ServerType=N  and level=N  and MsgType <= N

第一个语句返回行数21829854,查看具体慢日志,之后需要插入这张表的进程均处于等待状态。

# Time: 150924  1:03:12
# User@Host: xx[xxxx] @  [192.168.10.139]  Id: 1493761
# Query_time: 190.479062  Lock_time: 0.000000 Rows_sent: 21829854  Rows_examined: 21829854
SET timestamp=1443027792;
SELECT /*!40001 SQL_NO_CACHE */ * FROM `errormessage`;
# Time: 150924  1:03:14
# User@Host: xx[xxxx] @  [192.168.10.168]  Id: 1498010
# Query_time: 59.669817  Lock_time: 57.159403 Rows_sent: 0  Rows_examined: 0
SET timestamp=1443027794;
insert into errormessage (`ID`,`ServerType`,`MsgType`,`Level`,`dev`,`content`,`EventTime`,`SeverName`) values ( '1217', '3', '4', '4', '827', 'erc:start erc error,songid=46243,keymd5=ee1275b26762e85a7f00e9890bdc092e,ercmd5=abbc3ea9102dbd003b7aa0547dcbf6fa', '2015-09-23 21:49:27', '192.168.15.117');
# User@Host: xx[xxxx] @  [192.168.10.205]  Id: 1494756
# Query_time: 157.211158  Lock_time: 154.673647 Rows_sent: 0  Rows_examined: 0
SET timestamp=1443027794;
insert into errormessage (`ID`,`ServerType`,`MsgType`,`Level`,`dev`,`content`,`EventTime`,`SeverName`) values ( '865', '3', '1', '2', '106', '检测正常!', '2015-09-24 01:01:18', '192.168.0.33');
# User@Host: xx[xxxx] @  [192.168.10.213]  Id: 1496479
# Query_time: 100.733230  Lock_time: 98.210902 Rows_sent: 0  Rows_examined: 0
SET timestamp=1443027794;
insert into errormessage (`ID`,`ServerType`,`MsgType`,`Level`,`dev`,`content`,`EventTime`,`SeverName`) values ( '2472', '3', '2', '4', '809', 'videoseripnoconfig', '2015-09-24 01:02:26', '192.168.0.18');

分析这几天的日志,发现故障时间点附近都是这个语句引起后面的SQL堵塞。原来是每天早上1点开始备份并同步全表数据,锁住了这个表导致后面的所有这个表的insert操作处于等待状态。mysqldump应该使用–single-transaction来避免锁表,类似下面这个

mysqldump –uuser -p --skip-opt -q -R  --single-transaction --default-character-set=utf8 --master-data=2  --create-option --no-autocommit –S ${sock} -B ${DBName}  > backup.sql

但是这样仍然是全表扫描进行备份,如果能够增量备份的话,影响要小很多,或者数据做冷热/新旧之分,定期将新(每天)/热数据转入旧(历史)/冷数据中。后面运维的解决方案是:升级数据库机器从虚拟机变为实体机,配置从机,并从从机进行备份同步。

上面mysqlslowdump使用影响行数来排序,事实上用另外两个类型(时间,次数)分析结果是connectinfo比较频繁,一直以来都认为是这个表的操作引起的。这里还尝试了其他工具来分析,使用mysqlsla.pl进行分析,相关参数选项参考这里

[vagrant@centos64 mysql-log-filter-1.9]$  perl mysqlsla.pl today.log
Auto-detected logs as slow logs
Report for slow logs: today4.log
60.57k queries total, 17 unique
Sorted by 't_sum'
Grand Totals: Time 5.38M s, Lock 3.22M s, Rows sent 21.86M, Rows Examined 184.46M


______________________________________________________________________ 001 ___
Count         : 25.59k  (42.24%)
Time          : 3905525.574451 s total, 152.643069 s avg, 113.07488 s to 2720.338946 s max  (72.64%)
  95% of Time : 3260112.482495 s total, 134.12789 s avg, 113.07488 s to 282.366041 s max
Lock Time (s) : 3168076.975558 s total, 123.820721 s avg, 108.548105 s to 311.639359 s max  (98.45%)
  95% of Lock : 2961933.212121 s total, 121.860167 s avg, 108.548105 s to 123.487106 s max
Rows sent     : 0 avg, 0 to 0 max  (0.00%)
Rows examined : 54 avg, 0 to 4.92k max  (0.75%)
Database      :
Users         :
        xx@ 192.168.10.147 : 10.65% (2724) of query, 10.26% (6215) of all users
        xx@ 192.168.10.209 : 10.33% (2643) of query, 10.16% (6156) of all users
        xx@ 192.168.10.205 : 10.16% (2599) of query, 9.97% (6036) of all users
        xx@ 192.168.10.211 : 10.13% (2591) of query, 9.98% (6042) of all users
        xx@ 192.168.10.207 : 9.93% (2541) of query, 9.95% (6024) of all users
        xx@ 192.168.10.161 : 9.83% (2515) of query, 9.84% (5960) of all users
        xx@ 192.168.10.149 : 9.81% (2510) of query, 9.95% (6028) of all users
        xx@ 192.168.10.215 : 9.76% (2498) of query, 9.85% (5963) of all users
        xx@ 192.168.10.168 : 9.71% (2485) of query, 9.69% (5868) of all users
        xx@ 192.168.10.213 : 9.69% (2480) of query, 9.66% (5851) of all users

Query abstract:
SET timestamp=N; UPDATE connectinfo SET devicetag='S', connectipaddress='S', updatedatetime=now() WHERE ID=N AND apptype=N;

Query sample:
SET timestamp=1443027797;
update connectinfo set DeviceTag='1070A416AF000000', ConnectIPAddress='60.174.116.165', UpdateDatetime=now() where ID=5358 and AppType=0;

______________________________________________________________________ 002 ___
Count         : 32.79k  (54.14%)
Time          : 1344378.871914 s total, 40.99841 s avg, 2.000747 s to 1944.548192 s max  (25.01%)
  95% of Time : 587407.556704 s total, 18.85678 s avg, 2.000747 s to 233.465042 s max
Lock Time (s) : 1512.917798 s total, 46.138 ms avg, 76 ▒s to 114.302 ms max  (0.05%)
  95% of Lock : 1414.978902 s total, 45.423 ms avg, 76 ▒s to 50.514 ms max
Rows sent     : 1 avg, 1 to 1 max  (0.15%)
Rows examined : 4.92k avg, 4.92k to 4.92k max  (87.41%)
Database      :
Users         :
        xx@ 192.168.10.209 : 10.24% (3359) of query, 10.16% (6156) of all users
        xx@ 192.168.10.149 : 10.16% (3331) of query, 9.95% (6028) of all users
        xx@ 192.168.10.147 : 10.11% (3315) of query, 10.26% (6215) of all users
        xx@ 192.168.10.211 : 10.03% (3288) of query, 9.98% (6042) of all users
        xx@ 192.168.10.207 : 10.02% (3285) of query, 9.95% (6024) of all users
        xx@ 192.168.10.161 : 9.97% (3268) of query, 9.84% (5960) of all users
        xx@ 192.168.10.215 : 9.96% (3266) of query, 9.85% (5963) of all users
        xx@ 192.168.10.205 : 9.92% (3254) of query, 9.97% (6036) of all users
        xx@ 192.168.10.168 : 9.86% (3234) of query, 9.69% (5868) of all users
        xx@ 192.168.10.213 : 9.73% (3191) of query, 9.66% (5851) of all users

Query abstract:
SET timestamp=N; SELECT * FROM connectinfo WHERE ID=N AND apptype=N ORDER BY createdatetime DESC LIMIT N;

Query sample:
SET timestamp=1443027795;
select  *  from connectinfo where  ID=7646  and AppType=0  ORDER BY CreateDatetime DESC LIMIT 1;

______________________________________________________________________ 003 ___
Count         : 842  (1.39%)
Time          : 66663.314786 s total, 79.172583 s avg, 2.011408 s to 673.604537 s max  (1.24%)
  95% of Time : 56684.989954 s total, 70.944919 s avg, 2.011408 s to 193.623235 s max
Lock Time (s) : 48221.988255 s total, 57.27077 s avg, 69 ▒s to 185.402303 s max  (1.50%)
  95% of Lock : 40627.196184 s total, 50.847555 s avg, 69 ▒s to 166.67704 s max
Rows sent     : 0 avg, 0 to 0 max  (0.00%)
Rows examined : 0 avg, 0 to 0 max  (0.00%)
Database      :
Users         :
        xx@ 192.168.10.207 : 11.64% (98) of query, 9.95% (6024) of all users
        xx@ 192.168.10.205 : 11.28% (95) of query, 9.97% (6036) of all users
        xx@ 192.168.10.213 : 10.93% (92) of query, 9.66% (5851) of all users
        xx@ 192.168.10.161 : 10.45% (88) of query, 9.84% (5960) of all users
        xx@ 192.168.10.149 : 10.33% (87) of query, 9.95% (6028) of all users
        xx@ 192.168.10.211 : 9.74% (82) of query, 9.98% (6042) of all users
        xx@ 192.168.10.147 : 9.38% (79) of query, 10.26% (6215) of all users
        xx@ 192.168.10.215 : 9.38% (79) of query, 9.85% (5963) of all users
        xx@ 192.168.10.168 : 9.03% (76) of query, 9.69% (5868) of all users
        xx@ 192.168.10.209 : 7.84% (66) of query, 10.16% (6156) of all users

Query abstract:
SET timestamp=N; INSERT INTO errormessage (id,servertype,msgtype,level,dev,content,eventtime,severname) VALUES ( 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S')1;

使用mysqlsla可以看SQL语句的执行数量/比例,影响行数,用户,占比等。,从这里看很可能认为是connectinfo表(95%以上)引起,SHOW PROCESSLIST也是如此 。
由于这个文件我是单独下载回来的,运行mysqlsla.pl时候碰到很多错误,逐个安装解决了

[vagrant@centos64 mysql-log-filter-1.9]$  perl mysqlsla.pl today.log
Can't locate Time/HiRes.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at mysqlsla.pl line 2092.
BEGIN failed--compilation aborted at mysqlsla.pl line 2092.
#解决方法
[vagrant@centos64 mysql-log-filter-1.9]$ sudo yum install perl-Time-HiRes

[vagrant@centos64 mysql-log-filter-1.9]$ perl -MCPAN -e 'install DBI'
Can't locate CPAN.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .).
BEGIN failed--compilation aborted.

#解决方法
[vagrant@centos64 mysql-log-filter-1.9]$ sudo yum install perl-DBI

正确的方法是应该检测对应根目录下面的Makefile.PL,比如

[vagrant@centos64 percona-toolkit-2.2.15]$ perl Makefile.PL
#如果报以下错误,需要先安装对应模块,简单点就是
#sudo yum install perl-devel
Can't locate ExtUtils/MakeMaker.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at Makefile.PL line 1.
BEGIN failed--compilation aborted at Makefile.PL line 1.

然后安装对应模块,参考这里
使用Percona公司的工具pt-query-digest来分析也得到了同mysqlsla类似的结果

[vagrant@vagrant-centos64 bin]$ pt-query-digest ../../today4.log

# 9.8s user time, 700ms system time, 21.05M rss, 73.66M vsz
# Current date: Mon Oct  5 05:52:01 2015
# Hostname: vagrant-centos64.vagrantup.com
# Files: ../../today.log
# Overall: 60.57k total, 17 unique, 4.54 QPS, 402.68x concurrency ________
# Time range: 2015-09-23 22:17:29 to 2015-09-24 02:00:00
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
# Exec time        5376198s      2s   2720s     89s    258s    118s     57s
# Lock time        3217840s       0    312s     53s    118s     60s    48ms
# Rows sent         20.85M       0  20.82M  361.00    0.99  84.46k    0.99
# Rows examine     175.91M       0  20.82M   2.97k   4.71k  84.48k   4.71k
# Query size         7.85M      64     597  135.90  151.03   27.56  112.70

# Profile
# Rank Query ID           Response time      Calls R/Call   V/M   Item
# ==== ================== ================== ===== ======== ===== ========
#    1 0xF1132168DB0BFC57 3905525.5745 72.6% 25586 152.6431 61.61 UPDATE connectinfo
#    2 0xD4B317E755A0ABD7 1344378.8719 25.0% 32791  40.9984 30... SELECT connectinfo
#    3 0xE23849EE6FB19DAE   66663.3148  1.2%   842  79.1726 62.99 INSERT errormessage
...

# Query 1: 7.52 QPS, 1.15kx concurrency, ID 0xF1132168DB0BFC57 at byte 16243195
# This item is included in the report because it matches --limit.
# Scores: V/M = 61.61
# Time range: 2015-09-24 01:03:17 to 02:00:00
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         42   25586
# Exec time     72 3905526s    113s   2720s    153s    271s     97s    124s
# Lock time     98 3168077s    109s    312s    124s    118s     14s    118s
# Rows sent      0       0       0       0       0       0       0       0
# Rows examine   0   1.33M       0   4.80k   54.39       0  504.65       0
# Query size    48   3.78M     149     157  154.94  151.03    0.52  151.03
# String:
# Hosts        192.168.10.147 (2724/10%)... 9 more
# Users        gate
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms
#  10ms
# 100ms
#    1s
#  10s+  ################################################################
# Tables
#    SHOW TABLE STATUS LIKE 'connectinfo'\G
#    SHOW CREATE TABLE `connectinfo`\G
update connectinfo set DeviceTag='10705BDDCD000000', ConnectIPAddress='115.231.63.78', UpdateDatetime=now() where ID=6912 and AppType=0\G
# Converted for EXPLAIN
# EXPLAIN /*!50100 PARTITIONS*/
select  DeviceTag='10705BDDCD000000', ConnectIPAddress='115.231.63.78', UpdateDatetime=now() from connectinfo where  ID=6912 and AppType=0\G

PS:这个问题解决后没几天,数据库又出问题,以为又是慢SQL什么的,结果是交换机网口问题。。。数据传输太慢,导致主从不同步。。。

参考链接:
mysql 慢日志分析
慢日志按时间截取
MySQL 之 slow log
MySQL优化—工欲善其事,必先利其器(2)
日志常用统计技巧
性能优化之MySQL优化(一)
mysqlsla的安装与使用
安装DBI组件。 Can’t locate DBI.pm in @INC-mysql接口
Issue 12: Can’t locate Time/HiRes.pm
打开MySQL的慢查询记录
“Can’t locate ExtUtils/MakeMaker.pm” while compile git
analysing slow MySQL queries with pt-query-digest
mysqldump备份原理
mysqldump –single-transaction, yet update queries are waiting for the backup
mysql 利用binlog增量备份,还原实例
MySQl备份恢复策略(完全+增量备份策略)