2019年11月

高阶函数英文叫Higher-order function。什么是高阶函数?我们以实际代码为例子,一步一步深入概念。

变量可以指向函数
以Python内置的求绝对值的函数abs()为例,调用该函数用以下代码:

abs(-10)
10

但是,如果只写abs呢?

abs

<built-in function abs>
可见,abs(-10)是函数调用,而abs是函数本身。

要获得函数调用结果,我们可以把结果赋值给变量:

x = abs(-10)
x

10
但是,如果把函数本身赋值给变量呢?

f = abs
f

<built-in function abs>
结论:函数本身也可以赋值给变量,即:变量可以指向函数。

如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:

f = abs
f(-10)

10
成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。

函数名也是变量
那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

如果把abs指向其他对象,会有什么情况发生?

abs = 10
abs(-10)

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!

当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。要恢复abs函数,请重启Python交互环境。

注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。

传入函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

一个最简单的高阶函数:

def add(x, y, f):

return f(x) + f(y)

当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:

x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11

mysql一主一从
主从复制工作过程
mysql主从复制依赖于二进制日志;用户发送请求更新数据,数据库更新后生成二进制日志,主节点把新生成的二进制日志通过dump线程通过网络发送给从节点;从节点的io thread负责接收二进制日志,把二进制日志的内容放到中继日志中(relay log);从节点通过sql thread线程执行中继日志中的sql语句,实现更新本机的数据库数据
主从复制原理图如下:

主节点配置:
[root@centos7 ~]# vim /etc/my.cnf
[mysqld]
innodb-file-per-table #指定存储引擎
server-id=7 #设置区分主从的id;mysql配置文件不区分"_"和"-"
log-bin #主节点必须启用二进制日志,主从复制基于二进制日志
[root@centos7 ~]# systemctl retart mariadb
[root@centos7 ~]# mysql
MariaDB [(none)]> grant replication slave on . to repluser@'192.168.38.%' identified by 'centos'; #创建并授权一个账号,用于从节点连接主节点复制数据用,授权权限为从节点复制
从节点配置
[root@localhost ~]$ vim /etc/my.cnf #修改必须指定的配置
[mysqld]
server-id=17 #指定id号,用以区分是本机通过sql语句更新的数据库还是通过主节点的二进制日志更新的数据库
read-only=on #设置为只读
[root@localhost ~]$ systemctl restart mariadb
[root@localhost ~]$ mysql
MariaDB [(none)]> CHANGE MASTER TO
MASTER_HOST='192.168.38.7', #指定主节点的IP
MASTER_USER='repluser', #指定连接主节点的用户名
MASTER_PASSWORD='centos', #指定连接主节点的密码
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000001', #指定从主节点的哪个二进制日志开始复制,指定二进制日志文件名
MASTER_LOG_POS=245; #指定从主节点二进制日志位置开始复制的位置
MariaDB [(none)]> start slave; #开启从节点的io thread和sql thread两个线程

MariaDB [(none)]> show processlist; #可以查看当前进程列表
MariaDB [(none)]> show slave statusG #可以查看从节点的相关信息
将原有的一台mysql,实现主从复制
实验目的:前期访问量小,一台Mysql服务器足以应付;后期发现服务器压力大,需要增加一台从服务器
主节点配置:
[root@centos7 ~]# vim /etc/my.cnf #配置文件中必须开启的
[mysqld]
log-bin
server-id=7
[root@centos7 ~]# systemctl restart mariadb
[root@centos7 ~]# mysqldump -A -F --single-transaction --master-data=1 > /data/date +"%F-%T"_all.sql #做完全备份;针对所有数据库、开启二进制日志、开启事务、生成新的日志并且记录二进制日志位置
[root@centos7 ~]# less /data/2019-11-25-17:44:54_all.sql #完全备份里面会有一行:CHANGE MASTER TO MASTER_LOG_FILE='mariadb-bin.000002'(二进制日志名字根据配置文件中自己的设置), MASTER_LOG_POS=245(位置根据当时自己服务器的情况);这条记录之前的数据都做了备份,这条记录之后的都没有做备份
[root@centos7 ~]# scp /data/2019-11-25-17:44:54_all.sql 192.168.38.47:/data/ #把备份发送给从节点
[root@centos7 ~]# mysql
MariaDB [(none)]> grant replication slave on . to repluser@'192.168.38.%' identified by 'centos'; #创建授权用户
从节点配置
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
server-id=27
read-only=on
[root@localhost ~]# systemctl restart mariadb
[root@localhost ~]# vim /data/2019-11-25-17:44:54_all.sql #编辑备份文件
CHANGE MASTER TO #在change master to和MASTER_LOG_FILE之间加上主节点的各种信息;备份文件中加入之后,就不用在mysql数据库中进行添加了
MASTER_HOST='192.168.38.7',
MASTER_USER='repluser',
MASTER_PASSWORD='centos',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000002', MASTER_LOG_POS=245;
[root@localhost ~]# mysql < /data/2019-11-25-17:44:54_all.sql 把备份导入mysql数据库
[root@localhost ~]# mysql
MariaDB [(none)]> start slave; #开启从节点的两个线程
级联复制
模型:

最少准备三台机器,一台主,两台从
主节点配置:
[root@centos7 data]# vim /etc/my.cnf
[mysqld]
log-bin
server-id=7
[root@centos7 data]# systemctl restart mariadb
[root@centos7 data]# mysqldump -A -F --single-transaction --master-data=1 > /data/date +%F-all.sql #完全备份
[root@centos7 data]# scp 2019-11-25-all.sql 192.168.38.37:/data
[root@centos7 data]# scp 2019-11-25-all.sql 192.168.38.47:/data
[root@centos7 data]# mysql
MariaDB [(none)]> grant replication slave on . to repluser@'192.168.38.%' identified by 'centos';
中间的从节点配置:
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
log-bin #中间从节点必须开启二进制日志;中间从节点相当于又是从又是主
server-id=17
log-slave-updates #必须开启;把本机中继日志中的sql语句执行完毕后,在本机生成二进制日志;不加这项,默认中继日志中的sql语句是不会产生二进制日志
read-only
[root@localhost ~]# systemctl restart mariadb
[root@localhost ~]# vim /data/2019-11-25-all.sql #在数据库备份中进行配置
CHANGE MASTER TO
MASTER_HOST='192.168.38.7',
MASTER_USER='repluser',
MASTER_PASSWORD='centos',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000003', MASTER_LOG_POS=245; #根据当时的备份自动配置
[root@localhost ~]# mysql < /data/2019-11-25-all.sql #导入数据库备份
[root@localhost ~]# mysql
MariaDB [(none)]> start slave;
最下级从节点配置:
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
server-id=27
read-only
[root@localhost ~]# systemctl restart mariadb
[root@localhost ~]# vim /data/2019-11-25-all.sql
CHANGE MASTER TO
MASTER_HOST='192.168.38.37',
MASTER_USER='repluser',
MASTER_PASSWORD='centos',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000001', MASTER_LOG_POS=514701; #在中间的从节点上进行查询二进制日志为,使用show master logs;进行查询
[root@localhost ~]# mysql < /data/2019-11-25-all.sql
[root@localhost ~]# mysql
MariaDB [(none)]> start slave;
主主复制
主主复制一般不单独使用,一般一个主节点担当写操作,另一个主节点只担当读操作,一个主节点坏了,再提升另一个主节点
第一个主节点配置:
[root@centos7 ~]# vim /etc/my.cnf
[mysqld]
log-bin
server-id=7
auto_increment_offset=1 #对数据库表的id字段进行区分;初始值为1,增长幅度为2;只记录奇数行,id字段必须为自动增长,以免造成数据不一致
auto_increment_increment=2
[root@centos7 ~]# systemctl restart mariadb
[root@centos7 ~]# mysqldump -A -F --single-transaction --master-data=1 > /data/date +%F--all.sql #完全备份
[root@centos7 ~]# scp /data/2019-11-25--all.sql 192.168.38.37:/data
[root@centos7 ~]# mysql
MariaDB [(none)]> grant replication slave on . to repluser@'192.168.38.%' identified by 'centos';
第二个主节点配置
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
log-bin
server-id=17
auto_increment_offset=2 #id字段以偶数增长,初始值为2,id自动必须设置为自动增长
auto_increment_increment=2
[root@localhost ~]# systemctl restart mariadb
[root@localhost ~]# vim /data/2019-11-25--all.sql #编辑备份
CHANGE MASTER TO #设置主节点的相关信息
MASTER_HOST='192.168.38.7',
MASTER_USER='repluser',
MASTER_PASSWORD='centos',
MASTER_PORT=3306,
MASTER_LOG_FILE='mariadb-bin.000004', MASTER_LOG_POS=245; #根据备份自动生成
[root@localhost ~]# mysql
MariaDB [(none)]> set sql_log_bin=off; #临时关闭二进制日志,避免导入备份的时候产生大量的二进制日志
MariaDB [test]> source /data/2019-11-25--all.sql #导入备份
MariaDB [test]> start slave; #开启线程
第一个主节点还需要再次配置的内容
两个主节点都是互为主互为从
[root@centos7 ~]# mysql
MariaDB [(none)]> CHANGE MASTER TO

-> MASTER_HOST='192.168.38.37',
-> MASTER_USER='repluser',
-> MASTER_PASSWORD='centos',
-> MASTER_PORT=3306,
-> MASTER_LOG_FILE='mariadb-bin.000002',           #主节点二进制日志位置,通过在主节点上使用show master logs;进行查看
-> MASTER_LOG_POS=245;

MariaDB [(none)]> start slave;
GTID复制
GTID工作原理:
GTID由server_uuid和transaction_id组合而成,每台机器的server_uuid都不同,表现为mysql目录下的auto.cnf文件中
1、GTID需要主从都开启相关的功能;主节点发生数据的更新,把server_uuid和发生的事务的transaction_id一同复制到bin-log中,通过dump线程发送给从节点
2、从节点的io thread负责把接收主节点的bin-log的内容放到relay-log中
3、sql thread执行relay-log中的sql语句,执行前先检查事务的GTID是否执行过,如果执行过,就跳过,没执行过就通过sql thread进行执行
注意:
1、GTID功能从Mysql-5.6以上新添加的功能;并且mysql-5.6必须在配置文件中添加log_slave_updates选项
2、log_slave_updates:把中继日志中的操作同步到二进制日志中;从节点查看复制过来的GTID编号的事务是否执行过,查看的是二进制日志中的GTID,所以此项在Mysql-5.6中必须开启
3、在mysql-5.7开始,不需要设置log_slave_updates这个选项;mysql-5.7在mysql库中添加了gtid_executed表,表中记录了GTID的信息
优点:不需要记录二进制日志的位置
实现GTID----主节点配置
[root@localhost mysql]# vim /etc/my.cnf
[mysqld]
server-id=17
log-bin=mysql-bin
gtid_mode=on #开启GTID
enforce_gtid_consistency #强制GTID的一致性
[root@localhost mysql]# service mysqld restart
[root@localhost mysql]# mysql
mysql> grant replication slave on . to 'repluser'@'192.168.38.%' identified by 'centos'; #授权并且创建从节点连接主节点的账号
从节点配置
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
server-id=27
gtid_mode=on
enforce_gtid_consistency
[root@localhost ~]# service mysqld restart
[root@localhost ~]# mysql
mysql> CHANGE MASTER TO

-> MASTER_HOST='192.168.38.37',
-> MASTER_USER='repluser',
-> MASTER_PASSWORD='centos',
-> MASTER_PORT=3306,
-> MASTER_AUTO_POSITION=1;       #必须开启此项;自动同步主节点位置

mysql> start slave; #开启线程