Mysql之binlog日志说明及利用binlog日志恢复数据操作记录
众所周知,binlog日志对于mysql数据库来说是十分重要的。在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全备份+binlog日志恢复增量数据部分),化险为夷!
废话不多说,下面是梳理的binlog日志操作解说:
一、初步了解binlog
MySQL的二进制日志binlog可以说是MySQL最重要的日志,它记录了所有的DDL和DML语句(除了数据查询语句select),以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。
----------------------------------------------------------------------------------------------------------------------------------------------
DDL
----Data Definition Language 数据库定义语言
主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用。
DML
----Data Manipulation Language 数据操纵语言
主要的命令是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言
----------------------------------------------------------------------------------------------------------------------------------------------
mysqlbinlog常见的选项有以下几个:
--start-datetime:从二进制日志中读取指定等于时间戳或者晚于本地计算机的时间
--stop-datetime:从二进制日志中读取指定小于时间戳或者等于本地计算机的时间 取值和上述一样
--start-position:从二进制日志中读取指定position 事件位置作为开始。
--stop-position:从二进制日志中读取指定position 事件位置作为事件截至
*********************************************************************
一般来说开启binlog日志大概会有1%的性能损耗。
binlog日志有两个最重要的使用场景:
1)MySQL主从复制:MySQL Replication在Master端开启binlog,Master把它的二进制日志传递给slaves来达到
master-slave数据一致的目的。
2)自然就是数据恢复了,通过使用mysqlbinlog工具来使恢复数据。
binlog日志包括两类文件:
1)二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件
2)二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句select)语句事件。
二、开启binlog日志:
1)编辑打开mysql配置文件/etc/mys.cnf
[root@vm-002 ~]# vim /etc/my.cnf
在[mysqld] 区块添加
log-bin=mysql-bin 确认是打开状态(mysql-bin 是日志的基本名或前缀名);
2)重启mysqld服务使配置生效
[root@vm-002 ~]# /etc/init.d/mysqld stop
[root@vm-002 ~]# /etc/init.d/mysqld restart
Stopping mysqld: [ OK ]
Starting mysqld: [ OK ]
3)查看binlog日志是否开启
mysql> show variables like 'log_%';
+---------------------------------+---------------------+
| Variable_name | Value |
+---------------------------------+---------------------+
| log_bin | ON |
| log_bin_trust_function_creators | OFF |
| log_bin_trust_routine_creators | OFF |
| log_error | /var/log/mysqld.log |
| log_output | FILE |
| log_queries_not_using_indexes | OFF |
| log_slave_updates | OFF |
| log_slow_queries | OFF |
| log_warnings | 1 |
+---------------------------------+---------------------+
9 rows in set (0.00 sec)
三、常用的binlog日志操作命令
1)查看所有binlog日志列表
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 149 |
| mysql-bin.000002 | 4102 |
+------------------+-----------+
2 rows in set (0.00 sec)
2)查看master状态,即最后(最新)一个binlog日志的编号名称,及其最后一个操作事件pos结束点(Position)值
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000002 | 4102 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
3)flush刷新log日志,自此刻开始产生一个新编号的binlog日志文件
mysql> flush logs;
Query OK, 0 rows affected (0.13 sec)
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 149 |
| mysql-bin.000002 | 4145 |
| mysql-bin.000003 | 106 |
+------------------+-----------+
3 rows in set (0.00 sec)
注意:
每当mysqld服务重启时,会自动执行此命令,刷新binlog日志;在mysqldump备份数据时加 -F 选项也会刷新binlog日志;
4)重置(清空)所有binlog日志
mysql> reset master;
Query OK, 0 rows affected (0.12 sec)
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 106 |
+------------------+-----------+
1 row in set (0.00 sec)
四、查看binlog日志内容,常用有两种方式:
1)使用mysqlbinlog自带查看命令法:
注意:
-->binlog是二进制文件,普通文件查看器cat、more、vim等都无法打开,必须使用自带的mysqlbinlog命令查看
-->binlog日志与数据库文件在同目录中
-->在MySQL5.5以下版本使用mysqlbinlog命令时如果报错,就加上 “--no-defaults”选项
查看mysql的数据存放目录,从下面结果可知是/var/lib//mysql
[root@vm-002 ~]# ps -ef|grep mysql
root 9791 1 0 21:18 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pid-file=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql
mysql 9896 9791 0 21:18 pts/0 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock
root 9916 9699 0 21:18 pts/0 00:00:00 mysql -px xxxx
root 9919 9715 0 21:23 pts/1 00:00:00 grep --color mysql
[root@vm-002 ~]# cd /var/lib/mysql/
[root@vm-002 mysql]# ls
ibdata1 ib_logfile0 ib_logfile1 mysql mysql-bin.000001 mysql-bin.000002 mysql-bin.index mysql.sock ops test
使用mysqlbinlog命令查看binlog日志内容,下面截取其中的一个片段分析:
[root@vm-002 mysql]# mysqlbinlog mysql-bin.000002
..............
# at 624
#160925 21:29:53 server id 1 end_log_pos 796 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1474810193/*!*/;
insert into member(`name`,`sex`,`age`,`classid`) values('wangshibo','m',27,'cls1'),('guohuihui','w',27,'cls2') #执行的sql语句
/*!*/;
# at 796
#160925 21:29:53 server id 1 end_log_pos 823 Xid = 17 #执行的时间
.............
解释:
server id 1 : 数据库主机的服务号;
end_log_pos 796: sql结束时的pos节点
thread_id=11: 线程号
2)上面这种办法读取出binlog日志的全文内容比较多,不容易分辨查看到pos点信息
下面介绍一种更为方便的查询命令:
命令格式:
mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];
参数解释:
IN 'log_name' :指定要查询的binlog文件名(不指定就是第一个binlog文件)
FROM pos :指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)
LIMIT [offset,] :偏移量(不指定就是0)
row_count :查询总条数(不指定就是所有行)
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 125 |
| mysql-bin.000002 | 823 |
+------------------+-----------+
2 rows in set (0.00 sec)
mysql> show binlog events in 'mysql-bin.000002'\G;
*************************** 1. row ***************************
Log_name: mysql-bin.000002
Pos: 4
Event_type: Format_desc
Server_id: 1
End_log_pos: 106
Info: Server ver: 5.1.73-log, Binlog ver: 4
*************************** 2. row ***************************
Log_name: mysql-bin.000002
Pos: 106
Event_type: Query
Server_id: 1
End_log_pos: 188
Info: use `ops`; drop table customers
*************************** 3. row ***************************
Log_name: mysql-bin.000002
Pos: 188
Event_type: Query
Server_id: 1
End_log_pos: 529
Info: use `ops`; CREATE TABLE IF NOT EXISTS `member` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(16) NOT NULL,
`sex` enum('m','w') NOT NULL DEFAULT 'm',
`age` tinyint(3) unsigned NOT NULL,
`classid` char(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
*************************** 4. row ***************************
Log_name: mysql-bin.000002
Pos: 529
Event_type: Query
Server_id: 1
End_log_pos: 596
Info: BEGIN
*************************** 5. row ***************************
Log_name: mysql-bin.000002
Pos: 596
Event_type: Intvar
Server_id: 1
End_log_pos: 624
Info: INSERT_ID=1
*************************** 6. row ***************************
Log_name: mysql-bin.000002
Pos: 624
Event_type: Query
Server_id: 1
End_log_pos: 796
Info: use `ops`; insert into member(`name`,`sex`,`age`,`classid`) values('wangshibo','m',27,'cls1'),('guohuihui','w',27,'cls2')
*************************** 7. row ***************************
Log_name: mysql-bin.000002
Pos: 796
Event_type: Xid
Server_id: 1
End_log_pos: 823
Info: COMMIT /* xid=17 */
7 rows in set (0.00 sec)
ERROR:
No query specified
mysql>
上面这条语句可以将指定的binlog日志文件,分成有效事件行的方式返回,并可使用limit指定pos点的起始偏移,查询条数!
如下操作示例:
a)查询第一个(最早)的binlog日志:
mysql> show binlog events\G;
b)指定查询 mysql-bin.000002这个文件:
mysql> show binlog events in 'mysql-bin.000002'\G;
c)指定查询 mysql-bin.000002这个文件,从pos点:624开始查起:
mysql> show binlog events in 'mysql-bin.000002' from 624\G;
d)指定查询 mysql-bin.000002这个文件,从pos点:624开始查起,查询10条(即10条语句)
mysql> show binlog events in 'mysql-bin.000002' from 624 limit 10\G;
e)指定查询 mysql-bin.000002这个文件,从pos点:624开始查起,偏移2行(即中间跳过2个),查询10条
mysql> show binlog events in 'mysql-bin.000002' from 624 limit 2,10\G;
五、利用binlog日志恢复mysql数据
以下对ops库的member表进行操作
mysql> use ops;
mysql> CREATE TABLE IF NOT EXISTS `member` (
-> `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
-> `name` varchar(16) NOT NULL,
-> `sex` enum('m','w') NOT NULL DEFAULT 'm',
-> `age` tinyint(3) unsigned NOT NULL,
-> `classid` char(6) DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.10 sec)
mysql> show tables;
+---------------+
| Tables_in_ops |
+---------------+
| member |
+---------------+
1 row in set (0.00 sec)
mysql> desc member;
+---------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(16) | NO | | NULL | |
| sex | enum('m','w') | NO | | m | |
| age | tinyint(3) unsigned | NO | | NULL | |
| classid | char(6) | YES | | NULL | |
+---------+---------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
事先插入两条数据
mysql> insert into member(`name`,`sex`,`age`,`classid`) values('wangshibo','m',27,'cls1'),('guohuihui','w',27,'cls2');
Query OK, 2 rows affected (0.08 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
+----+-----------+-----+-----+---------+
2 rows in set (0.00 sec)
下面开始进行场景模拟:
1)
ops库会在每天凌晨4点进行一次完全备份的定时计划任务,如下:
[root@vm-002 ~]# crontab -l
0 4 * * * /usr/bin/mysqldump -uroot -p -B -F -R -x --master-data=2 ops|gzip >/opt/backup/ops_$(date +%F).sql.gz
这里手动执行下,将ops数据库备份到/opt/backup/ops_$(date +%F).sql.gz文件中:
[root@vm-002 ~]# mysqldump -uroot -p -B -F -R -x --master-data=2 ops|gzip >/opt/backup/ops_$(date +%F).sql.gz
Enter password:
[root@vm-002 ~]# ls /opt/backup/
ops_2016-09-25.sql.gz
-----------------
参数说明:
-B:指定数据库
-F:刷新日志
-R:备份存储过程等
-x:锁表
--master-data:在备份语句里添加CHANGE MASTER语句以及binlog文件及位置点信息
-----------------
待到数据库备份完成,就不用担心数据丢失了,因为有完全备份数据在!!
由于上面在全备份的时候使用了-F选项,那么当数据备份操作刚开始的时候系统就会自动刷新log,这样就会自动产生
一个新的binlog日志,这个新的binlog日志就会用来记录备份之后的数据库“增删改”操作
查看一下:
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 | 106 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
也就是说, mysql-bin.000003 是用来记录4:00之后对数据库的所有“增删改”操作。
2)
早上9点上班了,由于业务的需求会对数据库进行各种“增删改”操作。
比如:在ops库下member表内插入、修改了数据等等:
先是早上进行插入数据:
mysql> insert into ops.member(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');
Query OK, 5 rows affected (0.08 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | xiaoer | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
3)
中午又执行了修改数据操作:
mysql> update ops.member set name='李四' where id=4;
Query OK, 1 row affected (0.07 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update ops.member set name='小二' where id=2;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | 小二 | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | 李四 | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
4)
在下午18:00的时候,悲剧莫名其妙的出现了!
手贱执行了drop语句,直接删除了ops库!吓尿!
mysql> drop database ops;
Query OK, 1 row affected (0.02 sec)
5)
这种时候,一定不要慌张!!!
先仔细查看最后一个binlog日志,并记录下关键的pos点,到底是哪个pos点的操作导致了数据库的破坏(通常在最后几步);
先备份一下最后一个binlog日志文件:
[root@vm-002 ~]# cd /var/lib/mysql/
[root@vm-002 mysql]# cp -v mysql-bin.000003 /opt/backup/
`mysql-bin.000003' -> `/opt/backup/mysql-bin.000003'
[root@vm-002 mysql]# ls /opt/backup/
mysql-bin.000003 ops_2016-09-25.sql.gz
接着执行一次刷新日志索引操作,重新开始新的binlog日志记录文件。按理说mysql-bin.000003
这个文件不会再有后续写入了,因为便于我们分析原因及查找ops节点,以后所有数据库操作都会写入到下一个日志文件。
mysql> flush logs;
Query OK, 0 rows affected (0.13 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 | 106 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
6)
读取binlog日志,分析问题。
读取binlog日志的方法上面已经说到。
方法一:使用mysqlbinlog读取binlog日志:
[root@vm-002 ~]# cd /var/lib/mysql/
[root@vm-002 mysql]# mysqlbinlog mysql-bin.000003
方法二:登录服务器,并查看(推荐此种方法)
mysql> show binlog events in 'mysql-bin.000003';
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------+
| mysql-bin.000003 | 4 | Format_desc | 1 | 106 | Server ver: 5.1.73-log, Binlog ver: 4 |
| mysql-bin.000003 | 106 | Query | 1 | 173 | BEGIN |
| mysql-bin.000003 | 173 | Intvar | 1 | 201 | INSERT_ID=3 |
| mysql-bin.000003 | 201 | Query | 1 | 444 | use `ops`; insert into ops.member(`name`,`sex`,`age`,`gsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6') |
| mysql-bin.000003 | 444 | Xid | 1 | 471 | COMMIT /* xid=66 */ |
| mysql-bin.000003 | 471 | Query | 1 | 538 | BEGIN |
| mysql-bin.000003 | 538 | Query | 1 | 646 | use `ops`; update ops.member set name='李四' where id= |
| mysql-bin.000003 | 646 | Xid | 1 | 673 | COMMIT /* xid=68 */ |
| mysql-bin.000003 | 673 | Query | 1 | 740 | BEGIN |
| mysql-bin.000003 | 740 | Query | 1 | 848 | use `ops`; update ops.member set name='小二' where id= |
| mysql-bin.000003 | 848 | Xid | 1 | 875 | COMMIT /* xid=69 */ |
| mysql-bin.000003 | 875 | Query | 1 | 954 | drop database ops |
| mysql-bin.000003 | 954 | Rotate | 1 | 997 | mysql-bin.000004;pos=4 |
+------------------+-----+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------+
13 rows in set (0.00 sec)
或者:
mysql> show binlog events in 'mysql-bin.000003'\G;
.........
.........
*************************** 12. row ***************************
Log_name: mysql-bin.000003
Pos: 875
Event_type: Query
Server_id: 1
End_log_pos: 954
Info: drop database ops
*************************** 13. row ***************************
Log_name: mysql-bin.000003
Pos: 954
Event_type: Rotate
Server_id: 1
End_log_pos: 997
Info: mysql-bin.000004;pos=4
13 rows in set (0.00 sec)
通过分析,造成数据库破坏的pos点区间是介于 875--954 之间(这是按照日志区间的pos节点算的),只要恢复到875前就可。
7)
先把凌晨4点全备份的数据恢复:
[root@vm-002 ~]# cd /opt/backup/
[root@vm-002 backup]# ls
mysql-bin.000003 ops_2016-09-25.sql.gz
[root@vm-002 backup]# gzip -d ops_2016-09-25.sql.gz
[root@vm-002 backup]# mysql -uroot -p -v < ops_2016-09-25.sql
Enter password:
--------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */
--------------
--------------
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */
--------------
.............
.............
--------------
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */
--------------
这样就恢复了截至当日凌晨(4:00)前的备份数据都恢复了。
mysql> show databases; #发现ops库已经恢复回来了
mysql> use ops;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+---------------+
| Tables_in_ops |
+---------------+
| member |
+---------------+
1 row in set (0.00 sec)
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
+----+-----------+-----+-----+---------+
2 rows in set (0.00 sec)
mysql>
但是这仅仅只是恢复了当天凌晨4点之前的数据,在4:00--18:00之间的数据还没有恢复回来!!
怎么办呢?
莫慌!这可以根据前面提到的mysql-bin.000003的新binlog日志进行恢复。
8)
从binlog日志恢复数据
恢复命令的语法格式:
mysqlbinlog mysql-bin.0000xx | mysql -u用户名 -p密码 数据库名
--------------------------------------------------------
常用参数选项解释:
--start-position=875 起始pos点
--stop-position=954 结束pos点
--start-datetime="2016-9-25 22:01:08" 起始时间点
--stop-datetime="2019-9-25 22:09:46" 结束时间点
--database=zyyshop 指定只恢复zyyshop数据库(一台主机上往往有多个数据库,只限本地log日志)
--------------------------------------------------------
不常用选项:
-u --user=name 连接到远程主机的用户名
-p --password[=name] 连接到远程主机的密码
-h --host=name 从远程主机上获取binlog日志
--read-from-remote-server 从某个MySQL服务器上读取binlog日志
--------------------------------------------------------
小结:实际是将读出的binlog日志内容,通过管道符传递给mysql命令。这些命令、文件尽量写成绝对路径;
a)完全恢复(需要手动vim编辑mysql-bin.000003,将那条drop语句剔除掉)
[root@vm-002 backup]# /usr/bin/mysqlbinlog /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
b)指定pos结束点恢复(部分恢复):
--stop-position=471 pos结束节点(按照事务区间算,是471)
注意:
此pos结束节点介于“member表原始数据”与更新“name='李四'”之前的数据,这样就可以恢复到更改“name='李四'”之前的数据了。
操作如下:
[root@vm-002 ~]# /usr/bin/mysqlbinlog --stop-position=471 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | xiaoer | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
恢复截止到更改“name='李四'”之间的数据(按照事务区间算,是673)
[root@vm-002 ~]# /usr/bin/mysqlbinlog --stop-position=673 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | 李四 | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
c)指定pso点区间恢复(部分恢复):
更新 name='李四' 这条数据,日志区间是Pos[538] --> End_log_pos[646],按事务区间是:Pos[471] --> End_log_pos[673]
更新 name='小二' 这条数据,日志区间是Pos[740] --> End_log_pos[848],按事务区间是:Pos[673] --> End_log_pos[875]
c1)
单独恢复 name='李四' 这步操作,可这样:
按照binlog日志区间单独恢复:
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-position=538 --stop-position=646 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
按照事务区间单独恢复
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-position=471 --stop-position=673 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
c2)
单独恢复 name='小二' 这步操作,可这样:
按照binlog日志区间单独恢复:
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-position=740 --stop-position=848 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
按照事务区间单独恢复
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-position=673 --stop-position=875 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
c3)
将 name='李四'、name='小二' 多步操作一起恢复,需要按事务区间,可这样:
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-position=471 --stop-position=875 --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
查看数据库:
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | 小二 | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | 李四 | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
这样,就恢复了删除前的数据状态了!!
-----------------
另外:
也可指定时间节点区间恢复(部分恢复):
除了用pos节点的办法进行恢复,也可以通过指定时间节点区间进行恢复,按时间恢复需要用mysqlbinlog命令读取binlog日志内容,找时间节点。
如上,误删除ops库后:
先进行全备份恢复
[root@vm-002 backup]# mysql -uroot -p -v < ops_2016-09-25.sql
查看ops数据库
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
+----+-----------+-----+-----+---------+
2 rows in set (0.00 sec)
mysql>
查看mysq-bin00003日志,找出时间节点
[root@vm-002 ~]# cd /var/lib/mysql
[root@vm-002 mysql]# mysqlbinlog mysql-bin.000003
.............
.............
BEGIN
/*!*/;
# at 173
#160925 21:57:19 server id 1 end_log_pos 201 Intvar
SET INSERT_ID=3/*!*/;
# at 201
#160925 21:57:19 server id 1 end_log_pos 444 Query thread_id=3 exec_time=0 error_code=0
use `ops`/*!*/;
SET TIMESTAMP=1474811839/*!*/;
insert into ops.member(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6') #执行的sql语句
/*!*/;
# at 444
#160925 21:57:19 server id 1 end_log_pos 471 Xid = 66 #开始执行的时间
COMMIT/*!*/;
# at 471
#160925 21:58:41 server id 1 end_log_pos 538 Query thread_id=3 exec_time=0 error_code=0 #结束时间
SET TIMESTAMP=1474811921/*!*/;
BEGIN
/*!*/;
# at 538
#160925 21:58:41 server id 1 end_log_pos 646 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1474811921/*!*/;
update ops.member set name='李四' where id=4 #执行的sql语句
/*!*/;
# at 646
#160925 21:58:41 server id 1 end_log_pos 673 Xid = 68 #开始执行的时间
COMMIT/*!*/;
# at 673
#160925 21:58:56 server id 1 end_log_pos 740 Query thread_id=3 exec_time=0 error_code=0 #结束时间
SET TIMESTAMP=1474811936/*!*/;
BEGIN
/*!*/;
# at 740
#160925 21:58:56 server id 1 end_log_pos 848 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1474811936/*!*/;
update ops.member set name='小二' where id=2 #执行的sql语句
/*!*/;
# at 848
#160925 21:58:56 server id 1 end_log_pos 875 Xid = 69 #开始执行的时间
COMMIT/*!*/;
# at 875
#160925 22:01:08 server id 1 end_log_pos 954 Query thread_id=3 exec_time=0 error_code=0 #结束时间
SET TIMESTAMP=1474812068/*!*/;
drop database ops
/*!*/;
# at 954
#160925 22:09:46 server id 1 end_log_pos 997 Rotate to mysql-bin.000004 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
恢复到更改“name='李四'”之前的数据
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-datetime="2016-09-25 21:57:19" --stop-datetime="2016-09-25 21:58:41" --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | xiaoer | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-datetime="2016-09-25 21:58:41" --stop-datetime="2016-09-25 21:58:56" --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | guohuihui | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | 李四 | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
[root@vm-002 ~]# /usr/bin/mysqlbinlog --start-datetime="2016-09-25 21:58:56" --stop-datetime="2016-09-25 22:01:08" --database=ops /var/lib/mysql/mysql-bin.000003 | /usr/bin/mysql -uroot -p123456 -v ops
mysql> select * from member;
+----+-----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+-----------+-----+-----+---------+
| 1 | wangshibo | m | 27 | cls1 |
| 2 | 小二 | w | 27 | cls2 |
| 3 | yiyi | w | 20 | cls1 |
| 4 | 李四 | m | 22 | cls3 |
| 5 | zhangsan | w | 21 | cls5 |
| 6 | lisi | m | 20 | cls4 |
| 7 | wangwu | w | 26 | cls6 |
+----+-----------+-----+-----+---------+
7 rows in set (0.00 sec)
这样,就恢复了删除前的状态了!
总结:
所谓恢复,就是让mysql将保存在binlog日志中指定段落区间的sql语句逐个重新执行一次而已。
MySQL 用户权限详细汇总
1,MySQL权限体系
MySQL 的权限体系大致分为5个层级:
全局层级:
全局权限适用于一个给定服务器中的所有数据库。这些权限存储在mysql.user表中。GRANT ALL ON .和REVOKE ALL ON .只授予和撤销全局权限。
数据库层级:
数据库权限适用于一个给定数据库中的所有目标。这些权限存储在mysql.db表中。GRANT ALL ON db_name.和REVOKE ALL ON db_name.只授予和撤销数据库权限。
表层级:
表权限适用于一个给定表中的所有列。这些权限存储在mysql.talbes_priv表中。GRANT ALL ON db_name.tbl_name和REVOKE ALL ON db_name.tbl_name只授予和撤销表权限。
列层级:
列权限适用于一个给定表中的单一列。这些权限存储在mysql.columns_priv表中。当使用REVOKE时,您必须指定与被授权列相同的列。
子程序层级:
CREATE ROUTINE, ALTER ROUTINE, EXECUTE和GRANT权限适用于已存储的子程序。这些权限可以被授予为全局层级和数据库层级。而且,除了CREATE ROUTINE外,这些权限可以被授予为子程序层级,并存储在mysql.procs_priv表中。
这些权限信息存储在下面的系统表中:
mysql.user
mysql.db
mysql.host
mysql.table_priv
mysql.column_priv
mysql. procs_priv
当用户连接进来,mysqld会通过上面的这些表对用户权限进行验证!
2, 千里追踪之5表
相对于Oracle来说,mysql的特性是可以限制ip,用户user、ip地址host、密码passwd这3个是用户管理的基础,权限的细节基本在mysql.user、mysql.db、mysql.host、mysql.table_priv、mysql.column_priv这几张表就可以看到很多细节,接下来仔细分析这些表就可以知道权限的奥秘。
演示过程中需要建立用户来演示,先简单介绍下如何创建用户:
GRANT priv_type ON database.table
TO user[IDENTIFIED BY [PASSWORD] ‘password’]
[,user [IDENTIFIED BY [PASSWORD] ‘password’]…]
示例:
GRANT SELECT, INSERT, UPDATE, DELETE ON d3307.* TO zengxiaoteng@’%’ IDENTIFIED BY ‘0523’;
2.1db表
2.1.1 表结构如下:
mysql> desc mysql.db;
+-----------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+---------------+------+-----+---------+-------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Select_priv | enum('N','Y') | NO | | N | |
| Insert_priv | enum('N','Y') | NO | | N | |
| Update_priv | enum('N','Y') | NO | | N | |
| Delete_priv | enum('N','Y') | NO | | N | |
| Create_priv | enum('N','Y') | NO | | N | |
| Drop_priv | enum('N','Y') | NO | | N | |
| Grant_priv | enum('N','Y') | NO | | N | |
| References_priv | enum('N','Y') | NO | | N | |
| Index_priv | enum('N','Y') | NO | | N | |
| Alter_priv | enum('N','Y') | NO | | N | |
| Create_tmp_table_priv | enum('N','Y') | NO | | N | |
| Lock_tables_priv | enum('N','Y') | NO | | N | |
| Create_view_priv | enum('N','Y') | NO | | N | |
| Show_view_priv | enum('N','Y') | NO | | N | |
| Create_routine_priv | enum('N','Y') | NO | | N | |
| Alter_routine_priv | enum('N','Y') | NO | | N | |
| Execute_priv | enum('N','Y') | NO | | N | |
| Event_priv | enum('N','Y') | NO | | N | |
| Trigger_priv | enum('N','Y') | NO | | N | |
+-----------------------+---------------+------+-----+---------+-------+
22 rows in set (0.02 sec)
2.1.2分析如下:
db表存储了所有对一个数据库的所有操作权限。创建用户的时候,都会往Host字段,User字段,Password字段录入用户信息;
而当执行 GRANT SELECT,INSERT ON d3307.* TO u4@’%’ IDENTIFIED BY ‘u40523’;类似的授权语句的话,Select_priv和Insert_priv字段的值会变成Y其它字段仍然是N;
当你执行了GRANT ALL ON d3307.* TO u4@’%’ IDENTIFIED BY ‘u40523’;类似的复制语句的话,后面的字段都会变成Y的值;
2.1.3 创建单个select、insert授予权限
创建用户:
GRANT SELECT,INSERT ON d3307.* TO user4@'192.168.52' IDENTIFIED BY 'user0523';
应该除了Host、db、user字段有值,除了Select_priv、Insert_priv值为Y外,其它的都是N。
查看mysql.db表的记录正是如此,如下所示:
mysql> SELECT * FROM mysql.`db` where user='user4'\G;
*************************** 1. row ***************************
Host: 192.168.52
Db: d3307
User: user4
Select_priv: Y
Insert_priv: Y
Update_priv: N
Delete_priv: N
Create_priv: N
Drop_priv: N
Grant_priv: N
References_priv: N
Index_priv: N
Alter_priv: N
Create_tmp_table_priv: N
Lock_tables_priv: N
Create_view_priv: N
Show_view_priv: N
Create_routine_priv: N
Alter_routine_priv: N
Execute_priv: N
Event_priv: N
Trigger_priv: N
1 row in set (0.01 sec)
ERROR:
No query specified
2.1.4 授予ALL权限
执行sql语句建立用户:
GRANT ALL ON d3307.* TO dba5@'192.168.52.1' IDENTIFIED BY 'dba0523';
建立用户的时候,如下所示,除了Host、db、user字段外,所有的*_priv字段记录都会变成Y值,(Grant_priv仍然是N值除非加了WITH* GRANT OPTION执行GRANT ALL ON d3307.* TO dba5@’192.168.52.1’ IDENTIFIED BY ‘dba0523’ WITH GRANT OPTION 😉
如下所示:
mysql> SELECT * FROM mysql.`db` where user='dba5'\G;
*************************** 1. row ***************************
Host: 192.168.52.1
Db: d3307
User: dba5
Select_priv: Y
Insert_priv: Y
Update_priv: Y
Delete_priv: Y
Create_priv: Y
Drop_priv: Y
Grant_priv: N
References_priv: Y
Index_priv: Y
Alter_priv: Y
Create_tmp_table_priv: Y
Lock_tables_priv: Y
Create_view_priv: Y
Show_view_priv: Y
Create_routine_priv: Y
Alter_routine_priv: Y
Execute_priv: Y
Event_priv: Y
Trigger_priv: Y
1 row in set (0.00 sec)
ERROR:
No query specified
2.2 user表
2.2.1 表结构:
mysql> desc mysql.user;
+------------------------+-----------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+-----------------------------------+------+-----+---------+-------+
| Host | char(60) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Password | char(41) | NO | | | |
| Select_priv | enum('N','Y') | NO | | N | |
| Insert_priv | enum('N','Y') | NO | | N | |
| Update_priv | enum('N','Y') | NO | | N | |
| Delete_priv | enum('N','Y') | NO | | N | |
| Create_priv | enum('N','Y') | NO | | N | |
| Drop_priv | enum('N','Y') | NO | | N | |
| Reload_priv | enum('N','Y') | NO | | N | |
| Shutdown_priv | enum('N','Y') | NO | | N | |
| Process_priv | enum('N','Y') | NO | | N | |
| File_priv | enum('N','Y') | NO | | N | |
| Grant_priv | enum('N','Y') | NO | | N | |
| References_priv | enum('N','Y') | NO | | N | |
| Index_priv | enum('N','Y') | NO | | N | |
| Alter_priv | enum('N','Y') | NO | | N | |
| Show_db_priv | enum('N','Y') | NO | | N | |
| Super_priv | enum('N','Y') | NO | | N | |
| Create_tmp_table_priv | enum('N','Y') | NO | | N | |
| Lock_tables_priv | enum('N','Y') | NO | | N | |
| Execute_priv | enum('N','Y') | NO | | N | |
| Repl_slave_priv | enum('N','Y') | NO | | N | |
| Repl_client_priv | enum('N','Y') | NO | | N | |
| Create_view_priv | enum('N','Y') | NO | | N | |
| Show_view_priv | enum('N','Y') | NO | | N | |
| Create_routine_priv | enum('N','Y') | NO | | N | |
| Alter_routine_priv | enum('N','Y') | NO | | N | |
| Create_user_priv | enum('N','Y') | NO | | N | |
| Event_priv | enum('N','Y') | NO | | N | |
| Trigger_priv | enum('N','Y') | NO | | N | |
| Create_tablespace_priv | enum('N','Y') | NO | | N | |
| ssl_type | enum('','ANY','X509','SPECIFIED') | NO | | | |
| ssl_cipher | blob | NO | | NULL | |
| x509_issuer | blob | NO | | NULL | |
| x509_subject | blob | NO | | NULL | |
| max_questions | int(11) unsigned | NO | | 0 | |
| max_updates | int(11) unsigned | NO | | 0 | |
| max_connections | int(11) unsigned | NO | | 0 | |
| max_user_connections | int(11) unsigned | NO | | 0 | |
| plugin | char(64) | YES | | | |
| authentication_string | text | YES | | NULL | |
| password_expired | enum('N','Y') | NO | | N | |
+------------------------+-----------------------------------+------+-----+---------+-------+
43 rows in set (0.10 sec)
2.2.2 分析
存储用户记录的表,存储了用户的信息,每一次创建用户的时候,都会往这个表里录入记录,当你执行了,都会往Host字段,User字段,Password字段录入数据,但是后面的Select_priv、Insert_priv、Update_priv等字段的值,只有赋予GRANT ALL ON . TO timdba@’192.%’ IDENTIFIED BY ‘timdba0523’;类似的对所有库的操作权限的时候才会被记录成Y,否则都记录成N。
2.2.3 创建对库所有表有操作权限的普通用户
创建用户:
GRANT SELECT,UPDATE ON d3307.* TO user6@'192.168.52.1' IDENTIFIED BY 'user0523';
分析结果:存储在mysql.user表里面的记录当中,Host、User、Password是有值的,但是其它的Select_priv等*_priv字段值都是N。
验证结果,去查看表里的存储记录,如下所示:
mysql> SELECT * FROM mysql.user where user='user6'\G;
*************************** 1. row ***************************
Host: 192.168.52.1
User: user6
Password: *A4D1F6ACEBC5D3EB0F6D33C7DCC629E8BE55B75A
Select_priv: N
Insert_priv: N
Update_priv: N
Delete_priv: N
Create_priv: N
Drop_priv: N
Reload_priv: N
Shutdown_priv: N
Process_priv: N
File_priv: N
Grant_priv: N
References_priv: N
Index_priv: N
Alter_priv: N
Show_db_priv: N
Super_priv: N
Create_tmp_table_priv: N
Lock_tables_priv: N
Execute_priv: N
Repl_slave_priv: N
Repl_client_priv: N
Create_view_priv: N
Show_view_priv: N
Create_routine_priv: N
Alter_routine_priv: N
Create_user_priv: N
Event_priv: N
Trigger_priv: N
Create_tablespace_priv: N
ssl_type:
ssl_cipher:
x509_issuer:
x509_subject:
max_questions: 0
max_updates: 0
max_connections: 0
max_user_connections: 0
plugin: mysql_native_password
authentication_string:
password_expired: N
1 row in set (0.00 sec)
ERROR:
No query specified
2.2.4 创建对于所有表有操作权限的用户
创建用户:
mysql> GRANT SELECT,UPDATE ON *.* TO user7@'%' IDENTIFIED BY 'user0523';
Query OK, 0 rows affected (0.00 sec)
分析:
基本的Host、User、Password字段有记录值,然后grant了select和update所以关于*_priv字段中select和update字段有值为Y,其它*_priv字段值应该是N。
查看记录结果,分享正确,如下所示:
mysql> SELECT * FROM mysql.user where user='user7'\G;
*************************** 1. row ***************************
Host: %
User: user7
Password: *A4D1F6ACEBC5D3EB0F6D33C7DCC629E8BE55B75A
Select_priv: Y
Insert_priv: N
Update_priv: Y
Delete_priv: N
Create_priv: N
Drop_priv: N
Reload_priv: N
Shutdown_priv: N
Process_priv: N
File_priv: N
Grant_priv: N
References_priv: N
Index_priv: N
Alter_priv: N
Show_db_priv: N
Super_priv: N
Create_tmp_table_priv: N
Lock_tables_priv: N
Execute_priv: N
Repl_slave_priv: N
Repl_client_priv: N
Create_view_priv: N
Show_view_priv: N
Create_routine_priv: N
Alter_routine_priv: N
Create_user_priv: N
Event_priv: N
Trigger_priv: N
Create_tablespace_priv: N
ssl_type:
ssl_cipher:
x509_issuer:
x509_subject:
max_questions: 0
max_updates: 0
max_connections: 0
max_user_connections: 0
plugin: mysql_native_password
authentication_string:
password_expired: N
1 row in set (0.00 sec)
ERROR:
No query specified
2.3 tables_priv表
2.3.1 查看表结构
mysql> desc mysql.tables_priv;
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Grantor | char(77) | NO | MUL | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Table_priv | set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') | NO | | | |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
8 rows in set (0.00 sec)
2.3.2 分析:
记录了对一个表的单独授权记录,只有执行grant insert on dbname.tablename to user1@’%’identified by ‘pwd’;类似的授权记录才会在这个表里录入授权信息;其中各个字段涵义如下:
字段 | 存储的数据 |
---|---|
Host字段 | 用户的登录ip范围 |
User字段 | 表所在的数据库名称 |
Table_name字段 | 授权的表的名称 |
Grantor字段 | 执行grant建立用户的授权者 |
Timestamp字段 | 0000-00-00 00:00:00 |
Table_priv字段 | 所授予的操作表的权限,比如select、udate、delete等 |
Column_priv字段 | 对这个表的某个字段单独授予的权限 |
另外当赋予all在某张表上的时候,Table_priv列会多处所有关于表的授权记录,描述如下:
Select,Insert,Update,Delete,Create,Drop,References,Index,Alter,Create View,Show view,Trigger。
2.3.3 创建单独操作这个表的用户
创建用户:
mysql> GRANT INSERT,SELECT,UPDATE ON d3307.t TO user8@'192.168.52.1' IDENTIFIED BY 'dba0523';
Query OK, 0 rows affected (0.00 sec)
分析结果:
应该是Host、Db、User、Table_name、Grantor、Timestamp、Table_priv是有值的,但是Column_priv没有值,因为没有单独对某一个列做了授权限制的。
查看权限,如下所示:
mysql> SELECT * FROM mysql.tables_priv where user='user8'\G;
*************************** 1. row ***************************
Host: 192.168.52.1
Db: d3307
User: user8
Table_name: t
Grantor: root@localhost
Timestamp: 0000-00-00 00:00:00
Table_priv: Select,Insert,Update
Column_priv:
1 row in set (0.00 sec)
ERROR:
No query specified
2.3.4 单独为某个列授权
授权语句操作:
mysql> GRANT UPDATE(created_time) ON d3307.t TO user8@'192.168.52.1';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT SELECT(uname) ON d3307.t TO user8@'192.168.52.1';
Query OK, 0 rows affected (0.00 sec)
分析:
单独为某个列授权,会记录在这个表的Column_priv字段里面,会记录下对单个列的授权操作记录
查看记录:
mysql> SELECT * FROM mysql.tables_priv where user='user8'\G;
*************************** 1. row ***************************
Host: 192.168.52.1
Db: d3307
User: user8
Table_name: t
Grantor: root@localhost
Timestamp: 0000-00-00 00:00:00
Table_priv: Select,Insert,Update
Column_priv: Select,Update
1 row in set (0.00 sec)
ERROR:
No query specified
而且还会在另外一个权限表mysql.columns_priv留下记录单独的授权记录,如下所示:
mysql> SELECT * FROM mysql.columns_priv WHERE USER='user8';
+--------------+-------+-------+------------+--------------+---------------------+-------------+
| Host | Db | User | Table_name | Column_name | Timestamp | Column_priv |
+--------------+-------+-------+------------+--------------+---------------------+-------------+
| 192.168.52.1 | d3307 | user8 | t | created_time | 0000-00-00 00:00:00 | Update |
| 192.168.52.1 | d3307 | user8 | t | uname | 0000-00-00 00:00:00 | Select |
+--------------+-------+-------+------------+--------------+---------------------+-------------+
2 rows in set (0.00 sec)
2.4 columns_priv表
2.4.1 表结构如下:
mysql> desc mysql.columns_priv;
+-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Column_name | char(64) | NO | PRI | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
7 rows in set (0.04 sec)
2.4.2 分析
单独对某一列有操作权限的时候,会将权限信息记录在这个表里面,比如新建立一个账号GRANT UPDATE(uname) ON d3307.t TO user9@’192.168.52.%’ IDENTIFIED BY ‘user0520’; 那么就会在这个表上录入授权信息记录,重点看Column_name字段和Column_priv字段的值。
2.4.3 实际操作
创建用户操作:
mysql> GRANT UPDATE(uname) ON d3307.t TO user9@'192.168.52.%' IDENTIFIED BY 'user0520';
Query OK, 0 rows affected (0.00 sec)
查看结果,会在这个columns_priv表留下一条记录:
mysql> SELECT * FROM mysql.columns_priv WHERE USER='user9';
+--------------+-------+-------+------------+-------------+---------------------+-------------+
| Host | Db | User | Table_name | Column_name | Timestamp | Column_priv |
+--------------+-------+-------+------------+-------------+---------------------+-------------+
| 192.168.52.% | d3307 | user9 | t | uname | 0000-00-00 00:00:00 | Update |
+--------------+-------+-------+------------+-------------+---------------------+-------------+
1 row in set (0.00 sec)
2.5 procs_priv表
2.5.1 表结构
mysql> desc proxies_priv;
+--------------+------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Proxied_host | char(60) | NO | PRI | | |
| Proxied_user | char(16) | NO | PRI | | |
| With_grant | tinyint(1) | NO | | 0 | |
| Grantor | char(77) | NO | MUL | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+--------------+------------+------+-----+-------------------+-----------------------------+
7 rows in set (0.04 sec)
2.6.2分析:
procs_priv表可以对存储过程和存储函数进行权限设置。主要字段:proc_priv。
3,创建用户
3.1、CREATE USER创建用户
使用CREATE USER语句创建用户,必须要拥有CREATE USER权限。其格式如下:
CREATE USER user[IDENTIFIED BY [PASSWORD] 'password'],
[user[IDENTIFIED BY [PASSWORD] 'password']]...
其中,user参数表示新建用户的账户,user由用户名(User)和主机名(Host)构成;IDENTIFIED BY关键字用来设置用户的密码;password参数表示用户的密码;如果密码是一个普通的字符串,就不需要使用PASSWORD关键字。可以没有初始密码。
例如
CREATE USER 'sys'@'%' IDENTIFIED BY 'sys';
执行之后user表会增加一行记录,但权限暂时全部为‘N’。
3.2、用INSERT语句新建普通用户
可以使用INSERT语句直接将用户的信息添加到mysql.user表。但必须拥有mysql.user表的INSERT权限。
另外,ssl_cipher、x509_issuer、x509_subject等必须要设置值,否则INSERT语句无法执行。
示例:
INSERT INTO mysql.user(Host,User,Password,ssl_cipher,x509_issuer,x509_subject) VALUES(‘%’,’newuser1’,PASSWORD(‘123456’),”,”,”)
执行INSERT之后,要使用命令:FLUSH PRIVILEGES;命令来使用户生效。
3.3、用GRANT语句来新建普通用户
用GRANT来创建新的用户时,能够在创建用户时为用户授权。但需要拥有GRANT权限。
语法如下:
GRANT priv_type ON database.table
TO user[IDENTIFIED BY [PASSWORD] 'password']
[,user [IDENTIFIED BY [PASSWORD] 'password']...]
priv_type:参数表示新yoghurt的权限;
databse.table:参数表示新用户的权限范围;
user:参数新用户的账户,由用户名和主机构成;
IDENTIFIED BY关键字用来设置密码;
password:新用户密码;
PS:GRANT语句可以同时创建多个用户。.与db.*的区别在于。.对所有数据库生效,所以user表的SELECT会变为Y。而db.*user表为’N’,更改的是Db表。
4,删除用户
4.1 drop user删除用户
DROP USER语句删除普通用户,需要拥有DROP USER权限。
语法如下:
DROP USER user[,user]...
user是需要删除的用户,由用户名(User)和主机名(Host)构成。
4.2 DELETE语句删除普通用户
可以使用DELETE语句直接将用户的信息从mysql.user表中删除。但必须拥有对mysql.user表的DELETE权限。DELETE FROM mysql.user WHERE Host = ‘%’ AND User = ‘admin’; 删除完成后,一样要FLUSH PRIVILEGES才生效。
5,修改用户密码
5.1 使用mysqladmin命令来修改root用户的密码
语法:
mysqladmin -u -username -p password "new_password"
新密码(new_password)必须用括号括起来,单引号会报错。
示例,修改中要输入旧的密码来验证:
[root@data02 ~]# mysqladmin -u timman -p password "tim" --socket=/usr/local/mysql3307/mysql.sock
Enter password:
[root@data02 ~]#
[root@data02 ~]# mysql --socket=/usr/local/mysql3307/mysql.sock -utimman -ptim -e "select @@port";
+--------+
| @@port |
+--------+
| 3307 |
+--------+
[root@data02 ~]#
5.2 修改user表
UPDATE user表的passwor字段的值,也可以达到修改密码的目的;
UPDATE user SET Password = PASSWORD('123') WHERE USER = 'myuser';
FLUSH PRIVILEGES;
刷新后生效。
5.3 使用SET语句来修改密码
使用root用户登录到MySQL服务器后,可以使用SET语句来修改密码:
修改自己的密码,不需要用户名
SET PASSWORD = PASSWORD("123");
修改其他用户密码:
SET PASSWORD FOR 'myuser'@'%'=PASSWORD("123456") FOR 用户名@主机名
5.4 GRANT语句来修改普通用户的密码
使用GRANT语句修改普通用户的密码,必须拥有GRANT权限。
GRANT priv_type ON database.table TO user [IDENTIFIED BY [PASSWORD] 'password']
示例:
GRANT SELECT ON *.* TO 'user10'@'%' IDENTIFIED BY '123'
5.5 忘记用户密码的解决办法
普通用户,直接用root超级管理员登录进去修改密码就可以了,但是如果root密码丢失了,怎么办呢?
5.5.1 msyqld_saft方式找回密码
停止mysql:service mysqld stop;
安全模式启动:mysqld_safe –skip-grant-tables &
无密码回车键登录:mysql -uroot –p
重置密码:use mysql; update user set password=password(“”) where user=’root’ and host=’localhost’; flush privileges;
正常启动:service mysql restart
再使用mysqladmin: mysqladmin password ‘123456’
5.5.2 使用普通账号来找回密码
–>(1):有一个修改test库的用户:grant create,delete,update,insert,select on d3307.* to test@’%’ identified by ‘t1’;
–>(2):复制user表文件到test库下并且赋予mysql用户访问权限:
cp /home/data/mysql/data/mysql/user.* /home/data/mysql/data/test/;chown mysql.mysql /home/data/mysql/data/test/user.*
–>(3):mysql -utest -pt1登录修改root密码:
–>(4):将test库的user表文件覆盖 mysql库的user表文件
cp /home/data/mysql/data/mysql/user.* /tmp/; mv /home/data/mysql/data/test/user.* /home/data/mysql/data/mysql/ ; chown mysql.mysql /home/data/mysql/data/mysql/user.*;
–>(5):查找mysql进程号,并且发送SIGHUP信号,重新加载权限表。
pgrep -n mysql; kill -SIGHUP 12234;
–>(6):无密码登录,再使用mysqladmin重新设置密码。
PS:请参考第20课的视频,那里有详细的记录整个过修改密码的过程。
6,收回用户权限
查看权限:
SHOW GRANTS; SHOW GRANTS FOR user10@'%';
或者直接执行sql命令去mysql数据库下的user表中查看存储着用户的基本权限:
SELECT * FROM mysql.user WHERE USER='user10' AND HOST='%';
使用revoke关键字来收回权限:
REVOKE priv_type[(column_list)]
ON database.table
FROM user[,user]
示例:
REVOKE EXECUTE ON d3307.* FROM user10@'%';
7,数据库用户划分
7.1 普通数据管理用户:
赋予对业务表的查询维护权限即可,授权sql如下:
GRANT SELECT, INSERT, UPDATE, DELETE ON d3307.* TO zengxiaoteng@'%' IDENTIFIED BY '0523';
7.2 开发人员账户:
赋予增删改查的权限,授权sql如下:
GRANT SELECT,INSERT,DELETE,UPDATE ON d3307.* TO huyan@'%' IDENTIFIED BY '0523';
授予创建、修改、删除 MySQL 数据表结构权限。
GRANT CREATE ON d3307.* TO huyan@’192.168.52.11’;
GRANT ALTER ON d3307.* TO huyan@’192.168.52.11’;
GRANT DROP ON d3307.* TO huyan@’192.168.52.11’;
授予操作 MySQL 外键权限:
GRANT REFERENCES ON d3307.* TO huyan@’192.168.52.11’;
授予操作 MySQL 临时表权限:
GRANT CREATE TEMPORARY TABLES ON d3307.* TO huyan@’192.168.52.11’;
授予操作 MySQL 索引权限:
GRANT INDEX ON d3307.* TO huyan@’192.168.52.11’;
授予操作 MySQL 视图、查看视图源代码 权限:
GRANT CREATE VIEW ON d3307.* TO huyan@’192.168.52.11’;
GRANT SHOW VIEW ON d3307.* TO huyan@’192.168.52.11’;
授予操作 MySQL 存储过程、函数 权限:
GRANT CREATE ROUTINE ON d3307.* TO huyan@’192.168.52.11’;
GRANT ALTER ROUTINE ON d3307.* TO huyan@’192.168.52.11’;
GRANT EXECUTE ON d3307.* TO huyan@’192.168.52.11’;
7.3 DBA人员账户
授予普通DBA管理某个MySQL数据库(test)的权限:
GRANT ALL PRIVILEGES ON test TO sysdba@'192.168.52.%';
授予高级 DBA 管理 MySQL 中所有数据库的权限:
GRANT ALL ON *.* TO sysdba@'192.168.52.%';
7.4 数据分析人员只读账号
只需要分配只读的权限:
GRANT SELECT ON d3307.* TO dataquery@'192.168.52.129' IDENTIFIED BY '20150523';
甚至有些用户,可以只分配读取某些表列的权限,如下所示:
GRANT SELECT ON test.* TO dataquery@’192.168.52.%’ IDENTIFIED BY ‘20150523’;
GRANT SELECT(id,uname) ON d3307.t TO dataquery@’192.168.52.%’ ;
示列权限登录操作:
[root@data02 ~]# mysql --socket=/usr/local/mysql3307/mysql.sock -u dataquery -p20150523 -h192.168.52.130 -P3307
Welcome TO the MySQL monitor. Commands END WITH ; OR \g.
Your MySQL CONNECTION id IS 18
SERVER VERSION: 5.6.12-LOG Source distribution
Copyright (c) 2000, 2013, Oracle AND/OR its affiliates. ALL rights reserved.
Oracle IS a registered trademark of Oracle Corporation AND/OR its
affiliates. Other NAMES may be trademarks of their respective
owners.
TYPE 'help;' OR '\h' FOR help. TYPE '\c' TO clear the current input statement.
mysql> SELECT * FROM d3307.t;
ERROR 1142 (42000): SELECT command denied TO USER 'dataquery'@'data02' FOR TABLE 't'
mysql>
mysql> SELECT id,uname FROM d3307.t;
+----+-------+
| id | uname |
+----+-------+
| 1 | a |
+----+-------+
1 ROW IN SET (0.00 sec)
8,权限划分一般原则
数据库一般划分为线上库,测试库,开发库。
8.1对于线上库:
DBA:有所有权限,超级管理员权限
应用程序:分配insert、delete、update、select、execute、events、jobs权限。
测试人员:select某些业务表权限
开发人员:select某些业务表权限
原则:所有对线上表的操作,除了应用程序之外,都必须经由DBA来决定是否执行、已经什么时候执行等。
8.2 测试库
DBA:所有权限。
测试人员:有insert、delete、update、select、execute、jobs权限。
数据分析人员:只有select查询权限
开发人员:有select权限。
原则:DBA有所有权限,而且严格控制表结构的变更,不允许除了dba之外的人对测试环境的库环境进行修改,以免影响测试人员测试。所有对测试库的表结构进行的修改必须由测试人员和DBA一起审核过后才能操作。
8.3 开发库
DBA:所有权限
测试人员:有库表结构以及数据的所有操作权限。
开发人员:有库表结构以及数据的所有操作权限。
数据分析人员:有库表结构以及数据的所有操作权限。
这里大家可以愉快的玩耍了,只要不mysql服务不hang不downtime都OK了。
MySQL用户权限管理
用户权限管理主要有以下作用:
1. 可以限制用户访问哪些库、哪些表
2. 可以限制用户对哪些表执行SELECT、CREATE、DELETE、DELETE、ALTER等操作
3. 可以限制用户登录的IP或域名
4. 可以限制用户自己的权限是否可以授权给别的用户
一、用户授权
mysql> grant all privileges on *.* to 'yangxin'@'%' identified by 'yangxin123456' with grant option;
- all privileges:表示将所有权限授予给用户。也可指定具体的权限,如:SELECT、CREATE、DROP等。
- on:表示这些权限对哪些数据库和表生效,格式:数据库名.表名,这里写“*”表示所有数据库,所有表。如果我要指定将权限应用到test库的user表中,可以这么写:test.user
- to:将权限授予哪个用户。格式:”用户名”@”登录IP或域名”。%表示没有限制,在任何主机都可以登录。比如:”yangxin”@”192.168.0.%”,表示yangxin这个用户只能在192.168.0IP段登录
- identified by:指定用户的登录密码
- with grant option:表示允许用户将自己的权限授权给其它用户
可以使用GRANT给用户添加权限,权限会自动叠加,不会覆盖之前授予的权限,比如你先给用户添加一个SELECT权限,后来又给用户添加了一个INSERT权限,那么该用户就同时拥有了SELECT和INSERT权限。
用户详情的权限列表请参考MySQL官网说明:http://dev.mysql.com/doc/refman/5.7/en/privileges-provided.html
二、刷新权限
对用户做了权限变更之后,一定记得重新加载一下权限,将权限信息从内存中写入数据库。
mysql> flush privileges;
三、查看用户权限
mysql> grant select,create,drop,update,alter on *.* to 'yangxin'@'localhost' identified by 'yangxin0917' with grant option;
mysql> show grants for 'yangxin'@'localhost';
四、回收权限
删除yangxin这个用户的create权限,该用户将不能创建数据库和表。
mysql> revoke create on *.* from 'yangxin@localhost';
mysql> flush privileges;
五、删除用户
mysql> select host,user from user;
+---------------+---------+
| host | user |
+---------------+---------+
| % | root |
| % | test3 |
| % | yx |
| 192.168.0.% | root |
| 192.168.0.% | test2 |
| 192.168.0.109 | test |
| ::1 | yangxin |
| localhost | yangxin |
+---------------+---------+
8 rows in set (0.00 sec)
mysql> drop user 'yangxin'@'localhost';
六、用户重命名
shell> rename user 'test3'@'%' to 'test1'@'%';
七、修改密码
1> 更新mysql.user表
mysql> use mysql;
# mysql5.7之前
mysql> update user set password=password('123456') where user='root';
# mysql5.7之后
mysql> update user set authentication_string=password('123456') where user='root';
mysql> flush privileges;
2> 用set password命令
语法:set password for ‘用户名’@’登录地址’=password(‘密码’)
mysql> set password for 'root'@'localhost'=password('123456');
3> mysqladmin
语法:mysqladmin -u用户名 -p旧的密码 password 新密码
mysql> mysqladmin -uroot -p123456 password 1234abcd
注意:mysqladmin位于mysql安装目录的bin目录下
八、忘记密码
1> 添加登录跳过权限检查配置
修改my.cnf,在mysqld配置节点添加skip-grant-tables配置
[mysqld]
skip-grant-tables
2> 重新启动mysql服务
shell> service mysqld restart
3> 修改密码
此时在终端用mysql命令登录时不需要用户密码,然后按照修改密码的第一种方式将密码修改即可。
注意:mysql库的user表,5.7以下版本密码字段为password,5.7以上版本密码字段为authentication_string
4> 还原登录权限跳过检查配置
将my.cnf中mysqld节点的skip-grant-tables配置删除,然后重新启动服务即可。
Nginx使用的php-fpm的两种进程管理方式及优化
背景
最近将Wordpress迁移至阿里云。由于自己的服务器是云服务器,硬盘和内存都比较小,所以内存经常不够使,通过Linux命令查看后,发现启动php-fpm进程数有20多个,占用了将近1G的内存,整个服务器才1.5G的内存,最后通过对php-fpm进程数优化解决了此问题,服务器多节省出600M的内存,将php-fpm的优化方法和大家分享下。
备注:目前根据nginx、fpm-php进行了内存优化,详情见相关资料
php-fpm优化
1、php-fpm优化参数介绍
他们分别是:pm、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers。
pm:表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。
在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。
下面4个参数的意思分别为:
pm.max_children:静态方式下开启的php-fpm进程数量
pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量
区别:
如果dm设置为 static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数
2、服务器具体配置
对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。
这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。
对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。
因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到,比如8GB内存可以设置为100,
那么php-fpm耗费的内存就能控制在 2G-3G的样子。如果内存稍微小点,比如1G,那么指定静态的进程数量更加有利于服务器的稳定。
这样可以保证php-fpm只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。
因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,
因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M 得到。
比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比如服务器上只是部署php环境的话,比较合适的值在5~10之间。
本服务器配置
1、服务器基本信息:
硬盘:数据盘30G、系统盘20G
内存:1.5G
CPU:双核
系统:CentOS 6.3 64位
带宽:独享2M
2、部署的应用
Git、SVN、Apache、Tomcat、PHP、Nginx、Mysql、JDK
3、优化后的参数
pm = dynamic
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 8
max_children=40 , 每个children平均占用20M-30M内存,children越多,可以同时接受的并发数量越多,一般children的值是网站最高并发数+浮动值,这值再×内存占用,就是你需要用到的内存。
max_requests = N 是指当每个children接受了N次请求以后,就会把自己杀死,然后重新建立一个children。
PV / max_children = 每一个children接受的request次数
比如上面的值是1000,而你定义的是10240,那么fpm要超过10天才能杀死children并重建,这样如果存在内存泄露的话,就会导致进程占用过多的内存而无法释放,从而使fpm的处理能力降低,还会产生一些莫名其妙的错误。
但是如果你把这个值设置的过小,fpm频繁的杀死children并重建,也会导致额外的开销。
最好的优化当然是根据你网站的运行情况,去不断的调试,找到一个平衡点。
whistle-全新的跨平台web调试工具
whistle是基于Node实现的跨平台web调试代理工具,类似的工具有Windows平台上的Fiddler+Willow,基于Java实现的Charles,及公司同事基于Node实现的Livepool等等;whistle与所有的web调试代理工具一样,主要功能也是用于查看、修改HTTP、HTTPS、Websockt的请求响应或者作为HTTP代理服务器,但不同于Fildder+Willow、Charles和Livepool通过断点的方式修改请求响应,whistle采用的是类似配置Hosts方式,通过配置修改请求响应,且提供规则分组功能及通过域名、路径、正则三种匹配方式(系统的hosts配置只支持域名匹配),特别针对终端调试提供了weinre,log等功能,并支持通过Node模块进行扩展。
基本功能
安装启动
安装启动whistle,需要以下四个步骤: 安装node、安装whistle、启动whistle、配置代理。
- 安装Node(建议安装最新版本,LTS版本或当前版本都可以,如果已安装可以忽略此步骤): https://nodejs.org/
安装tnpm():npm install @tencent/tnpm -g --registry=http://r.tnpm.oa.com --no-proxy # Mac、Linux用户可能需要加sudo sudo npm install @tencent/tnpm -g --registry=http://r.tnpm.oa.com --no-proxy
- 安装whistle及在公司网络访问外网需要用到的插件txpac插件:
tnpm install -g whistle @tencent/whistle.txpac # Mac、Linux用户可能需要加sudo sudo tnpm install -g whistle @tencent/whistle.txpac
- 启动whistle
w2 start
whistle默认端口为8899,如果要修改端口号,可以这么启动:
w2 start -p 8888
重启whistle:
w2 restart
关闭whistle:
w2 stop
更多内容请查看命令行帮助:
w2 help
- 配置代理
whistle需要手动配置浏览器代理或者系统代理(代理的ip为whistle所在机器的ip,如果是本机就填127.0.0.1
;端口号为启动时设置的端口号,默认为8899
):
使用方法
安装node、安装whistle、启动whistle、配置代理后可以开始使用whistle,用Chrome浏览器打开whistle配置管理页面
界面相关操作参见界面功能;
配置模式
传统hosts的配置模式:
# 单个host
ip hostname
# 组合host
ip hostname1 hostname2 ... hostnameN
# 例如
127.0.0.1 www.example.com
127.0.0.1 a.example.com b.example.com c.example.com
whistle的配置模式:
# 单个操作
pattern operator-uri
# 如果pattern和operator-uri不同时为域名或路径的一种组合,位置可以调换
operator-uri pattern
# 组合模式
pattern operator-uri1 operator-uri2 ... operator-uriN
# pattern1和operator-uri不同时为域名或路径的一种组合,可以如下配置
operator-uri pattern1 pattern2 ... patternN
其中,pattern可以为:
- 域名:
www.test.com(
所有该域名下的请求都会匹配operator-uri
) - 路径:
http://www.test.com/xxx
(http://www.test.com/xxx
及其子路径的请求都会匹配operator-uri),或不加协议protocol://www.test.com/xxx,protocol
可以为http、https、ws、wss(http://www.test.com/xxx
及其子路径的请求都会匹配operator-uri
) - 正则:
/^https?:\/\/([^\/]+)\/xxx/(http(s)://host:port/xxx
及其子路径的请求都会匹配operator-uri
,且在operator-uri
中可以通过$1, $2, ..., $9
获取url里面的子匹配)
operator-uri
由上述基本功能抽象成的形如protocol://ruleValue
的URI,whistle会根据匹配的operator-uri
的protocol及ruleValue修改请求或响应,protocol和ruleValue的详细内容参见whistle帮助文档及协议列表。
例如:
# 修改www.example.com的响应cors
www.example.com resCors://*
# 或
resCors://* www.example.com
# 同时修改多个自定域名或路径
resCors://* /example\.com/ a.test.com b.test.com
# 修改www.test.com的带端口host、referer和响应的cors
www.test.com 127.0.0.1:8080 referer://http://www.example.com resCors://*
一些例子
- 拦截HTTPS
不开启拦截HTTPS,无法在whistle看到HTTPS和Websockt请求响应的明文,且只能通过whistle对HTTPS和Websockt设置host、代理等有限的操作,要通过whistle完全操作HTTPS、Websocket请求响应,需要开启HTTPS拦截及在系统或浏览器安装whistle的根证书,具体参见:https://avwo.github.io/whistle/webui/https.html
- 配置host
# 传统hosts配置 127.0.0.1 www.example.com # 等价于: www.example.com 127.0.0.1 127.0.0.1 a.example.com b.example.com c.example.com # 支持带端口 127.0.0.1:8080 www.example.com # 等价于: www.example.com 127.0.0.1:8080 127.0.0.1:8080 a.example.com b.example.com c.example.com # 支持通过域名获取host host://www.qq.com:8080 www.example.com # 等价于: www.example.com host://www.qq.com:8080 host://www.qq.com:8080 a.example.com b.example.com c.example.com # 支持通过正则表达式匹配 127.0.0.1:8080 /example\.com/i # 等价于: /example\.com/i 127.0.0.1:8080 127.0.0.1:8080 /example\.com/ /test\.com/ # 支持路径匹配 127.0.0.1:8080 example.com/test # 等价于: example.com/test 127.0.0.1:8080 127.0.0.1:8080 http://example.com/index.html https://www.test.com/test
完整功能参见协议列表。
- 修改请求
以下功能都支持通过域名、路径、正则匹配方式,为方便只以域名匹配方式为例,其它同理:
# 修改url参数 www.qq.com urlParams://E:\test\params.json # 请求方法 www.qq.com method://post # 添加请求头 www.qq.com reqHeaders://(x-a=1&x-b=a%20b) # 修改referer(修改referer快捷方法) www.qq.com referer://http://ke.qq.com/ # 修改cookie(修改请求cookie快捷方法) www.qq.com reqCookie://{reqCookie.json} # 修改请求表单 www.qq.com params://{form.json}
JSON对象可以存在本地文件,或存在界面的Values,也可以内联到Rules配置里面,具体参见实现原理、数据格式。
完整功能参见协议列表。 - 修改响应
以下功能都支持通过域名、路径、正则匹配方式,为方便只以域名匹配方式为例,其它同理:
# 修改响应状态码 www.qq.com statusCode://500 # 请求不会发送到后台服务器,可以用来模拟4xx、5xx请求 www.qq.com replaceStatus://404 # 请求返回后再修改statusCode # 添加响应头 www.qq.com resHeaders://(x-res-a=1&x-res-b=a%20b) # 修改响应类型(修改响应类型的快捷方法) www.qq.com resType://text/plain # 或者: www.qq.com resType://text # 请求替换 www.qq.com https://www.baidu.com # 本地替换(windows中目录分割符可以用`\`,也可以用`/`) www.qq.com file://E:\xxx # 等价于: file://E:/xxx www.qq.com # Mac或Linux www.qq.com file:///User/xxx/test # 如果要让本地没有对应文件的请求继续请求线上,可以采用 www.qq.com xfile://E:\xxx # 本地替换jsonp www.qq.com tpl://E:\xxx.json # xxx.json: {callback}({"ec": 0}) www.qq.com xtpl://E:\xxx.json # xxx.json: {callback}({"ec": 0}) # 注入html、css、js(whistle会自动根据响应类型封装后注入) www.qq.com html://htmlFile www.qq.com css://cssFile www.qq.com js://jsFile
JSON对象或注入的文本可以存在本地文件,或存在界面的Values,也可以内联到Rules配置里面,
- 设置代理
以下功能都支持通过域名、路径、正则匹配方式,为方便只以域名匹配方式为例,其它同理:
# http代理 www.qq.com proxy://127.0.0.1:8888 # 等价于: proxy://127.0.0.1:8888 www.qq.com # 同时设置多个 proxy://127.0.0.1:8888 www.qq.com /google/ /facebook/ # socks代理 www.qq.com scoks://127.0.0.1:1008 # 等价于: socks://127.0.0.1:8888 www.qq.com # 同时设置多个 socks://127.0.0.1:1008 www.qq.com /google/ /facebook/ # pac脚本 # 设置办公网pac脚本(如果安装了whistle.txpac,则无需设置) /./ pac://http://txp-01.tencent.com/proxy.pac # 设置办公网pac脚本(如果安装了whistle.txpac,则无需设置) /./ pac://http://txp-01.tencent.com/proxy_devnet.pac
完整功能参见协议列表。
- 移动端调试
打开whistle配置界面右上角的Online按钮获取当前whistle的ip和端口,移动设备根据相应的ip和端口配置代理(确保移动设备和PC要在同一网段);配置完后如果还是无法访问,可能需要关闭防火墙或者设置白名单。
# weinre(调试网页DOM结构) www.qq.com weinre://test # log(输出网页console打印的日志) www.qq.com log://{test.js}
完整功能参见协议列表。
- 其它功能
以下功能都支持通过域名、路径、正则匹配方式,为方便只以域名匹配方式为例,其它同理:
# 禁用缓存 www.qq.com disable://cache # 忽略规则 www.qq.com filter://rule|host|https
完整功能参见协议列表。
插件扩展及应用
whistle支持通过Node模块的方式扩展功能,具体参见:插件开发。
插件的一些应用:
- HTTP代理服务器功能:imweb本地代理imwebproxy(里面的Pb和CMEM的功能分别对应两个whistle插件whistle.imwebproxy、whistle.cmem)
- 扩展协议功能:imweb前端本地调试环境whistle.imwebenv
- 扩展界面功能:whistle.websocket
用户反馈
有问题可以先查whistle帮助文档;
如果还是无法解决或者有建议、贡献代码等可以使用下面任一种方式:
最后,如果whistle对你有帮助,给项目加个Star: https://github.com/avwo/whistle
手机无须ROOT和修改hosts在本地测试安卓和H5应用
开发手机APP和 H5 应用经常需要在本地和线上环境分开测试,一般想到的操作都是修改hosts,我也一直这么干的,但手机上修改hosts是需要 ROOT权限的,这样太过麻烦,还有变砖头的风险,而且有些手机根本不能ROOT的。
忙和了一下午,总算完全达到预期,有点收获,下面说说重点,主要是两个:
- 本地 DNS 服务
- 网络代理
做个本地的 DNS 服务,有点像DNS劫持的感觉,这个是一劳永逸的办法
首先,你需要一个Linux 虚拟机或一台服务器
我下面的配置使用的 Centos7.0 系统,不通版本可能有些差别的。
一、安装 DNSMasq
# 安装
yum install dnsmasq -y
service dnsmasq start
# 编辑配置参数
vi /etc/dnsmasq.conf
# 查找 resolv-file 去掉注释符,添加文件路径
# 表示 dnsmasq 会从这个指定的文件中寻找上游dns服务器
resolv-file=/etc/resolv.conf
# 查找 no-hosts , 确保前面有 # 井号的(表示被注释掉的),否则 hosts 配置不起作用
# 监听的IP
listen-address=127.0.0.1,192.168.0.103
strict-order
addn-hosts=/etc/hosts
cache-size=32768
# 配置到网卡参数,centos7 和 6 的网卡名是不同的,用ifconfig 查看替换下
# centos 7 以前
vi /etc/sysconfig/network-scripts/ifcfg-eth0
# centos7 不一定相同
vi /etc/sysconfig/network-scripts/ifcfg-enp3s0
# 最多只能写3个,注意原配置中若存在则需要调整
DNS1=127.0.0.1
DNS2=192.168.0.103
DNS3=202.101.172.35
#DNS3=202.101.172.47
# 保存后, resolv.conf 文件内容会自动更新的
cat /etc/resolv.conf
# 查看DNS是否已添加进去
# 注册服务
chkconfig dnsmasq on
# 重启
/etc/init.d/dnsmasq restart
# centos7 下使用的命令
systemctl restart dnsmasq
# 检查是否安装成功
netstat -tunlp|grep 53
yum install bind-utils
# 测试是否启用 DNS 配置
dig m.ai9475.com
# 编辑 hosts 文件,配置本地 DNS 劫持域名 IP 的映射关系
vi /etc/hosts
192.168.0.105 m.ai9475.com
这样服务端的 DNS 劫持就基本配置好了
下一步还需要在手机上设置 DNS 对应的 IP,
打开 网络设置 -> wifi连接 -> 修改网络 -> 高级 -> 静态IP方式 -> 修改第一个NS的IP即可,第二个可用正常的公网DNS
如下图:
最后注意:修改 hosts 之后,需要重启 dnsmasq 还有 network 最好也重启
二、使用本地网络代理
这个方法比较有局限性,对 H5 网页应用还可以,对部分原生 APP 可能无效哦,但有个好处是配合一些软件可以做前端的抓包查看,对测试接口之类的查看数据很有用
我用到过两个软件,
1. Fiddler
这个软件使用很方便,网上有很多介绍,这里就不说了,但据说仅支持 windows 平台
2. Whistle
这个是开源的项目,跨平台,我目前用的是这个,配置也很简单
下面简单说下如何安装使用
依赖 Nodejs,所以先安装 nodejs
我这里图方便,就用 yum 的方式安装了
curl --silent --location https://rpm.nodesource.com/setup_4.x | bash -
yum install -y nodejs
下载速度可能有些忙,10KB/s 下载了好半天才下完,耐心等吧
npm install cnpm -g --registry=https://registry.npm.taobao.org
cnpm install -g whistle
w2 start
这样就完成了。。。
查看代理请求数据,就是抓包数据
http://127.0.0.1:8900
http://127.0.0.1:8899
:8899 是代理的端口,但也可以查看抓包数据
两个端口都可以看,下图看看效果先(图片可以直接拖拽查看大图)
其他的就直接看官方说明文档吧,中文的,很清晰明了
https://avwo.github.io/whistle/install.html
后面就是手机上操作和上面的 DNS 设置是同一个地方
打开 网络设置 -> wifi连接 -> 修改网络 -> 高级 -> 代理(手动配置) -> 填写局域网中服务器的IP,如192.168.0.103,端口 8899
如下图:
最后就是修改服务器上的 hosts,参考上方 DNS 配置 hosts 的操作是一样的
差不多就这样设置好了。
最后需要注意的就是清除手机上的缓存,一定要关闭浏览器和应用,清理内存后再打开,否则手机上的 DNS 和网络设置可能没有切换过来的。
以上配置我都是在 Android 设备上测试的, iOS 没有应用可测试,不过按理说应该是一样的效果的。
虽然代理的方法对有些APP 不一定有用,但建议还是 DNS 和 代理一起上吧。。。就这样了。
百度文字转语音免费接口使用实例
1、接口
http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=2&text=你要转换的文字
上述接口的url,在浏览器上直接打开,即可听到文字转换后的语音。
lan=zh:语言是中文,如果改为lan=en,则语言是英文。
ie=UTF-8:文字格式。
spd=2:语速,可以是1-9的数字,数字越大,语速越快。
text=**:这个就是你要转换的文字。
2、js调用
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Document</title>
- </head>
- <body>
- <form action="" method="post">
- <table align="center">
- <tr>
- <td><input type="text" id='val' placeholder='你要装换的文字'></td>
- <td><input type="button" value="提交" onclick="fun()"></td>
- </tr>
- </table>
- </form>
- </body>
- </html>
- <script type="text/javascript">
- function fun()
- {
- var val=document.getElementById("val").value;
- var zhText = val;
- zhText = encodeURI(zhText);
- document.write("<audio autoplay=\"autoplay\">");
- document.write("<source src=\"http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=2&text="+ zhText +"\" type=\"audio/mpeg\">");
- document.write("<embed height=\"0\" width=\"0\" src=\"http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&spd=2&text="+ zhText +"\">");
- document.write("</audio>");
- }
- </script>
优雅的使用 phpStorm 开发工具
按照惯例依然是从百科上复制一条简介: PhpStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具。PhpStorm可随时帮助用户对其编码进行调整,运行单元测试或者提供可视化debug功能和智能HTML/CSS/JavaScript/PHP编辑、代码质量分析、版本控制集成(SVN、GIT)、调试和测试等功能。另外,它还是跨平台。在Windows和MacOS下都可以使用。PhpStorm-让开发更智能,而不是更困难。
听说phpStorm 10支持php7呃
优点
- 跨平台。
- 对PHP支持refactor功能。
- 自动生成phpdoc的注释,非常方便进行大型编程。
- 内置支持Zencode。
- 生成类的继承关系图,如果有一个类,多次继承之后,可以通过这个功能查看他所有的父级关系。
- 支持代码重构,方便修改代码。
- 拥有本地历史记录功能(local history功能)。
- 方便的部署,可以直接将代码直接upload到服务器。
总之它很牛逼就是了,什么都能干
快捷键
phpStorm有非常非常多并且好用的的快捷键,我下面就举一些经常用的的快捷键演示,还有一些不常用的就不举例了,绝对能提高你开发的效力率...
(Windows与Mac类似,只要把 command
键换成 ctrl
)
查询相关
command + f
查找当前文件command + r
查找替换command + e
打开最近的文件command + shift + o
快速查询文件command + shift + f
关键字查找,更强大的查询器(机器不好的,最好还是先确定一下目录)command + shift + r
高级替换command + alt + b
找到当剪类的所有子类alt + shift + c
查找最近修改的文件alt + f7
直接查询选中的字符ctrl + f7
文件中查询选中字符command + 鼠标点击
跳到类或方法或变量等声明处command + shift + tab
切换tab页文件command + shift + +,-
展开或缩起command + .
折叠或展开选中的代码
自动代码
alt + 回车
导入包,自动修正command + n
快事为每个成员属性生成 getter 及 setter 方法ctrl + i
快速生成插入魔术方法ctrol + o
复写父类方法command + alt + l
对当前文件进行格式化排版command + d
复制当剪行command + /
// 注释command + shift + /
/ / 注释
以 command + n
举个例子
我创建了一个 Person
类在 /Entity/
目录下,然后我设置一些私有的属性如下代码:
namespace Entity;
class Person
{
private $sign = '';
private $name = '';
private $age = 0;
private $work = '';
private $sex = '女';
}
然后咱们使用 command + n
在弹出来的窗口选择"PHPDoc Blocks..." 如下图:
再再弹出的窗口选择所有属性再点"OK":
namespace Entity;
/**
* Class Person
* @package Entity
*/
class Person
{
/**
* @var string
*/
private $sign = '';
/**
* @var string
*/
private $name = '';
/**
* @var int
*/
private $age = 0;
/**
* @var string
*/
private $work = '';
/**
* @var string
*/
private $sex = '女';
}
然后它就对刚刚所选择属性加上了注释...... 是不是灰常神奇。
ok,咱们继续,再次使用 command + n
键选择 Contructor...
弹出需要进行传参赋值的属性:
/**
* Person constructor.
* @param string $sign
*/
public function __construct($sign)
{
$this->sign = $sign;
}
如果不选择的话将不需要对成员属性进行设置。
然后咱们再来看看其他功能,比如"Implement Methods..."这个是快速生成魔术方法。
通常咱们设置、获取一个成员属性时最好不要直接使用 $person->name = $name
这种方式进行设置参数或取得参数值, 建议是对每个属性都开放一个 getter
跟 setter
方法,这样可以很方便得对传进或传出去的值进行处理,这就是上面我为什么要把成员属性设置置为私有的原因之一
同样的 command + n
选择"Getters and Stetters" 然后选择所有属性,它就会把所有的属性设置 getter
及 setter
方法,这里要注意的是 Person
的 sign
是唯一的,不可进行修改,所以咱们要把设置 sign
的方法去掉。注意: 最好 setter
方法设置完后返回当剪对象,这样的话咱们就可以连写了并且phpStorm的提示还相当友好 下面有例子:
/**
* @return string
*/
public function getSign()
{
return $this->sign;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return int
*/
public function getAge()
{
return $this->age;
}
/**
* @param int $age
* @return $this
*/
public function setAge($age)
{
$this->age = $age;
return $this;
}
/**
* @return string
*/
public function getWork()
{
return $this->work;
}
/**
* @param string $work
* @return $this
*/
public function setWork($work)
{
$this->work = $work;
return $this;
}
/**
* @var string
*/
private $sex = '女';
/**
* @return string
*/
public function getSex()
{
return $this->sex;
}
/**
* @param string $sex
* @return $this
*/
public function setSex($sex)
{
$this->sex = $sex;
return $this;
}
连写的例子:
use Entity\Person;
$person = new Person();
$person->setName("蛋蛋")
->setAge(17)
->setWork('student');
最后再演示一个快速复写被继承类的功能。咱们新建一个 Man
类,然后继承 Person
类,上面的Person类缺省是女性别,所以我们需要重写它并且加上"中国男人"。同样的使用 command + n
打开快捷窗口选择 "Override Methods..." 弹出来可被复写的方法:
然后咱们选择 getSex
跟 setSex
方法,然后确定,在 Man
方法下生成以下方法。
namespace Entity;
/**
* Class Man
* @package Entity
*/
class Man extends Person
{
/**
* @return string
*/
public function getSex()
{
return parent::getSex(); // TODO: Change the autogenerated stub
}
/**
* @param int $sex
* @return $this
*/
public function setSex($sex)
{
return parent::setSex($sex); // TODO: Change the autogenerated stub
}
}
咱们把 return parent::getSex()
跟 return parent::setSex( $age )
删除掉,不需要这样,然后改成如下模式。
/**
* @return string
*/
public function getSex()
{
if ( ! mb_strpos(parent::getSex(), "中国") )
return "中国".parent::getSex();
return parent::getSex();
}
/**
* @param int $sex
* @return $this
*/
public function setSex($sex)
{
if( ! mb_strpos($sex, "中国") )
$sex = "中国".$sex;
return parent::setSex($sex);
}
碉堡了有木有。
工具类等
看起来好多的样纸,我懒,不想讲可不可以?我就挑几个好不好?
- 连拉ssh 照着配就行了,很简单
- composer 这个也很明了吧,不多说了,平时咱们都是通过命令行来实现的
- vagrant 这个phpstorm 10集成了vagrant,介于咱们自己已经搭建好了自己的vagrant环境,就不使用phpstorm所集成的啦
参考: 《使用Virtual Box和Vagrant搭建开发环境》
Database 工具
phpStorm所集成的database工具十分强大,当然它还有单独的database工具叫做: DataGrip,当然需要独立购买,咱们phpStorm有集成,就使用它好啦哈哈....(咱们的PhpStorm可是花钱买的,请支持正版)
Database工具一般在右侧栏,如果没有的话搜一下就好了,多简单的事儿呀...
开始创建一个数据库连接吧...
选择如上图的那个"+"号,然后选择 Data Source
数据来源,再选择数据库类型,一般咱们都是使用mysql吧,这次咱们试试新的,比如 SQLite
选择 sqlite
数据文件的地址,然后选择驱动,如果没有的话得先下载安装sqlite的驱动插件,这个很简单,在Driver下有提示,照做就是了...
咱们先看一下mysql的配制吧...
mysql的也非常简单,如果需要ssh/ssl连接的话,需要在SSH/SSL选项卡上配配地址入连接密码或sshkey...
配制好了,打开选择的数据库:
上图是连接的数据库的表及表字段信息... 来演示一下查询... 点击那"QL"样的dos窗口图标会弹出一个tab页,咱们可以在这里写sql语句。
咱们查询 User
表下的所有数据,可以看到会有相当提示,这是相当的好使啊...查询完成后在下面的 Database Console
上会有显示表数据,可对它进行修改,等等操作增加数据也可以。
快捷键 command + alt + l
不但对代码进行格式化,也sql语句也是非常有效的,如上图。
在"Database Console"栏上点"Output"选项卡可以查看sql语句执行的情况、记录及所消耗的时间等等信息...
command + 回车
执行sql语句或执行选中的sql语句
关于database工具的用法还有很多很多,我就不一一讲解了,大家可以自己慢慢去研究,真的非常好用
CVS 和 Git
command + k
command + shift + k
关于FTP的配制,由于我不推荐使用,所以这里就不多说啦!
都到这了,那咱们就说说在phpStorm上如何使用git工具吧
算了,还是举一个例子吧,配辣么多太累了,一会我看下有没有已经配好的,如果有的话一会拉出来截个图看看就行吧,反正现在svn用得也比较少了,还是git用得爽,分布式嘛,离线嘛,多好...关于 svn -> git 可以参考我之前写的一篇文章
从git服务器上把代码抓到本地
选择 CVS -> Checkout from Version Control -> Git
在弹出的窗口输入自己的git仓库信息:
注意 conle 的时间如果没有设置你的github账号的话可能会提示你输入账号信息,咱们输入就行了。如果需要修改的话则在设置里面进行修改,咱们可以使用 command + ,
打开"Preferences" 然后找到"Version Control"选项目的"GitHub"进行设置,还有"Git"路径。
从mster创建分支
创建分支以通过命令行进行创建,咱们可以通过phpstrom的窗口进行创建,如下:
这个东西在右下角,"Git:master" 然后弹出上面窗口选择"New Branch" 然后输入新分支的名称就好了,它会自动切换到新分支下。
是不是超级简单呀...
提交代码至远程分支
当咱们修改完代码后,咱们需要把代码提交到远程分支上,使用快捷键 command + k
提交相当修改后的代码,双击文件可以进行对比。在"Commit Message"写上修改的东西然后点提交,这时就把代码提交到本地分支上了。
不使用快捷键的话,可以使用"CVS -> Commit Changes"提交,也会弹出下面窗口...

提交到本地分支后,咱们需要把代码推到远程分支上,所以需要使用快捷键: command + shif + k
提交远程分支...

也可以使用"CVS -> Git -> Push"进行提交...效果是一样的
注意svn木有 command + shift + k
这一步
合并分支
分并非常简单,只要选择需要合并的分支,然后merge就行了,如下图:
这样就合并完成,当然,如果有冲突的话会提交有冲突,并让你解决,如果没有的话就直接合并成功了...然后就可以push了......
Compare
是对合并的分支进行对比...
使用svn...
灰常抱歉,我电脑上木有找到相关Svn项目的代码,就不多说了...
安装插件
这里讲一个javascript 的安装,使用快捷键 cmd + ,
打开 Preferances
安装 JavaScript 插件
Languages & Frameworks
-> Javascript
-> Libraries
选择add需要的框架
安装symfony2插件,搜索插件,然后点install
然后重启phpStorm 就完事了....
注意
- 灰色+波浪线: 变量未使用
- 黄色波浪线: 变量未名单词拼写问题
- 红色波浪线: 变量未定义
- 还有好多我就不一一举例了,可能是因为我代码写得太好,出错的东西比较少吧...
右边栏出现红色,这点是必须要杜绝的,好的代码不应该出现红色的任何提示...一旦出现一定要马上解决,好的代码不应该出现一个黄、红色的提示。
TODO 表示待办事件,当提交到vcs、svn或git的时候,会提示是还有未处理的事件,需要确认提交。
架构师的能力模型(图)
这四幅图不见得能阐述架构师能力的全部,但我尽量给出范围和有用的建议,希望能对大家有所启发和裨益。
图一:本能力模型的基本角度。与爱立信人力模型做了一个对比,请注意没有涉及到与“个人内在素质”相关的任何部分。所以类似于诚信、坚韧、耐心等等这类素质不在讨论的范围之内。
图二:个人特性
图三:技术技能
图四:对“技术技能”中“学会平衡设计”的补充
Worktile v5.2.0 更强大的角色管理,权限控制及角色审批
Worktile v5.2.0迎来一系列重大更新。本次更新主要包含以下三点:
1.新增角色管理:自定义系统角色、权限和数据范围;
2.更精细的权限控制,自定义项目、日历、网盘操作权限;
3.新增角色审批,分条件审批流程,让企业审批流程更可控。
一、新增角色管理 :自定义系统角色、权限和数据范围
什么是角色?
在各行各业的不同企业中,面对不同的职能、岗位和业务线的要求,需要配置不同的角色、权限,以及数据范围。
一般情况下,企业成员的操作权限、查看数据范围是通过角色来控制的,例如企业所有者具备最高的功能权限,可以查看公司全部资源,而普通成员只能操作被限定范围内的功能权限和数据。
在Worktile中,系统预定义了企业所有者、管理员、部门主管、成员4个默认角色,同时还根据职务预置了财务、出纳、客服、采购、人事、行政、HR等角色。在角色管理模块中,企业可根据公司实际职能、岗位、业务线需求设置相应的角色,并为其配置相应的角色成员、权限和数据范围。
如何进行角色管理?
1. 新建角色组、角色,并添加角色成员
点击“新建角色组”创建新的角色组,然后在角色组下添加相应的角色。设置好后,添加角色成员。企业成员可以被赋予多个角色,若一个成员归属于多个角色,则此成员的权限为所属角色具有的权限合集。
为了更好地帮助企业进行角色权限配置,系统预定义了企业所有者、管理员、部门主管、成员4个默认角色,同时还根据职务预置了财务、出纳、客服、采购、人事、行政、HR等角色。除了默认角色中的“所有者”的权限和数据范围默认为全部且不可修改,以及部门主管仅显示角色成员外,其余角色权限皆可修改。
默认的角色:所有者、管理员、部门主管和成员,具体说明如下:
- 企业所有者:仅限1人,具有企业最高的功能权限和最全的数据可见范围;
- 管理员:可设置多名员工,可设置其对应的功能权限,默认情况下具有大部分功能操作权限和本人相关的数据范围,可按企业所需自行编辑修改;
- 部门主管:仅显示出企业组织架构中已设置的“部门主管”。如需添加“部门主管”,请到【成员管理】页面,在【编辑部门】中设置“部门主管”,设置完成后信息自动同步;
- 成员:系统默认角色,设置除了所有者、管理员、主管外的企业成员应有的基础功能权限和数据可见范围。
系统按照常见的“职务”预置了部分角色,如财务、出纳、客服、采购、人事、行政等,按照“区域”预置了东区、西区、南区、北区、华中区,企业可增删改此类角色,并根据自身所需,配置相应的角色成员、功能权限和数据可见范围。
Tips:
1. 若一个成员归属于多个角色,则此成员的权限为所属角色具有的权限合集;
2. 系统默认角色“企业所有者”仅有1人,如需更换企业所有者,需要在企业后台【企业设置】页面中通过【转让企业】来更换。
2. 设置不同角色的功能权限范围
添加完角色成员后,可继续配置角色对应的功能权限。此处的功能权限包含系统所有的基础功能权限、应用管理权限以及其他后台模块的管理权限,勾选权限并保存即可设置成功。
Tips:
1. 角色功能权限的详细说明,请戳链接:角色管理
2. 在功能权限中配置了审批、考勤、销售、简报的操作权限后,同时应配置一下此角色可操作的数据范围。
3. 设置数据的查看、管理范围
在设置了角色的成员、权限后,还可以针对审批、考勤、简报、销售等OA应用来设置该角色的查看、管理数据范围。
通常,企业数据面对不同职务的员工需要有不同的数据查看、管理范围,如销售总监可以查看全部销售、客户、合同的统计,但是销售组长只能查看本组成员的数据,而普通的销售人员仅能查看与本人相关的数据。
通过设置角色的数据查看、管理范围,可以有效地解决这类问题。常见的查看数据范围主要分为:本人相关、本部门、本部门及下属部门、全部。除了企业所有者外,其他所有角色默认的数据范围均为本人相关,可根据不同角色自行调整修改。
设置范围后,在产品中查看简报、销售、审批、考勤数据时,不同的角色仅能查看、管理所设定范围内的数据。
- 本人相关:仅限于本人创建、参与的数据;
- 本部门:本角色可查看用户所属部门数据;
- 本部门及下级部门:本角色可查看用户所属部门,及其子部门数据;
- 全部:本角色可查看全公司的数据。
二、更精细的权限控制,自定义项目、日历、网盘操作权限
一个进行中的项目,不同的成员常常需要具备不同的操作权限,比如项目管理员需要具备项目最高权限,而有些成员需要具备任务相关的操作权限,另外一些成员只需要能查看即可;在企业网盘中,通常不同的成员需要具备不同的管理、编辑、上传、下载、只读权限。
不同行业不同企业有各自的需求,在本次产品升级中,Worktile 支持了更精细化的权限控制,支持自定义项目、日历、网盘权限,根据需要添加权限,然后在项目、日历、私有文件夹成员中配置成员相应的权限。
下面我们将以【自定义项目权限】为例,展现本次更新后Worktile 所具备的更加精细化的权限控制。
自定义项目权限
1. 如何自定义项目权限?
进入后台任务应用设置,点击“权限设置”,在此,企业可新建、编辑、删除项目权限。在【新建权限】页面勾选对应可操作的权限,企业即可根据业务需要配置项目成员所需的权限。
在项目权限列表中,系统预置了管理、编辑、只读的权限,“管理”权限为项目最高权限,包括项目的删除、归档、成员增删等;“编辑”仅限于对任务的操作权限;“只读”仅限于查看、评论任务。
其中“编辑”权限默认为成员初始加入私有项目时的权限,管理者可自行调整“编辑”所应具备的操作权限。
2. 如何在项目中配置成员不同权限?
在项目中,打开【项目设置】,在【成员管理】页面对每个成员配置相应的权限,如下图所示。配置完成后,项目成员仅能进行各自权限限定范围内的任务操作。
同理,【自定义日历权限】、【自定义网盘权限】同【自定义项目权限】,可在后台自定义权限,可在日历、文件夹设置的【成员管理】中给成员配置相应操作权限。
三、新增角色审批,分条件审批流程,让企业审批流程更可控
分条件审批流
进入企业,选择【应用管理-审批】,点击【模板管理-审批人设置】。审批流程分为自由流程、固定流程、分条件审批。
- 自由流程:企业成员可自行添加相应的审批人;
- 固定流程:企业成员需按照规定好的审批人流程进行审批;比如企业固定报销流程为“张三—李四”,设定固定流程后,企业成员填写审批单时,审批人已默认设置为“张三—李四”,且成员自己不能修改;
- 分条件审批:当表单包含以下类型字段时才可以使用分条件审批:「数字字段」、「下拉选项」;申请人提交的表单会根据相应字段进入对应的审批流程。
Tips:
1. 若审批环节中某角色有多个成员,则到此角色审批环节时,会审批单会通知对角色中所有人。当角色中的任意一人同意或拒绝,则此审批环节完成;
2. 部门主管为特殊角色,多次点击时会按层级叠加显示。1级主管=申请人所在部门主管,2级部门主管=申请人所在部门的父部门的部门主管,以此类推。
若审批环节中某部门有多个主管,则到此审批环节时,此审批单会通知该级“部门主管”中的所有人。当任意一个主管同意或拒绝后,此审批环节完成。
角色审批
角色可以帮助企业规范审批流程,提高流程处理速度,减轻管理员后台配置的负担。预置好审批角色后,工作流程可在审批流程中固定下来,人员离职变动等,流程也无需再单独修改。同事,因为审批流程已设置好,并且清晰直观,即使新人不熟悉流程,也可直接提交审批。
预置知会人及通知方式
在本次升级中,还新增了预置审批知会人,可选择具体员工或者角色为知会人,如某些与财务相关的流程必须知会 法务[角色]或 财务[角色]。
设置后,申请人提交此表单时,表单中会默认添加好后台预置的知会人,无需申请者手动添加。这样,即使相关流程知会人的职务发生变化时,流程也无需再维护修改。
此外,知会的通知方式分为三种:仅全部同意后通知,仅发起时通知,发起时和全部同意后均通知,企业可根据需要自行设置调整。
Worktile Web v5.2.0 其他更新内容如下:
1. 审批同意或拒绝时可附加审批意见;
2. 审批新增审批汇总功能;
3. 全局项目分组,设置后可同步至所有企业成员。