主从同步及主从延迟

简单的说,主从同步就是通过数据库底层二进制日志的复制来完成的。

1、 什么是二进制日志

二进制日志又称为binlog。当在配置项中开启二进制日志后,数据库会将每次操作(DDLDML)记录在二进制日志中。

mysql-binlog

进入datadir目录我们可以看到mysql-bin.xxxxx格式的文件就是二进制日志。

mysql-binlog

我们可以通过mysqlbinlog命令去查看这些日志中放了些什么东东。

2、 主从同步流程

主从原理

从图中我们我们可以看到,当客户端对主库进行操作的时候,主库会对改变数据库的操作进行记录二进制日志。这个时候从库的I/O线程(Slave_IO_Running)会将主库的二进制日志拉取到本机斌且写入中继日志中去,而从库的另一个SQL线程(Slave_SQL_Running)会将中继日志中的数据拉取出来,解析和执行。这就是一个主从同步的基本流程。

那我们可以将主从同步简单的分为3个步骤:

  1. 主库产生二进制日志
  2. 从库通过IO线程拉取二进制日志并存入中继日志
  3. 从库SQL线层从中继日志中拉取数据,解析并执行

3、 主从延迟

如果将上述三个步骤按照效率来排序的话那么会是这么一个结果: 1 > 2 > 3

为什么会是这个一个顺序呢?

  • 主库产生二进制日志是顺序写,并且有时卸载本地磁盘,所以效率是非常高的。(当然如果是SSD的话会更快)
  • 从库IO线程拉取二进制日志涉及到网络传输,所以效率就要比步骤1稍慢一些。
  • 从库SQL线程涉及到数据的操作,那么效率快慢就要看具体执行的SQL了。

那么问题就来了。因为IO线程SQL线程都是单线程。假设SQL线程执行一个SQL需要10分钟的话,此时SQL线程什么事情就不能做了,后续的SQL操作只能等待这次操作完成后才能继续执行。那么延迟自然而然的产生了。

回过头来,我们又会发现MASTER主库可以多并发写入,而多IO线程SQL线程都是单线程,当网站访问量上升,并发量增大,身为单线程的IO线程SQL线程是很难支撑的住,延迟也会自然而然的产生。

当然还有其他问题也会主从延迟,比如:SLAVE从库执行大型QUERY语句产生了锁等待。等等

虽然说MYSQL5.6.3开始支持多线程复制,事实上是针对每个database开启相应的独立线程。即每个库有一个单独的(sql thread)如果线上业务中,只有一个database或者绝大多数压力集中在个别database的话,多线程并发复制特性就没有意义了。

4、 总结

其实主从延迟是不可避免的,那么只有提升我们自己的危机的意识,码代码的时候时刻考虑着这个问题,才能避免各种BUG,从而避免为公司带来资损。

有序数组

一、 概念

有序数组中也分有普通有序数组和循环有序数组。

普通有序数组是一种特殊的数组,里面的元素,按一定的顺序排列,我们这里假设由小到大排列。

循环有序数组指的是,将一个有序数组循环左/右移动若干距离之后变成的数组。如,[1,2,3,4,5]循环右移3位,就成为[4,5,1,2,3]。该数组的特点是,其中包含着一个转折点。转折点左右两侧的子数组都是有序的,并且左侧的子数组整体都比右侧的子数组大。

二、 二分查找

有一个古老的印度故事,国王要给大臣赢棋的奖赏,大臣希望能够“请在第一个格子里放一颗麦粒,第二个格子里放2颗,第三个格子里放4个,第四个格子里放8粒……依此类推,把64 个格子都放满。” 总共能有多少,结论是一千八百四十四吉六千七百四十四兆零七百三十七亿零九百五十五万一千六百一十五。

也就是说二分查找能够在64次运算后处理这样多的记录,结果还是很惊人的,这也就是O(log2N) 时间复杂度的由来,二分查找,2次可以从4条记录中找到对应记录,3次8条,4次16条。。依次类推。

使用二分查找的前提条件是:

  1. 数据能够按照某种条件进行排序
  2. 可以通过某种方式,取出该数据集中任意子集的中间值。
普通有序数组二分查找

循环有序数组二分查找

循环有序数组的二分查找,需要多一个步骤,就是查询到转折点。
但是,不要想着直接定位到转折点。

只需要知道转折点在中间点的哪一侧就行。因为不含转折点的一侧一定是单调递增的,依然能够帮助我判断出key在mid的哪一侧。

根据left和right下标,求得mid下标。

  • 如果A[left]<=A[mid],那么A[mid]一定不在转折点左侧,意味着从left到mid的整个左半部分都是严格递增的,因此我能够判断key是否在左半部分里。
  • 如果A[left]>A[mid],那么A[mid]一点在转折点左侧,意味着从mid到right的整个右半部分是严格递增的,因此我能够判断key是否在右半部分里。

三、 扩展

  1. 是否支持范围查找
    基本上来说,这个问题主要取决于两个方面,一个是数据是否有序,另外一个是该存储结构是否提供了快速读取下一个记录的方法。
    对于数组来说,所有数据都是有序的,并且只需要简单地做下标=下标+1的操作,就可以以程序提供的最快方法。因此,范围查找肯定是可以支持的。而且效率应该是很高的.

  2. 集合是否能够随着数据的增长而自动扩展
    现代语言中,一般都会提供一个支持自由扩展的数组。
    然而,虽然能够做到自动的扩展了,但是,要知道,要能够保持映射可以快速的查询,那么必须还要保证新写入的数据的有序性,这就很麻烦了。。因为你无法知道数据写入的顺序,那么如何处理中间值插入就是个很麻烦的问题了。
    比如,如果我的写入顺序是1,3,4,2,5 这个顺序,并且在开始的时候没有办法知道写入的顺序,那么我们唯一能做的事情就是,按照顺序,写1,然后在1的右临位,写3,然后再在右临位写4,这时候来个2,麻烦来了,我应该怎么做呢?
    在数组内,一种最容易想到的方式就是,找到1这个位置,然后将1右边的数据全部往右移动一个位置,然后将2填写进去。
    哈,你也看到了,这种方式会随着我们数据量的变大,而代价变得非常巨大,比如如果有100万行记录,那么就意味着最坏情况下,需要每次写入都移动100万行记录。。。这代价可是真大。
    因此,这也就是我们使用数组的时候的一个最大的问题了,单独的使用数组,是不支持更新的,而使用简单的策略比如自动扩增的数组,也无法支持有序数据的自增的。

  3. 读写性能如何
    对于读取,如果要快速查找数据,需要使用二分查找来找到一个数据,而二分查找的时间复杂度大概是O(log2N).
    对于写入,数组支持的代价很高,因此基本没人会想要用大数组来做需要频繁增加和删除对应关系的映射的实现的吧
  4. 是否面向磁盘结构
    大部分情况下,纯粹的数组并不是一个面向磁盘的结构。
    磁盘的特性是,一次读取一批数据,或一次写入一批数据时效率会高。
    顺序读大批数据,或者顺序写大批数据的时候,效率会变高。
    而如果要进行二分查找,如果能够一次从磁盘中取出整个数组再进行二分查找,则查找效率会变高,若不得不直接依托磁盘进行二分查找,则读取效率会很低,每次折半,都需要从磁盘中的一个随机位置取出一个block,并从中取出一条记录,随机读取的次数= log2N的结果,而每一次的随机读,对磁盘来说都是灾难。
    不过,在未来,我们会看到一些依托于数组的数据结构,部分的解决了这个问题:)
  5. 并行指标
    只要是不支持修改的数据模型,一般来说并行读取效率都可以做到很高,因为。。完全不会变就不需要加锁啊- -,因此是可以完全并行的
  6. 内存占用
    数组的内存占用应该是个标杆,如果再能比数组更节省空间,那就需要做大量hack和压缩了。

参考:
http://blog.csdn.net/ojshilu/article/details/17485787
http://blog.sina.com.cn/s/blog_693f08470101mi2o.html

RPC服务 —— http

[toc]

1、什么是RPC

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

当我们对网站进行SOA化时,我们就需要通过RPC服务来保持服务与服务之间的通信。RPC的实现包括客户端和服务端。服务端提供服务,客户端进行调用。现在比较常见有2种方式的RPC服务,一个是基于HTTP协议的RPC服务,另外一个是基于TCP协议的RPC服务。本文将介绍一下基于HTTP协议的RPC服务。

2、基于HTTP的RPC服务

HTTP是基于TCP之上的一个协议,所以其相对TCP协议比较稳定,但是相对性能也稍差一些。

2.1、服务端

在php中,使用基于HTTP的PRC服务,那么就需要借助于nginx/apache来作为服务器。

服务端,我们可以使用上面的目录结构。
index.php 作为一个入口,并且做一些初始化的动作。
Api目录下存放所有提供服务的文件。
当然这只是一个比较简单目录结构,我们可以根据自己的项目去改造、去变化。

下面是简单实现代码。

2.2、客户端

客户端我们可以php的curl库来完成。

总结

当然上面是一个最简单的例子。在做架构中,我们需要对其扩充,比如安全验证、序列化等等的功能扩展。

io复用

1、基本概念

多路复用模型是对多个IO操作进行检测,返回可操作集合,这样就可以对其进行操作了。这样就避免了阻塞IO不能随时处理各个IO和非阻塞占用系统资源的确定。多路复用适用如下场合:
1. 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
2. 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
3. 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
4. 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
5. 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

2、socket_select

socket_select()等待sockets打开的连接事件。socket_select()调用系统的select(2)函数来工作:前面三个参数是你要使用的socket的数组;你可以对其读取,写入和获取异常(分别针对三个参数)。

3、 php实现多路复用

大致流程见下图:
io复用图