Fork me on GitHub

如何解决数据库主从不一致

常见数据库架构

一主多从、主从同步、读写分离。

1、主库提供写服务

2、其它多个从库提供读服务,可以增加从库提升读性能

3、主从之间数据同步

加从库的本质是为了提升读性能

数据库主从不同步原因

数据库的主从数据不一致,主要还是由主从同步存在延时,而读写就是这延时片刻。

如上图:

1、服务发起一个写请求

2、服务发起一个请从库的请求,此时主库数据还未同步至从库中,读到不一致的数据

3、数据主从同步完成

所有数据冗余,都会引发一致性问题

主从不同步解决方案

方案一:忽略

常见的很多业务都允许短时间内数据不一致,比如新闻页面,商品详情等。当新闻有内容修改时,短时间内用户读到的是旧数据也不影响业务。此时直接忽略数据不同步即可。

方案二:强制读主库

读写都走主库,依靠缓存来提升读性能。(主从就没意义了)

方案三:适时读取主库

在主从未同步之前读主库,主从同步完成之后读从库,这是我们想要的理想状态。

此时,必须了解主从同步的延时有多长(比如1秒),当有数据更新时,在缓存中作标记(过期时间为1秒),标记该条数据刚更新过,还未同步到从库。当有读操作时,首先判断缓存中是否存在该条数据的标记,以此来决定是读取主库还是读取从库。

缓存的key可以设置为 数据库名:数据表名:主键

本文思路来源于公众号:架构师之中

自己制作 Centos7 Openresty / Nginx Docker镜像

说明

制定自己的 Nginx Docker 镜像,方便进行 Lua 与 Nginx开发,今天以 Openresty-1.13.6.2 为例,创建咱们自己的 Docker 镜像。

默认大家已经安装并配置好 Docker 环境。

获取最新的 centos 系统

1
➜ ~ docker pull centos

运行 centos,并进行容器

1
2
3
4
➜ ~ docker run -itd centos:latest /bin/bash
c588b5ff0914

➜ ~ docker exec -it c588b5ff0914 /bin/bash

安装 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装必备工具及依赖
➜ ~ yum install wget openssl openssl-devel pcre-devel openssl-devel gcc curl postgresql-devel

# 下载最新版本 openresty
➜ ~ wget https://openresty.org/download/openresty-1.13.6.2.tar.gz

# 编译安装
➜ ~ ./configure --prefix=/usr/local/openresty \
--with-luajit \
--without-http_redis2_module \
--with-http_iconv_module \
--with-http_postgres_module

➜ ~ gmake

➜ ~ gmake install

启动 Nginx

1
➜ ~ /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/nginx.conf

新建并运行 Nginx 容器与 php-fpm 容器通信

1
➜ ~ docker run -itd --name nginx --link=php -p 8080:80 -v /var/www:/var/www 267591818477 /usr/local/openresty/nginx/sbin/nginx -g 'daemon off;' -c /usr/local/openresty/nginx/conf/nginx.conf

保存镜像到云端

阿里云提供的免费云端容器镜像服务,大家可以登录后,根据官方教程提交自己的镜像,方便在任何机器上使用镜像。

自己制作 Centos7 PHP7 Docker镜像

说明

制定自己的 PHP Docker 镜像,方便进行不同版本的PHP测试,今天以 PHP-7.2.6为例,创建咱们自己的 Docker 镜像。

默认大家已经安装并配置好 Docker 环境。

获取最新的 centos 系统

1
➜ ~ docker pull centos

运行 centos,并进行容器

1
2
3
4
➜ ~ docker run -itd centos:latest /bin/bash
c588b5ff0914

➜ ~ docker exec -it c588b5ff0914 /bin/bash

安装必备工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜ ~ yum install -y wget vim

# 配置 vim
➜ ~ vim ~/.vimrc

# 保存以下内容
syntax on
set shiftwidth=4
set tabstop=4
set ts=4
set softtabstop=4
set nu
set smartindent
set expandtab

安装 PHP-7.2.6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 安装 php 依赖
➜ ~ yum -y install libxml2 libxml2-devel curl-devel libjpeg-devel libpng-devel freetype-devel libicu-devel libxslt-devel openssl-devel glibc-headers gcc-c++ openssl openssl-devel

cd /usr/local/src

# 下载 php 源码,并解压
➜ ~ wget http://cn2.php.net/distributions/php-7.2.6.tar.gz

➜ ~ tar zxvf php-7.2.6.tar.gz

➜ ~ cd php-7.2.6

# 编译并安装
➜ ~ ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-mysql-sock=/tmp/mysql.sock --with-mhash --with-openssl --with-mysqli=shared,mysqlnd --with-pdo-mysql=shared,mysqlnd --with-gd --with-iconv --with-zlib --enable-zip --enable-inline-optimization --enable-shared --enable-xml --enable-bcmath --enable-shmop --enable-sysvsem --enable-mbregex --enable-mbstring --enable-ftp --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --without-pear --with-gettext --enable-session --with-curl --with-jpeg-dir --with-freetype-dir --enable-opcache --enable-fpm --enable-fast-install

➜ ~ make

➜ ~ make install

# 复制配置文件到 php 安装目录
➜ ~ cp php.ini-development /usr/local/php/etc/php.ini

PHP-FPM 管理命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# check php-fpm config
➜ ~ /usr/local/php/sbin/php-fpm -t
➜ ~ /usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf -t

# start php-fpm
➜ ~ /usr/local/php/sbin/php-fpm
➜ ~ /usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf

# stop php-fpm
➜ ~ kill -INT `cat /usr/local/php/var/run/php-fpm.pid`

# restart php-fpm
➜ ~ kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`

生成自己的镜像,方便以后测试

1
2
3
4
5
6
7
8
9
➜ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c588b5ff0914 49 "/bin/bash" About an hour ago Up About an hour sad_williams

# 生成镜像
➜ ~ docker commit c588b5ff0914 my-php

# 以后想运行 php-fpm,创建并启动一个容器
➜ ~ docker run -itd --name php -v /var/www:/var/www my-php /usr/local/php/sbin/php-fpm -F -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf

保存镜像到云端

阿里云提供的免费云端容器镜像服务,大家可以登录后,根据官方教程提交自己的镜像,方便在任何机器上使用镜像。

Swoole 实现 PHP 多进程处理任务

send.php 待处理数据入 redis 队列

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/local/Cellar/php71/7.1.11_22/bin/php
<?php

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$user_list = file('data/msg.txt');

foreach ($user_list as $k => $user) {
$redis->lPush('message', rtrim($user));
}

$redis->close();

task.php 处理 redis 队列中的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/local/Cellar/php71/7.1.11_22/bin/php
<?php

require_once 'functions.php';

$workerNum = 4;

$pool = new Swoole\Process\Pool($workerNum);

$pool->on("WorkerStart", function ($pool, $workerId) {
$running = true;

pcntl_signal(SIGTERM, function () use (&$running) {
$running = false;
});

echo "Worker#{$workerId} is started\n";

$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379);

$key = "message";

while ($running) {
$msgs = $redis->brpop($key, 2);

pcntl_signal_dispatch();

if ($msgs == null) {
continue;
}

// 处理任务代码

echo $msgs . PHP_EOL;
}
});

$pool->on("WorkerStop", function ($pool, $workerId) {
echo "Worker#{$workerId} is stopped\n";
});


$pool->start();

论学习能力

1.

学习能力的第一个是能够界定自己需要学习的内容:在一段时间内学一点东西就很了不起了。如果不知道自己学什么的话,以你有限的时间去跟无限的知识较劲,你会被淹死。因为不知道学什么,一类人就放弃学习了,另一类人什么都学,但结果其实是一样的。

2.

在任何学校的时候都有人告诉你学什么,但当你是成年人的时候就不要指望了:确定学什么是你的职责。这也是对自我探索的过程,了解你自己的优势特长、人生目标和兴趣,当然更重要的是还要考虑社会的需求。许多“活跃的学习者”兴趣太广了、学的太多了,所以每天都在学习,但仍然没有长进。

3.

除了不知道应该聚焦在学习那个专业的知识外,还有就是不知道学什么内容,除了陈述性知识和流程性知识之外,你还需要学习情景性知识、战略性知识,你还需要学习“不知道自己不知道”的知识(如何发现?),还需要有学习一个领域、行业、机构等的框架,这是不知道学什么的四个层次。

4.

真正的学习一定要包括干活和解决问题,只有经过实践的东西才是真正学会了。你还要注意跟人去学习,高手的指点让你少走许多弯路。所以说学习不仅仅是你传统认为的读书、练习、思考这些形式。跟书和文章学习、跟人学习、在解决问题中学习,合起来才是正确的学习途径,而且实践中的学习是根本。

5.

输入决定输出,当你在一个领域初入门的时候最需要的是看系统化的内容,这个时候看一本比较成熟的教科书要远超过你读几年的朋友圈。当你在某个领域内成为高手或者起码要对这个领域的全貌有所了解的时候,碎片化的内容才对你有价值。不要沉迷于那些煽动情绪或者读起来很爽的内容,这些可能是毒药。

6.

警惕时髦的内容,许多时候他们只是某些个人的感触,即便不是故意的骗子但也会受制于提供者的认知限制而价值不大。警惕那些许多新概念和术语的内容,如果一个概念在其他地方没有而只在这里出现,那就要加倍警惕。

7.

知识结构是跟岗位和职能相关的,也跟个人的目标相关,是一种自我的选择。知识体系是对于某领域知识掌握后的结果,没有人可以一上来就有知识体系,它只不过是对某领域掌握后更高层次的概括,形成知识体系的过程是个人学习、解决问题和思考的过程。

8.

真正学会的时候你是知道的,因为你能够发现在你的前面已经没有人了。但做出这个判断需要你多次的对所学领域的遍历:将这个领域的前辈们说过的、写过的都看一遍,而且都能看懂,知道他们说法的优缺点。在这个时候其实你就知道有许多书其实不需要读,许多人也不需要去聊。

如何了解团队

只有全盘了解团队的工作内容,已经清楚你的团队如何为该组织增加价值的时候,你才能决定工作的优先顺序,才能知道应该做什么,不应该做什么。

向每一句团队成员了解情况,了解他们在做什么,掌握他们的全部工作内容:项目工作、临时性工作、周期性工作、持续性工作和管理工作。

  • 项目工作具备起点和终点,并且满足特定的组织目标。

  • 临时性工作的起因无从说起,可能由于一次危机、一个意外的要求,或者是计划外的其他工作。

  • 持续性工作是指那些维持业务运转的工作。

  • 周期性工作是在可知的时间段出现的工作。

  • 管理工作涉及剩余工作的计划和组织工作。管理工作还涉及雇佣员工、员工发展以及留住人才, 预算和报告工作,影响力通过团队工作创造价值。

这些构成团队的“工作整体”

作为管理者,可以尝试如下的做法:

  • 开始与每一名团队成员进行每周一次的一对一会谈。
  • 注意员工的出色表现,并及时提出表扬。
  • 走出自己的办公室!“走动和聆听”管理法的关键就在于注意改变。熟悉正常的办公室噪音级、慧黠装饰和员工的士气。走出办公区,在休息室喝杯咖啡。在公司餐厅吃午餐。
  • 罗列出团队的所有工作内容,包括你自己的工作。

Lua 实现根据图片URL规则,匹配相应图片服务器域名

需求:有图片服务器集群,photo1至photo16,图片是根据URL路径来分布的。现在另外加一GFS图片存储。

问题:我怎么知道这张图片是在GFS在,还是在16台集群上?

解决方案:搭建一个图片中转服务,加载 GFS 到 本地目录(如:/data/gfs),使用 lua 来完成图片域名判断,使之匹配至正确的图片机器上。

nginx 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
server {
listen 8080;
server_name t1.com;

access_log logs/test.log main;

root /data/gfs;

location / {
try_files $uri @proxy;
}

location @proxy {
set $target '';
access_by_lua '
local table = {
["0"] = "photos1.xxx.com",
["1"] = "photos2.xxx.com",
["2"] = "photos3.xxx.com",
["3"] = "photos4.xxx.com",
["4"] = "photos5.xxx.com",
["5"] = "photos6.xxx.com",
["6"] = "photos7.xxx.com",
["7"] = "photos8.xxx.com",
["8"] = "photos9.xxx.com",
["9"] = "photos10.xxx.com",
["a"] = "photos11.xxx.com",
["b"] = "photos12.xxx.com",
["c"] = "photos13.xxx.com",
["d"] = "photos14.xxx.com",
["e"] = "photos15.xxx.com",
["f"] = "photos16.xxx.com"
}
local request_uri = ngx.var.request_uri
local key = string.sub(request_uri, 2, 2)

local host = table[key]
ngx.var.target = host
';

proxy_pass http://$target;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}

多台图片服务器下 nginx 自动匹配图片

需求:有图片服务器,A、B、C、D,由于某种原因,读取图片时,不知道图片在哪台机器上。所以用 nginx 来判断图片在哪台机器上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
upstream test.com {
server t2.com:80;
server t3.com:80;
server t4.com:80;
}

server {
listen 80;
server_name test.com;

root /var/www/test.com;
access_log logs/test.log main;

location / {
try_files $uri @proxy;
}

location @proxy {
proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;
proxy_pass http://test.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}

参考资料

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream