MySQL提供了count命令来统计表中的记录数, 使用起来非常方便。但加上where条件的count命令有时会很慢,此时需要优化。

最近在项目中就遇到这个问题。看下面两个SQL的查询结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SELECT COUNT(*) FROM user WHERE user.is_active = 1;
+-----------+

| COUNT(id) |
+-----------+

| 239563 |
+-----------+

1 row in set (0.21 sec)

mysql> select count(*) from sku_pro where is_agent=1;
+-----------+

| count(id) |
+-----------+

| 1253535 |
+-----------+

1 row in set (0.11 sec)

看到这个结果,肯定会很吃惊。user表的数据比sku_pro表的数据少很多,但执行时间却比它长,匪夷所思。查看两个表结构,两个表都是MyISAM引擎,看到的差别是user表61个字段,而sku_pro表16个字段。

在网上找各种资料,没有找到问题的答案。于是请教同学,同学提示说看数据大小,如果数据大的,读到内存需要花费更多的IO,这样会更慢一些。但发现两个表数据大小差不多。

在查看show table status的结果时,发现user表的RowFormat是Dynamic, 而sku_pro表的是Fixed, 于是查看user表结构,发现很多字段是varchar字段,于是猜测在Dynamic时,要查找到字段的值,需要计算便宜量,这样速度更慢。在MySQL Optimization: Faster Selects with MyISAM fixed row format一文中有提到这个问题。

但是如果不想改变RowFormat又该怎么做?在StackoverFlow上提了这个问题, 很快得到解答,只需要创建索引即可,需要注意索引里有加上id字段,

1
CREATE INDEX is_active ON user (is_active,id);

值得注意的是,这里不是创建

1
CREATE INDEX is_active ON user (is_active);

在解答中有提到,这个索引会被忽略。

参考资料:

联系作者

最近在添加微信支付的公众号支付功能,遇到一些问题,记录下来。

您没有JSAPI支付权限

这个是因为在商户后台的支付配置里,没有设置支付目录

支付签名失效

这里要注意app支付的签名字段和公众号支付的签名字是不一样

app签名字段

查看app支付的业务流程,可以看到

1
2
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。
参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay

公众号支付jsapi

公众号支付的业务流程里,没有看到像APP支付那样的步骤,没有写明签名字段,查看H5调起支付API, 看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId""wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp"" 1395712654", //时间戳,自1970年以来的秒数
"nonceStr""e61463f8efa94090b1f366cccfbbb444", //随机串
"package""prepay_id=u802345jgfjsdfgsdg888",
"signType""MD5", //微信签名方式:
"paySign""70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}

还是没有弄清楚paySign字段的生成方式。

但是在公众平台的微信支付文档里, 又看到发起微信支付请求

1
2
3
4
5
6
7
8
9
10
11
12
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});

备注:prepay_id 通过微信支付统一下单接口拿到,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。

在这里提到了生成paySign需要的字段。但纳闷的是为何发起微信支付请求会有两种方式,难道是新旧方式?

需要注意的是,在getBrandWCPayRequest方式中,时间戳字段是timeStamp,而在chooseWXPay中,时间戳字段是timestamp.

在APP支付中,package字段的值是Sign=WXPay, 而在公众号支付中,package字段的值是 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***

联系作者

最近遇到一个DateTimeField的问题,记录下来

在Post的model里设置

1
update_time = models.DateTimeField(auto_now=True)

希望每次更新文章时,这个update_time能自动更新为当前时间。

发现了三种情况

使用queryset的update方法

这里使用update方法更新,Post.object.filter(pk=id).update(), 发现不会更新update_time

使用save方法

1
2
post = Post.object.filter(pk=id)[:1][0]
post.save()

此时会更新update_time

使用save方法,指定update_fields

1
2
post = Post.object.filter(pk=id)[:1][0]
post.save(update_fields=['title'])

此时指定了更新字段,不更新update_time

联系作者

有时候想了解MySQL的版本号,运行状态等,此时status命令派上了用场

在mysql客户端里输入status命令,可以查看MySQL服务器的运行状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> status
--------------
mysql Ver 14.14 Distrib 5.7.10, for osx10.11 (x86_64) using EditLine wrapper

Connection id: 282
Current database: blog
Current user: root@localhost
SSL: Not in use
Current pager: less
Using outfile: ''
Using delimiter: ;
Server version: 5.7.10 Homebrew
Protocol version: 10
Connection: Localhost via UNIX socket
Server characterset: utf8
Db characterset: utf8
Client characterset: utf8
Conn. characterset: utf8
UNIX socket: /tmp/mysql.sock
Uptime: 29 days 18 hours 32 min 59 sec

Threads: 1 Questions: 8074 Slow queries: 0 Opens: 1592 Flush tables: 1 Open tables: 354 Queries per second avg: 0.003

而当你想查看MySQL的表运行状态时,可以执行show table status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

mysql> show table status;
+-----------------------+--------+---------+------------+------+----------------+-------------+------------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+--------------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-----------------------+--------+---------+------------+------+----------------+-------------+------------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+--------------------+----------+----------------+---------+
| wp_commentmeta | MyISAM | 10 | Dynamic | 25 | 207 | 5184 | 281474976710655 | 10240 | 0 | 827 | 2016-03-21 16:04:14 | 2016-03-21 16:04:14 | NULL | utf8_general_ci | NULL | | |
| wp_comments | MyISAM | 10 | Dynamic | 129 | 558 | 72008 | 281474976710655 | 20480 | 0 | 1005 | 2016-03-21 16:20:21 | 2016-03-21 16:20:21 | 2016-03-21 16:20:21 | utf8_general_ci | NULL | | |
| wp_links | MyISAM | 10 | Dynamic | 13 | 97 | 1272 | 281474976710655 | 3072 | 0 | 25 | 2016-03-21 16:04:14 | 2016-03-21 16:04:15 | NULL | utf8_general_ci | NULL | | |
| wp_options | MyISAM | 10 | Dynamic | 247 | 1451 | 364504 | 281474976710655 | 22528 | 6084 | 95575 | 2016-03-21 16:20:22 | 2016-06-21 17:46:41 | NULL | utf8_general_ci | NULL | | |
| wp_postmeta | MyISAM | 10 | Dynamic | 952 | 116 | 111032 | 281474976710655 | 47104 | 76 | 2312 | 2016-03-21 16:04:14 | 2016-05-30 14:39:33 | NULL | utf8_general_ci | NULL | | |
| wp_posts | MyISAM | 10 | Dynamic | 782 | 4397 | 3439132 | 281474976710655 | 1702912 | 0 | 1212 | 2016-03-21 16:20:21 | 2016-06-21 17:46:38 | 2016-03-21 16:20:22 | utf8_general_ci | NULL | | |
| wp_term_relationships | MyISAM | 10 | Fixed | 620 | 21 | 13020 | 5910974510923775 | 31744 | 0 | NULL | 2016-03-21 16:04:15 | 2016-05-30 14:00:52 | NULL | utf8_general_ci | NULL | | |
| wp_term_taxonomy | MyISAM | 10 | Dynamic | 368 | 39 | 14696 | 281474976710655 | 17408 | 0 | 421 | 2016-03-21 16:04:15 | 2016-05-30 14:00:53 | NULL | utf8_general_ci | NULL | | |
| wp_termmeta | InnoDB | 10 | Dynamic | 0 | 0 | 16384 | 0 | 32768 | 0 | 1 | 2016-07-04 11:14:31 | NULL | NULL | utf8mb4_unicode_ci | NULL | | |
| wp_terms | MyISAM | 10 | Dynamic | 367 | 45 | 16880 | 281474976710655 | 35840 | 0 | 420 | 2016-03-21 16:04:15 | 2016-03-21 16:04:16 | NULL | utf8_general_ci | NULL | | |
| wp_usermeta | MyISAM | 10 | Dynamic | 28 | 79 | 3092 | 281474976710655 | 10240 | 864 | 31 | 2016-03-21 16:04:15 | 2016-06-21 17:46:33 | NULL | utf8_general_ci | NULL | | |
| wp_users | MyISAM | 10 | Dynamic | 1 | 96 | 96 | 281474976710655 | 4096 | 0 | 2 | 2016-03-21 16:20:21 | 2016-03-21 16:20:21 | NULL | utf8_general_ci | NULL | | |
| wp_wp_rp_tags | MyISAM | 10 | Dynamic | 173 | 24 | 4200 | 281474976710655 | 8192 | 0 | NULL | 2016-03-21 16:04:15 | 2016-03-21 16:04:16 | NULL | latin1_swedish_ci | NULL | | |
+-----------------------+--------+---------+------------+------+----------------+-------------+------------------+--------------+-----------+----------------+---------------------+---------------------+---------------------+--------------------+----------+----------------+---------+

其中,Engine是使用的引擎,Row_format指的是行模式(Dynamic说明存在varchar字段, Fixed说明表的所有列长度都是固定的),Rows指的是表记录数,Data_length指的是表数据大小。

联系作者

因为启动docker服务器需要root权限,所以连接到docker服务器需要输入sudo, 即便设置了sudo不需要输入密码, 还是需要输入sudo, 可以通过增加docker组的方式来避免输入sudo.

增加一个docker组

sudo groupadd docker

将用户test加入docker组,用户test需要重新登录才能生效

sudo gpasswd -a test docker

重启docker服务

sudo service docker restart

此后执行docker命令就不需要加上sudo

参考资料:

联系作者

准备慢慢的将香港VPS上的服务迁移到新的服务器,趁这个机会,学习使用Docker技术。首先安装MySQL

搜索MySQL镜像

使用docker search命令,docker search mysql

1
2
3
4
5
INDEX       NAME                                 DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io docker.io/mysql MySQL is a widely used, open-source relati... 3021 [OK]
docker.io docker.io/mysql/mysql-server Optimized MySQL Server Docker images. Crea... 194 [OK]
docker.io docker.io/centurylink/mysql Image containing mysql. Optimized to be li... 46 [OK]
docker.io docker.io/sameersbn/mysql 36 [OK]

下载MySQL镜像

使用docker pull命令,docker pull docker.io/mysql

启动MySQL镜像

使用docker run命令,执行docker run docker.io/mysql
提示

1
2
error: database is uninitialized and password option is not specified
You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD

参考How to connect to MySQL running on Docker from the host machine,执行docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d docker.io/mysql启动

访问MySQL容器

1
2
sudo docker exec -it mysql bash
mysql -uroot -ppassword

参考资料

联系作者

之前也设置过SSH登录,这次记下来吧。

生成公钥

使用ssh-keygen命令,例如执行ssh-keygen -t rsa即可

添加ssh key

在远程服务器的用户目录新建.ssh目录,新建authorized_keys文件,将本地上传的id_ras.pub里的内容添加到authorized_keys文件里。

这里需要注意.ssh和authorized_kesy权限,.ssh必须是700, 而authorized_keys文件只有文件拥有者有写权限。否则会提示Authentication refused: bad ownership or modes for file /home/dengsl/.ssh/authorized_keys错误。所以authorized_keys的权限设置必须为600, 640等等。

从阮一峰的博客里看到一条命令,但其实这条命令里存在错误,修改之后如下

1
ssh user@host 'mkdir -p .ssh && chmod 700 .ssh && touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

如果已经存在.ssh目录,可以执行

1
ssh user@host 'chmod 700 .ssh && touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

之后会提示输入密码,之后就可以不需要密码登录了。

禁止root用户登录

修改/etc/ssh/sshd_config文件,在#PermitRootLogin yes后面添加PermitRootLogin no即可。之后重启sshd服务service sshd restart使修改的配置生效

参考资料

联系作者

最近在DigitOcean买了VPS,又开始折腾主机。这次要记录下来。首先从新建用户开始。

新建用户

新增用户使用useradd命令,例如新增test用户,执行命令useradd test即可。新增用户后,要给用户设置密码,否则无法登录。

修改用户密码

设置密码使用passwd命令,例如给test用户设置密码,执行’passwd test’即可。

设置用户sudo不需要密码

每次安装软件都要切换到root用户比较麻烦,可以添加用户sudo时不需要密码。在/etc/sudoers文件里添加
test ALL=(ALL) NOPASSWD: ALL即可。

联系作者

最近服务时不时的会卡顿,不知道什么原因。下班之后突然想到速度慢,一般都出在数据库上。于是想到查看MySQL链接数,网上查到执行show processlist;即可。

在卡顿时,查询结果中发现以下可疑连接,如下

1
2
3
 Waiting for table level lock | UPDATE `sku` SET  `view_num` = `sku`.`view_num` + 1 WHER |         0 |             0 |         1 |

Waiting for table level lock | SELECT `sku`.`id`, `sku`.`name`, `sku`.`dosage_form`, `sku`.`specs`, `sku`.`factory`, `sku`.`categor | 0 | 0 | 1 |

在淘宝的MySQL资料MySQL 锁问题最佳实践里找到’table level lock’的原因,是因为MyISAM,引发table level lock wait。查看建表语句,果然是MyISAM引擎, 将它转成InnoDB即可解决问题。

上面使用show processlist命令显示不完全,可以加上full, 即执行show full processlist

参考Converting Tables from MyISAM to InnoDB, 执行ALTER TABLE table_name ENGINE=InnoDB;即可转换。

联系作者