http的变迁(http1.0-http2.0,https)

先来一张图来解释。。。

未分类

太模糊了?不急不急,让我们分步分析。

http1.0

http1.0早在1996年就出现了。基本上满足了ajax的需求,但是呢,毕竟刚出来没多久,在优化上就得多下功夫,在http开始普及之后,他的缺点也开始展现。

http1.0最大的问题就是关于tcp连接的问题,大家都知道tcp是连接型的协议,所以需要建立连接和断开连接。但是早期的http1.0只能在一个tcp上承载一个http,而且web端只能有6-8个链接,这就使高并发的状态下带宽利用率非常低。所以在99年,推出了http1.1。

http1.0与http1.1

下图表示了其主要区别

未分类

来详细说一说。

  • 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。在这之中Etag也是最为重要的。它提供了精确的缓存定向。可以精确缓存某个文件而不是根据日期全部缓存。浏览器缓存控制

  • 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。

  • 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

  • Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。Host,referer,origin三者区别

  • 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。

好了,现在已经优化到http1.1了。但是是不是还有问题呢。有肯定是有的,在这之前我们先说一下https;

http与https

https其实和http差不多,就是加了个s嘛,实质上也是,https是http上加了一层安全套接层(SSL :safe socket level),也就是这样。

未分类

http1.1与SPDY

然而http1.1还有几个明显的缺点,
一是会发生堵塞:请求到达的服务器的速度是不同的,如果先发的请求先到达可能会发生阻塞,这样带宽就降低了
二是不支持服务端推送,如果要求做一个服务端数据变动页面立即改变的组件就很难做。要用短轮询和长轮询。这样对带宽影响也是很大的。

所以,GOOGLE推出了SPDY。

未分类

SPDY是基于https的,也就是这样。

未分类

嘛,SPDY解决的问题还是很多的。具体如下。

  • 降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率。

  • 请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。

  • header压缩。前面提到HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。

  • 基于HTTPS的加密协议传输,大大提高了传输数据的可靠性。

  • 服务端推送(server push),采用了SPDY的网页,例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。SPDY构成图:

SPDY与http2

结果httpSPDY这么好用,基本上大家都在用,就基于SPDY推出了 http2.0,而且把SPDY的优势用在了http上,也就是可以不用加密也能使用。而且稍微的把头部压缩算法换了一换。

具体来说

  • HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
  • HTTP2.0 消息头的压缩算法采用 HPACK http://http2.github.io/http2-spec/compression.html,而非 SPDY 采用的 DEFLATE http://zh.wikipedia.org/wiki/DEFLATE

综合上文 来个完整的流程图吧。

未分类

ubuntu14.04升级php curl,支持http2发送APNs

网上升级很多都不完整,不是这里出错就是那里出错。

遇到的问题是:

一、首先升级curl,这个比较容易,虽然支持http2,但是利用命令发送不了APNs的消息,明明支持http2,可以发送消息的头部依然是http/1.1。找了很多资料,需要升级openssl

二、升级openssl也容易,但是编译了很多次,curl -V查看版本的时候,openssl依然是老版本

总结问题,要cURL支持http2,必须满足openssl >= 1.0.2 , cURL >= 7.46.0

安装库文件

sudo apt-get install g++ make binutils autoconf automake git autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libxml2-dev libev-dev libevent-dev libssl-dev libjansson-dev libjemalloc-dev cython python3-dev python-setuptools

1、升级openssl

sudo add-apt-repository ppa:0k53d-karl-f830m/openssl

sudo apt-get update

sudo apt-get install openssl

2、安装nghttp2

git clone https://github.com/tatsuhiro-t/nghttp2.git

cd nghttp2

autoreconf -i

automake

autoconf

./configure

make

sudo make install

3、升级curl

sudo apt-get build-dep curl

wget https://curl.haxx.se/download/curl-7.55.1.tar.gz

tar zxvf curl-7.55.1.tar.gz

cd curl-7.55.1

./configure --prefix=/usr/local --with-nghttp2=/usr/local --with-ssl

make -s && sudo make install

sudo ldconfig

4、升级php5-curl

apt-get remove php5-curl

apt-get install php5-curl

php5enmod curl

service apache2 restart

Mysql入门mysql innodb 异常修复经验分享

一套测试用的mysql库,之前用的centos6默认源里的mysql 5.1.71的版本 .后来想试用下Percona server 5.7,由于这套库里没有什么重要数据 .所以操作前也未进行备份,配置好源后,直接就进行了安装.数据文件也存放在默认位置,安装完成后,直接启动mysql,发现启动失败,发现无法启动正常启动.

一、回退重新装mysql

为避免再从其他地方导入这个数据的麻烦,先对当前库的数据库文件做了个备份(/var/lib/mysql/位置).接下来将Percona server 5.7包进行了卸载,重新安装原先老的5.1.71的包,启动mysql服务,提示Unknown/unsupported table type: innodb,无法正常启动.

110509 12:04:27 InnoDB: Initializing buffer pool, size = 384.0M
110509 12:04:27 InnoDB: Completed initialization of buffer pool
InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytes
InnoDB: than specified in the .cnf file 0 157286400 bytes!
110509 12:04:27 [ERROR] Plugin 'InnoDB' init function returned error.
110509 12:04:27 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
110509 12:04:27 [ERROR] Unknown/unsupported table type: innodb
110509 12:04:27 [ERROR] Aborting
110509 12:04:27 [Note] /usr/sbin/mysqld: Shutdown complete

删除/var/lib/mysql/目录,重新启动数据库服务,并初始化,发现正常,show engines能发现有innodb引擎.再将数据库停掉,将之前备份的/var/lib/mysql/目录的内容覆盖当前位置的内容,重启.又发现不能进行启动,报错内容和刚刚一样.

/var/lib/mysql目录内容的结构如下:

-rw-rw---- 1 mysql mysql 10485760 2月  26 18:10 ibdata1
-rw-rw---- 1 mysql mysql 5242880 2月  26 18:10 ib_logfile0
-rw-rw---- 1 mysql mysql 5242880 2月  26 17:20 ib_logfile1
drwx------ 2 mysql mysql   4096 2月  26 17:20 mysql
drwx------ 2 mysql mysql   4096 2月 26 17:24 wiki

wiki目录是测试数据的库,ibdata1文件为数据文件,ib开头的两个文件为日志文件,mysql 目录下为系统库相关的东西 .再次使用初始化的数据,并将wiki目录和ibdata1文件覆盖到/var/lib/mysql 目录下,可以正常启动,也可以正常登录.

二、innodb模块重装

不过在通过mysqldump备份时,又提示unknow table engine “Innodb” .登录后,查看当前所有的引擎类型,发现其中果然不存在innodb类型:

未分类

通过alter命令修改其中一个表的类型为MyISAM ,发现仍然报错.

未分类

通过 find 查找发现/usr/lib64/mysql/plugin/目录下有ha_innodb_plugin.so文件.印象中mysql5以后的版本支持在线插件安装 .通过下面查看确认,果然支持:

未分类

使用如下命令加载时,发现不成功:

install plugin innodb soname 'ha_innodb.so';

三、备份

在/etc/my.cnf中增加如下配置:

plugin-load=innodb=ha_innodb_plugin.so
plugin_dir=/usr/lib64/mysql/plugin/
default-storage-engine=InnoDB 

发现仍启动失败.查看mysql-error.log发现有如下内容:

InnoDB: Database page corruption on disk or a failed
InnoDB: file read of page 7.
InnoDB: You may have to recover from a backup.
InnoDB: It is also possible that your operating
InnoDB: system has corrupted its own file cache
InnoDB: and rebooting your computer removes the
InnoDB: error.
InnoDB: If the corrupt page is an index page
InnoDB: you can also try to fix the corruption
InnoDB: by dumping, dropping, and reimporting
InnoDB: the corrupt table. You can use CHECK
InnoDB: TABLE to scan your table for corruption.
InnoDB: See also http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html

打开forcing-innodb-recovery官方页面,发现可以通过指定innodb_force_recovery参数,进行强制启动和恢复.在/etc/my.cnf中增加如下内容:

innodb_force_recovery=6

重新启动成功了.通过mysqldump备份也没有问题,将备份数据导入其他主机发现也正常可以测试.

这下就好搞了,将mysql彻底删除,重新安装Percona server 5.7,安装完后,建库,还原数据,程序重新连接,一切OK.

总结:

由于mysql innodb数据文件的特性,可以在出现问题,无法正常启动时,先将./ib_logfile0 和 ./ib_logfile1 两个日志文件先移走,再启动,如果还不成功,可以用innodb_force_recovery参数进行强制恢复.除此之外,日志也很重启,有问题先看日志.

InnoDB Buffer Pool巧配置全解

一、简介

InnoDB维护了一个缓存数据和索引信息到内存的存储区叫做Buffer Pool,它会将最近访问的数据缓存到缓冲区。我们通过配置各个Buffer Pool的参数,可以显著提高MySQL的性能。

InnoDB的Buffer Pool是基于LRU算法来实现的,下面我们可以简单了解一下LRU算法:

least recently used (LRU),InnoDB管理Buffer Pool是将Buffer Pool作为一个list管理,基于LRU算法的管理。当有新的页信息要读入到Buffer Pool里面的时候,Buffer Pool就将最近最少使用的页信息从Buffer Pool当中驱逐出去,并且将新页加入到list的中间位置,这就是所谓的中点插入策略。

一般情况下list 头部存放的是热数据,就是所谓的young page(最近经常访问的数据),list尾部存放的就是old page(最近不被访问的数据)。这个算法就保证了最近经常使用的page信息会被保存在最近访问的sublist,相反的不被经常访问的就会保存在old sublist。而old sublist当中的page信息都是在新数据写入时被驱逐的。

LRU算法有以下的标准算法:

  • 3/8的list信息是作为old list,这些信息是被驱逐的对象。

  • list的中点就是我们所谓的old list头部和new list尾部的连接点,相当于一个界限

  • 新数据的读入首先会插入到old list的头部,

  • 如果是old list的数据被访问到了,这个页信息就会变成new list,变成young page,就会将数据页信息移动到new sublist的头部。

  • 在数据库的Buffer Pool里面,不管是new sublist还是old sublist的数据如果不会被访问到,最后都会被移动到list的尾部作为牺牲者。

一般情况下,页信息会被查询语句立马查询到而被移动到new sublist,这就意味着他们会在Buffer Pool里面保留很长一段时间。

表扫描(包括mysqldump或者没有where条件的select等操作)等操作将会刷入大量的数据进入Buffer Pool,同时也会将更多的Buffer Pool当中的信息刷出去,即使这个操作可能只会使用到一次而已。同样的如果 read-ahead后台进程读入大量数据的情况下也是会造成Buffer Pool大量高频的刷新数据页,但是这些操作是可控的,下面会讲到。read-ahead操作简单说一下就是MySQL的一个后台预读进程,能够保证MySQL预读入数据进入Buffer Pool当中。

二、参数一览表

下面先看下InnoDB Buffer Pool的一些相关参数信息,后面会详细解释一下:

  • innodb_buffer_pool_size:这个值是设置InnoDB Buffer Pool的总大小;

  • innodb_buffer_pool_chunk_size:InnoDB Buffer Pool的执行单元 chunk size的大小。这里面有个关系要确定一下,最好按照这个设置 innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances*N(N>=1);

  • innodb_buffer_pool_instances:设置InnoDB Buffer Pool实例的个数,每一个实例都有自己独立的list管理Buffer Pool;

  • innodb_old_blocks_pct:默认InnoDB Buffer Pool中点的位置,默认值是37,最大100,也就是我们所谓的3/8的位置,可以自己设置;

  • innodb_old_blocks_time:设置保留在Buffer Pool里面的数据在插入时候没有被改变list位置的时候的保存时间;

  • innodb_read_ahead_threshold:参数控制MySQL何时进行预读,也可以控制MySQL预读数据时候对于数据的敏感度,如果Buffer Pool里面存储的数据页的频繁值大于innodb_read_ahead_threshold的值,InnoDB就会启动一个异步的预读操作;

  • innodb_random_read_ahead:默认是disabled,是控制预读方式的参数,开启的话将不使用线性预读而是使用随机预读;

  • innodb_adaptive_flushing:指定是否动态自适应刷新脏页到盘,这个是MySQL根据负载自己决定的。不过还是尽量不要设置,让MySQL自己来管理自己;

  • innodb_adaptive_flushing_lwm:关闭adaptive_flushing的话才会有用,用来标记redo log使用率的百分比的最低线,当达到这个值的时候就会刷新脏页,默认为10;

  • innodb_flush_neighbors:控制是否刷新Buffer Pool脏页的脏数据的时候将同一区的脏数据页一同刷新,默认值为1;

  • innodb_flushing_avg_loops:为InnoDB保存InnoDB Buffer Pool前几次的冲洗状态快照的迭代数,默认值为30,增大的话,冲洗就会变得缓慢。减小的话冲洗的频率就会变高;

  • innodb_lru_scan_depth:控制LRU算法的一个参数,用来控制Buffer Pool后台进程page_cleaner 刷新脏页的位置;

  • innodb_max_dirty_pages_pct:参数会让InnoDB Buffer Pool刷新数据而不让脏数据的百分比超过这个值;

  • innodb_max_dirty_pages_pct_lwm:InnoDB会自动维护后台作业自动从Buffer Pool当中清除脏数据,当Buffer Pool中的脏页占用比 达到innodb_max_dirty_pages_pct_lwm的设定值的时候,就会自动将脏页清出Buffer Pool;

  • innodb_buffer_pool_filename:指定文件名字;

  • innodb_buffer_pool_dump_at_shutdown:配置的InnoDB是否保留当前的缓冲池的状态,以避免在服务器重新启动后,还要经历一个漫长的暖机时间;

  • innodb_buffer_pool_load_at_startup:指定此参数启动,数据库重启以后会自动暖机,读入Buffer Pool重启前保存的信息;

  • innodb_buffer_pool_dump_now和innodb_buffer_pool_load_now当数据库已经提起来的时候,我们忘了以前指定,也可以指定马上恢复;

  • innodb_buffer_pool_dump_pct:设置一下恢复Buffer Pool中多少数据;

  • innodb_buffer_pool_load_abort:终止Buffer Pool恢复,可以指定负载运行。

三、innoDB Buffer Pool解读

1、Buffer Pool Size设置和生效过程

理想情况下,在给服务器的其他进程留下足够的内存空间的情况下,Buffer Pool Size应该设置的尽可能大。当Buffer Pool Size设置的足够大时,整个数据库就相当于存储在内存当中,当读取一次数据到Buffer Pool Size以后,后续的读操作就不用再访问磁盘。

下面我们看一下Buffer Pool Size的设置方式:

当数据库已经启动的情况下,我们可以通过在线调整的方式修改Buffer Pool Size的大小。通过以下语句:

SET GLOBAL innodb_buffer_pool_size=402653184;

当执行这个语句以后,并不会立即生效,而是要等所有的事务全部执行成功以后才会生效;新的连接和事务必须等其他事务完全执行成功以后,Buffer Pool Size设置生效以后才能够连接成功,不然会一直处于等待状态。

期间,Buffer Pool Size要完成碎片整理,去除缓存page等等操作。在执行增加或者减少Buffer Pool Size的操作时,操作会作为一个执行块执行,innodb_buffer_pool_chunk_size的大小会定义一个执行块的大小,默认的情况下,这个值是128M。

Buffer Pool Size的大小最好设置为innodb_buffer_pool_chunk_size innodb_buffer_pool_instances的整数倍,而且是大于等于1。

如果你的机器配置的大小不是整数倍的话,Buffer Pool Size的大小是会自适应修改为innodb_buffer_pool_chunk_sizeinnodb_buffer_pool_instances的整数倍,会略小于你配置的Buffer Pool Size的大小。

比如以8G为例:

mysqld –innodb_buffer_pool_size=8G –innodb_buffer_pool_instances=16,然后innodb_buffer_pool_instances=16的大小刚好设置为16,是一个整数倍的关系。而且innodb_buffer_pool_chunk_size的大小也是可以在my.cnf里面指定的。

还有一种情况是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances大于buffer pool size的情况下,innodb_buffer_pool_chunk_size 也会自适应为Buffer Pool size/innodb_buffer_pool_instances,可见MySQL的管理还是非常的智能的。

如果我们要查看Buffer Pool的状态的话:

SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status'

可以帮我们查看到状态。我们可以看一下增加Buffer Pool的时候的一个过程,再看一下减少的时候的日志,其实还是很好理解的,我们可以看成每次增大或者减少Buffer Pool的时候就是进行innodb_buffer_pool_chunk的增加或者释放,按照innodb_buffer_pool_chunk_size 设定值的大小增加或者释放执行块。

增加的过程:增加执行块,指定新地址,将新加入的执行块加入到free list(控制执行块的一个列表,可以这么理解)。

减少的过程:重新整理Buffer Pool和空闲页,将数据从块中移除,指定新地址。

2、Buffer Pool Instances

在64位操作系统的情况下,可以拆分缓冲池成多个部分,这样可以在高并发的情况下最大可能的减少争用。下面我们看一下怎么配置Buffer Pool Instances?

配置多个Buffer Pool Instances能在很大程度上能够提高MySQL在高并发的情况下处理事物的性能,优化不同连接读取缓冲页的争用。

我们可以通过设置 innodb_buffer_pool_instances来设置Buffer Pool Instances。当InnoDB Buffer Pool 足够大的时候,你能够从内存中读取时候能有一个较好的性能,但是也有可能碰到多个线程同时请求缓冲池的瓶颈。这个时候设置多个Buffer Pool Instances能够尽量减少连接的争用。

这能够保证每次从内存读取的页都对应一个Buffer Pool Instances,而且这种对应关系是一个随机的关系。并不是热数据存放在一个Buffer Pool Instances下,内部也是通过hash算法来实现这个随机数的。每一个Buffer Pool Instances都有自己的free lists,LRU和其他的一些Buffer Poll的数据结构,各个Buffer Pool Instances是相对独立的。

innodb_buffer_pool_instances 的设置必须大于1才算得上是多配置,但是这个功能起作用的前提是innodb_buffer_pool_size的大小必须大于1G,理想情况下innodb_buffer_pool_instances的每一个instance都保证在1G以上。

3、innoDB Buffer Poll LRU原理

你可以频繁的往Buffer Pool里面读取数据,当backup或者report的时候,不用有太多的顾虑。InnoDB采用的是一种不是像LRU那么严格的方法来保证将最近访问的数据写入到Buffer Pool里面,并且最大可能的降低减少数据的带入量。这个语句是全表扫描或者以后这个数据将不会再被访问到,但是缓冲数据还是会写入到Buffer Pool里面。

新写入的数据会被插入到LRU list的中间位置,默认会插入到从list尾部算起来的3/8的位置,当这些写入的数据在Buffer Pool中被第一次访问的时候,在list中的位置就会向前移动,这样其实就会在list保留两个位置,老的位置并不会被立即清除,直到老的LRU list的位置被标记为OLD的时候,才会在下一次插入数据的时候被作为牺牲者清除掉。

我们本身是可以指定插入LRU list的位置,并且也可以设置当索引扫描或者是全表扫描的时候是不是采用这个相同的优化方法。 innodb_old_blocks_pct这个参数设置的是插入的位置,默认的值是37,我们可以设置的值是5-95之间,其余部分并不用来保存热数据。

但是还有一个严重的问题就是当一个全表扫描或者索引的扫描经常被访问的时候,就会存储很大的数据到Buffer Pool里面,我们都知道这是很危险的一件事。

所以MySQL给我们以下参数来设置保留在Buffer Pool里面的数据在插入时候没有被改变list位置的时候的保存时间innodb_old_blocks_time,单位是毫秒,这个值的默认值是1000。如果增大这个值的话,就会让Buffer Pool里面很多页信息变老的速度变快,这个很好理解吧,因为这些数据会不会很快被内存中擦除的话,就会变成热数据而挤掉原有缓存的数据。

以上的两个参数都是可以动态设置的,当然也可以在my.cnf里面设置。当然设置这些前一定要对机器配置,表信息,负载情况有充分的了解才能进行设置,生产库尽量不要随便修改。如果OLTP系统中有大量的大查询的话,设置innodb_old_blocks_time能够较大的提供系统的稳定性。

如果当一个大查询很大不足够存储到Buffer Pool当中的时候,我们可以指定innodb_old_blocks_pct的值小一点,以保证这些数据只会被读取一次,比如说设置为5的时候,就就限制了一次读取数据最多只能被读取到Buffer Pool当中5%。一些小表并且是经常访问到的数据的话就可以适当设置较大的值,比如50。当然设置这两个值的时候一定要建立在你充分了解你的数据负载的基础上,不然千万不要乱改。

4、InnoDB Buffer Pool预读

我们可以控制MySQL何时以何种方式预读数据进入Buffer Pool。 预读就是IO异步读取多个页数据读入Buffer Pool的一个过程,并且这些页被认为是很快就会被读取到的,当需要读取这些数据的时候就会将需要的页放在一个区当中,InnoDB就是通过两次预读的方式来提高IO读写的性能。

线性预读:能够预测将有那些数据很快能被读到的一种技术,因为Buffer Pool中的页数据是顺序访问的。我们可以通过设置innodb_read_ahead_threshold参数控制MySQL何时进行预读,也可以控制MySQL预读数据时候对于数据的敏感度,如果Buffer Pool里面存储的数据页的频繁值大于innodb_read_ahead_threshold的值,INNODB就会启动一个异步的预读操作,innodb_read_ahead_threshold的值可以设置为0-64的任何一个值。默认值是56,值设置的越高就会造成检索更加严格。当设置为8的时候,只有小于8个页数据被读进Buffer Pool中才会被检索。

随机预读:随机预读能够将在内存当中的将被读取到的页信息很快的组织到一个区中,而且这些页面的读取顺序不用分顺序,InnoDB能够很快调度Buffer Pool当中相似的很多页的信息,并且发出请求。这些页可能并不是连续的,要想使用这个功能就要设置innodb_random_read_ahead 这个参数为ON。

5、动态调整MySQL后台进程刷新

我们可以控制MySQL后台进程何时刷新,或者根据MySQL负载的情况动态调整。 InnoDB会自动维护后台作业自动从Buffer Pool当中清除脏数据,当Buffer Pool中的脏页占用比达到innodb_max_dirty_pages_pct_lwm的设定值的时候,就会自动将脏页清出Buffer Pool,这是为了保证Buffer Pool当中脏页的占有率,也是为了防止脏页占有率超过innodb_max_dirty_pages_pct的设定值,当脏页的占有率达到了innodb_max_dirty_pages_pct的设定值的时候,InnoDB就会强制刷新Buffer Pool Pages。

InnoDB采用一种基于redo log的最近生成量和最近刷新频率的算法来决定冲洗速度。这样的算法可以保证数据库的冲洗不会影响到数据库的性能,也能保证数据库Buffer Pool中的数据的脏数据的占用比。这种自动调节的方式还能够防止突然的并发redo变大,但是flush的时候将不能进行普通的IO读写操作。

我们知道InnoDB使用日志的方式是循环使用的,在重用前一个日志文件之前,InnoDB就会将这个日志这个日志记录相关的所有在Buffer Pool当中的数据刷新到磁盘,也就是所谓的sharp checkpoint,和sqlserver的checkpoint很像。当一个插入语句产生大量的redo信息,需要记录的日志当前redo log文件不能够完全存储,也会写入到当前的redo 文件当中。当redo log当中的所有使用空间都被用完了的,就会触发 sharp checkpoint,所以这个时候即使脏数据占有率没有达到innodb_max_dirty_pages_pct ,还是会进行刷新。这种算法是经得住考验的,所以说千万不要随便设置,最好是默认值。但是我们从中也就会知道为什么一个事物的redo信息只能记录在一个redo log文件当中了。

因为有这么多的好处,所以 innodb_adaptive_flushing的值默认就是true的。

6、buffer poll缓冲保留

我们可以配置的InnoDB如何保留当前的缓冲池的状态,以避免在服务器重新启动后,还要经历一个漫长的暖机时间。

通过innodb_buffer_pool_dump_at_shutdown来设置,当设置这些参数以后,MySQL就会在机器重启后快速恢复以前内存中的数据,当然这些数据是从磁盘重新读取到Buffer Pool当中的,个人认为这个值还是需要配置一下的,当然这会花费一些时间,在重新读取这些数据到内存当中的时候,新的DML操作是不能够进行操作的。

这些数据是怎么恢复呢?其实 InnoDB_BUFFER_PAGE_LRU 表(INFORMATION_SCHEMA )会记录缓存的table ID和page ID,通过这个来恢复。 在LOAD数据进入Buffer Pool之前,可以设置Buffer Pool恢复数据的百分比,当然默认值肯定是100,不设置默认就是全部恢复。

SET GLOBAL innodb_buffer_pool_dump_pct=40;

通过以下的语句,设置是否重启服务器的时候重新LOAD数据进入Buffer Pool,默认是关闭的,还可以在启动时候指定或者在my.cnf当中指定:

SET GLOBAL innodb_buffer_pool_dump_at_shutdown=ON;

如果我们保存了Buffer Pool的信息,也可以在启动MySQL时候指定重新LOAD数据:

SET GLOBAL innodb_buffer_pool_dump_now=ON;保存信息,在重启前要指定 SET GLOBAL innodb_buffer_pool_load_now=ON;LOAD信息

如果要终止Buffer Pool加载,可以指定负载运行:

SET GLOBAL innodb_buffer_pool_load_abort=ON;

也可以通过以下的命令查看状态:

SHOW STATUS LIKE 'Innodb_buffer_pool_dump_status'; SHOW STATUS LIKE 'Innodb_buffer_pool_load_status';

而且我们可以通过innodb 的performance schema监控Buffer Pool的 LOAD状态:

  • 打开或者关闭stage/innodb/buffer pool load
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'stage/innodb/buffer%';
  • 打开以下参数来获取最近的Buffer Pool的dump状态:
SET GLOBAL innodb_buffer_pool_dump_now=ON; SET GLOBAL innodb_buffer_pool_load_now=ON; SHOW STATUS LIKE 'Innodb_buffer_pool_dump_status'G SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_current; SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_history;

需要留意的是,如果是压缩表的话,在读取到Buffer Pool的时候还是会保持压缩的格式。直到被读取的时候才会调用解压程序进行解压。

四、结语

InnoDB Buffer Pool不可不说是MySQL的核心功能之一,合理的配置InnoDB Buffer Pool能够显著地提高我们数据库的性能。而且本身InnoDB Buffer Pool的配置也给予我们很高的可控性,我们可以根据自己的业务场景,负载等寻找较优的配置。

不过这些都要建立在你对自己的MySQL服务充分了解的基础上,不然可能会适得其反。最后,如果有不对的地方,欢迎拍砖。

Mysql InnoDB 共享表空间和独立表空间

前言:学习mysql的时候总是习惯性的和oracle数据库进行比较。在学习mysql InnoDB的存储结构的时候也免不了跟oracle进行比较。Oracle的数据存储有表空间、段、区、块、数据文件;mysql InnoDB的存储管理也类似,但是mysql增加了一个共享表空间和独立表空间的概念;

未分类

一、概念

共享表空间: Innodb的所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制。从Innodb的官方文档中可以看到,其表空间的最大限制为64TB,也就是说,Innodb的单表限制基本上也在64TB左右了,当然这个大小是包括这个表的所有索引等其他相关数据。

独立表空间:

二、查看数据库的表空间

mysql> show variables like 'innodb_data%';

未分类

l 表空间有四个文件组成:ibdata1、ibdata2、ibdata3、ibdata4,每个文件的大小为10M,当每个文件都满了的时候,ibdata4会自动扩展;

l 当前的存储空间满的时候,可以在其他的磁盘添加数据文件,语法如下:语法如下所示:

pathtodatafile:sizespecification;pathtodatafile:sizespec;.;pathtodatafile:sizespec[:autoextend[:max:sizespecification]]

如果用 autoextend 选项描述最后一个数据文件,当 InnoDB 用尽所有表自由空间后将会自动扩充最后一个数据文件,每次增量为 8 MB。示例:

不管是共享表空间和独立表空间,都会存在innodb_data_file文件,因为这些文件不仅仅要存放数据,而且还要充当着类似于ORACLE的UNDO表空间等一些角色。

三、共享表空间优缺点

既然Innodb有共享表空间和独立表空间两种类型,那么这两种表空间存在肯定都有时候自己的应用的场景,存在即合理。以下是摘自mysql官方的一些介绍:

3.1 共享表空间的优点

表空间可以分成多个文件存放到各个磁盘,所以表也就可以分成多个文件存放在磁盘上,表的大小不受磁盘大小的限制(很多文档描述有点问题)。

数据和文件放在一起方便管理。

3.2 共享表空间的缺点

所有的数据和索引存放到一个文件,虽然可以把一个大文件分成多个小文件,但是多个表及索引在表空间中混合存储,当数据量非常大的时候,表做了大量删除操作后表空间中将会有大量的空隙,特别是对于统计分析,对于经常删除操作的这类应用最不适合用共享表空间。

共享表空间分配后不能回缩:当出现临时建索引或是创建一个临时表的操作表空间扩大后,就是删除相关的表也没办法回缩那部分空间了(可以理解为oracle的表空间10G,但是才使用10M,但是操作系统显示mysql的表空间为10G),进行数据库的冷备很慢;

四、独立表空间的优缺点

4.1 独立表空间的优点

每个表都有自已独立的表空间,每个表的数据和索引都会存在自已的表空间中,可以实现单表在不同的数据库中移动。

空间可以回收(除drop table操作处,表空不能自已回收)

Drop table操作自动回收表空间,如果对于统计分析或是日值表,删除大量数据后可以通过:alter table TableName engine=innodb;回缩不用的空间。

对于使innodb-plugin的Innodb使用turncate table也会使空间收缩。
对于使用独立表空间的表,不管怎么删除,表空间的碎片不会太严重的影响性能,而且还有机会处理。

4.2 独立表空间的缺点

单表增加过大,当单表占用空间过大时,存储空间不足,只能从操作系统层面思考解决方法;

五、共享表空间和独立表空间之间的转换

5.1 查看当前数据库的表空间管理类型

脚本:show variables like “innodb_file_per_table”;

mysql> show variables like "innodb_file_per_table";

未分类

ON代表独立表空间管理,OFF代表共享表空间管理;(查看单表的表空间管理方式,需要查看每个表是否有单独的数据文件)

5.2 修改数据库的表空间管理方式

修改innodb_file_per_table的参数值即可,但是修改不能影响之前已经使用过的共享表空间和独立表空间;

  • innodb_file_per_table=1 为使用独占表空间
  • innodb_file_per_table=0 为使用共享表空间

5.3共享表空间转化为独立表空间的方法(参数innodb_file_per_table=1需要设置)

单个表的转换操作,脚本:alter table table_name engine=innodb;
当有大量的表需要操作的时候,先把数据库导出,然后删除数据再进行导入操作,该操作可以用mysqldump进行操作(http://blog.itpub.net/12679300/viewspace-1259451/)
总结:经过以上操作便完成数据库的存储空间的转换,了解技术是为了更好的利用技术,当数据量很小的时候建议使用共享表空间的管理方式。数据量很大的时候建议使用独立表空间的管理方式。

MySQL数据恢复和复制对InnoDB锁机制的影响

MySQL通过BINLOG记录执行成功的INSERT,UPDATE,DELETE等DML语句。并由此实现数据库的恢复(point-in-time)和复制(其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQLl数据库,多称为slave,进行实时同步)。MySQL 5.5.x以后的版本支持3种日志格式。通过binlog_format参数设置。该参数影响了记录二进制日志的格式,十分重要。

  1. STATEMENT格式和之前的MySQL版本一样,二进制日志文件记录的是日志的逻辑SQL语句。

  2. ROW格式记录的不再是简单的SQL语句,而是记录表的每行记录更改的情况。

  3. 在MIXED格式下,MySQL默认采用STATEMENT格式进行二进制日志文件的记录。但是在一些特殊情况下会使用ROW格式,可能的情况如下:

  • 表的存储引擎为NDB,这时对表的DML操作都会以ROW格式记录。

  • 使用了UUID(),USER(),CURRENT_USER(),FOUND_ROWS(),ROW_COUNT()等不确定函数。

  • 使用了INSERT DELAY语句。

  • 使用了用户自定义函数(UDF).

  • 使用了临时表(temporary table) 。

对于基于语句的日志格式(STATEMENT)的恢复和复制而言,由于MySQL的BINLONG是按照事务(transaction)提交(committed)的先后顺序记录的,因此要正确恢复或者复制数据,就必须满足:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读(Phantom Problem)。这已经超过了ISO/ANSI SQL92″可重复读(Repeatable Read)”隔离级别的要求,实际上是要求事务要串行化。这也是许多情况下,InnoDB要用到Next-Key Lock锁的原因,比如用在范围条件更新记录时,无论是在Read Committed或者是Repeatable Read隔离级别下,InnoDB都要使用Next-key Lock锁。既然说到Next-key Lock锁机制,我这里简单说一下,演示各种效果就让童鞋们自己去测试了^_^

InnoDB锁的算法

innodb引擎有三种锁的算法设计:

  • Record lock:对单个索引项加锁
  • Gap lock:间隙锁,对索引项之间的”间隙”,第一条记录前的”间隙”或最后一条记录后的”间隙”加锁,不包括索引项本身
    Next-key lock:Gap lock+Next-key lock 锁定索引项范围。对记录及其前面的间隙加锁

注意:

  • 对于唯一索引,其加上的是Record Lock,仅锁住记录本身。但也有特别情况,那就是唯一索引由多个列组成,而查询仅是查找多个唯一索引列中的其中一个,那么加锁的情况依然是Next-key lock。

  • 对于辅助索引,其加上的是Next-Key Lock,锁定的是范围,包含记录本身。

  • 另外如果使用相等的条件给一个不存在的记录加锁,innodb也会使用Next-key lock

特别注意:

  • innodb存储引擎是通过给索引上的索引项加锁来实现,这意味着:只有通过索引条件检索数据,innodb才会使用行锁,否则,innodb将使用表锁。(Repeatable Read隔离级别下)

  • 如果是在表没有主键或者没有任何索引的情况下(并且是在read committed隔离级别)。如果一个表有主键,没有其他的索引,检索条件又不是主键,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,会在判断后放锁,最终持有的,是满足条件的记录上的锁,但是不满足条件的记录上的加锁/放锁动作不会省略。同时,优化也违背了2PL的约束。

对于”INSERT INTO target_tab SELECT * FROM source_tab WHERE….” 和”CREATE TABLE new_tab…SELECT….FROM source_tab WHERE…(CTAS)”这种SQL语句,用户并没有对source_tab做任何操作,但是MySQL会对这种SQL语句做特别的处理。我们来看一个实际的例子:

mysql> select * from source_tab;
+------+------+--------+
| id   | age  | name   |
+------+------+--------+
|    1 |   24 | yayun  |
|    2 |   24 | atlas  |
|    3 |   25 | david  |
|    4 |   24 | dengyy |
+------+------+--------+
4 rows in set (0.00 sec)

mysql> select * from target_tab;
Empty set (0.00 sec)

mysql> desc source_tab;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| age   | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> desc target_tab;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| age   | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> 

CTAS操作给原表加锁的例子

session1操作

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from  source_tab;
+------+------+--------+
| id   | age  | name   |
+------+------+--------+
|    1 |   24 | yayun  |
|    2 |   24 | atlas  |
|    3 |   25 | david  |
|    4 |   24 | dengyy |
+------+------+--------+
4 rows in set (0.00 sec)

mysql> insert into target_tab select * from source_tab where name='yayun';         #该语句执行以后,session2中的update操作将会等待
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> 

session2操作

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from source_tab;
+------+------+--------+
| id   | age  | name   |
+------+------+--------+
|    1 |   24 | yayun  |
|    2 |   24 | atlas  |
|    3 |   25 | david  |
|    4 |   24 | dengyy |
+------+------+--------+
4 rows in set (0.00 sec)

mysql> update source_tab set name='dengyayun' where name='yayun';  #一直等待,除非session1执行commit提交。
Query OK, 1 row affected (49.24 sec)                               #可以看见用了49秒,这就是在等待session1提交,当session1提交后,顺利更新
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> 

在上面示例中,只是简单的读source_tab表的数据,相当于执行一个普通的SELECT语句,用一致性读就可以了。Oracle正是这么做的,它通过MVCC技术实现的多版本并发控制实现一致性读,不需要给source_tab加任何锁。大家都知道InnoDB也实现了多版本并发控制(MVCC),对普通的SELECT一致性读,也不需要加任何锁;但是这里InnoDB却给source_tab表加了共享锁,并没有使用多版本一致性读技术。

MySQL为什么这么做呢?why?其原因还是为了保证恢复和复制的正确性。因为在不加锁的情况下,如果上述语句执行过程中,其他事务对原表(source_tab)做了更新操作,就可能导致数据恢复结果错误。为了演示错误的发生,再重复上面的例子,先将系统变量innodb_locks_unsafe_for_binlog的值设为”on”,默认值是off。

innodb_locks_unsafe_for_binlog

设定InnoDB是否在搜索和索引扫描中使用间隙锁(gap locking)。InnoDB使用行级锁(row-level locking),通常情况下,InnoDB在搜索或扫描索引的行锁机制中使用“下一键锁定(next-key locking)”算法来锁定某索引记录及其前部的间隙(gap),以阻塞其它用户紧跟在该索引记录之前插入其它索引记录。站在这个角度来说,行级锁也叫索引记录锁(index-record lock)。

默认情况下,此变量的值为OFF,意为禁止使用非安全锁,也即启用间隙锁功能。将其设定为ON表示禁止锁定索引记录前的间隙,也即禁用间隙锁,InnoDB仅使用索引记录锁(index-record lock)进行索引搜索或扫描,不过,这并不禁止InnoDB在执行外键约束检查或重复键检查时使用间隙锁。

启用innodb_locks_unsafe_for_binlog的效果类似于将MySQL的事务隔离级别设定为READ-COMMITTED,但二者并不完全等同:
innodb_locks_unsafe_for_binlog是全局级别的设定且只能在服务启动时设定,而事务隔离级别可全局设定并由会话级别继承,然而会话级别也以按需在运行时对其进行调整。类似READ-COMMITTED事务隔离级别,启用innodb_locks_unsafe_for_binlog也会带来“幻影问题(phantom problem)”,但除此之外,它还能带来如下特性:

  • 对UPDATE或DELETE语句来说,InnoDB仅锁定需要更新或删除的行,对不能够被WHERE条件匹配的行施加的锁会在条件检查后予以释放。这可以有效地降低死锁出现的概率;

  • 执行UPDATE语句时,如果某行已经被其它语句锁定,InnoDB会启动一个“半一致性(semi-consistent)”读操作从MySQL最近一次提交版本中获得此行,并以之判定其是否能够并当前UPDATE的WHERE条件所匹配。如果能够匹配,MySQL会再次对其进行锁定,而如果仍有其它锁存在,则需要先等待它们退出。

其无法动态修改,需要修改配置文件,演示如下:

CTAS操作不给原表加锁带来的安全问题

mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
1 row in set (0.00 sec)

mysql> show variables like 'innodb_locks_unsafe%';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_locks_unsafe_for_binlog | ON    |
+--------------------------------+-------+
1 row in set (0.00 sec)

mysql> 

session1操作

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from source_tab where id=1;
+------+------+-----------+
| id   | age  | name      |
+------+------+-----------+
|    1 |   24 | dengyayun |
+------+------+-----------+
1 row in set (0.00 sec)

mysql> insert into target_tab select * from source_tab where id=1;        
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> commit;    #插入操作后提交
Query OK, 0 rows affected (0.01 sec)

mysql> select * from source_tab where name='good yayun'; #此时查看数据,target_tab中可以插入source_tab更新前的结果,这复合应用逻辑
+------+------+------------+
| id   | age  | name       |
+------+------+------------+
|    1 |   24 | good yayun |
+------+------+------------+
1 row in set (0.00 sec)

mysql> select * from target_tab;
+------+------+-----------+
| id   | age  | name      |
+------+------+-----------+
|    1 |   24 | dengyayun |
+------+------+-----------+
1 row in set (0.00 sec)

session2操作

mysql> begin; 
Query OK, 0 rows affected (0.00 sec)

mysql> select * from source_tab where id=1;
+------+------+-----------+
| id   | age  | name      |
+------+------+-----------+
|    1 |   24 | dengyayun |
+------+------+-----------+
1 row in set (0.00 sec)

mysql> update source_tab set name='good yayun' where id=1;  # session1未提交,可以对session1中的select记录进行更新操作
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;       # 更新操作先提交
Query OK, 0 rows affected (0.02 sec)

mysql> select * from source_tab where name='good yayun';
+------+------+------------+
| id   | age  | name       |
+------+------+------------+
|    1 |   24 | good yayun |
+------+------+------------+
1 row in set (0.00 sec)

mysql> select * from target_tab;
+------+------+-----------+
| id   | age  | name      |
+------+------+-----------+
|    1 |   24 | dengyayun |
+------+------+-----------+
1 row in set (0.00 sec)

mysql> 

从上面的测试结果可以发现,设置系统变量innodb_locks_unsafe_for_binlog的值为”ON”后,innodb不再对原表(source_tab)加锁,结果也符合应用的逻辑,但是如果我们分析一下BINLOG内容,就可以发现问题所在

[root@MySQL-01 mysql]# mysqlbinlog mysql-bin.000120 | grep -A 20 'update source_tab'
update source_tab set name='good yayun' where id=1
/*!*/;
# at 468
#140401  2:04:12 server id 1  end_log_pos 495   Xid = 74
COMMIT/*!*/;
# at 495
#140401  2:04:23 server id 1  end_log_pos 563   Query   thread_id=5     exec_time=0     error_code=0
SET TIMESTAMP=1396289063/*!*/;
BEGIN
/*!*/;
# at 563
#140401  2:02:42 server id 1  end_log_pos 684   Query   thread_id=5     exec_time=0     error_code=0
SET TIMESTAMP=1396288962/*!*/;
insert into target_tab select * from source_tab where id=1
/*!*/;
# at 684
#140401  2:04:23 server id 1  end_log_pos 711   Xid = 73
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
[root@MySQL-01 mysql]# 

可以清楚的看到在BINLOG的记录中,更新操作的位置在INSERT……SELECT之前,如果使用这个BINLOG进行数据库恢复,恢复的结果则与实际的应用逻辑不符;如果进行复制,就会导致主从数据不一致!

通过上面的例子,相信童鞋们不难理解为什么MySQL在处理

“INSERT INTO target_tab SELECT * FROM source_tab WHERE….”

“CREATE TABLE new_tab….SELECT…..FROM source_tab WHERE….”

时要给原表(source_tab)加锁,而不是使用对并发影响最小的多版本数据来实现一致性读。还要特别说明的是,如果上述语句的SELECT是范围条件,innodb还会给原表加上Next-Key Lock锁。

因此,INSERT….SELECT和CREATE TABLE….SELECT…..语句,可能会阻止对原表的并发更新。如果查询比较复杂,会照成严重的性能问题,生产环境需要谨慎使用。

总结如下:

如果应用中一定要用这种SQL来实现业务逻辑,又不希望对源表的并发更新产生影响,可以使用下面3种方法:

  1. 将innodb_locks_unsafe_for_binlog的值设置为”ON”,强制MySQL使用多版本数据一致性读。但付出的代价是可能无法使用BINLOG正确的进行数据恢复或者主从复制。因此,此方法是不推荐使用的。

  2. 通过使用SELECT * FROM source_tab ….. INTO OUTFILE 和LOAD DATA INFILE…..语句组合来间接实现。采用这种放松MySQL不会给(源表)source_tab加锁。

  3. 使用基于行(ROW)的BINLOG格式和基于行的数据的复制。此方法是推荐使用的方法。

Jenkins 邮件配置 (使用 Jenkins Email Extension Plugin)

前言

jenkins 总算调试好了,也能进行自动打包并发送到蒲公英内测平台,接下来开始细节改造

jenkins 的邮件通知

  • jenkins 内置的邮件功能

  • 使用插件扩展的邮件功能

内置邮件功能

默认使用java sdk中api进行邮件发送操作。配置简单。功能单一。

(图一)

步骤,系统管理—系统配置

这里写图片描述

未分类
图1

这里写图片描述

未分类
图2

注意发送服务器的设置。

引用链接:–

使用插件扩展的邮件功能

使用EmailExt 插件可以定义邮件内容。方便直观。

下载插件

系统管理—管理插件– 进入页面在搜索输入框中输入__ Email Ext

选择EmailExt 插件安装完成。

进入需要设置EmailExt插件的项目—-build 配置界面

未分类
图4

构建后任务,会多出来一个选项。选中就可以进行配置了

注意:在这之前,可以进行Email 发送模板创建。

默认模板 :

Default Subject:构建通知:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!

Default Content:

<hr/>

(本邮件是程序自动下发的,请勿回复!)<br/><hr/>

项目名称:$PROJECT_NAME<br/><hr/>

构建编号:$BUILD_NUMBER<br/><hr/>

svn版本号:${SVN_REVISION}<br/><hr/>

构建状态:$BUILD_STATUS<br/><hr/>

触发原因:${CAUSE}<br/><hr/>

构建日志地址:<a href="${BUILD_URL}console">${BUILD_URL}console</a><br/><hr/>

构建地址:<a href="$BUILD_URL">$BUILD_URL</a><br/><hr/>

变更集:${JELLY_SCRIPT,template="html"}<br/><hr/>

未分类
图5

注意:这里以qq邮箱为例,需要设置邮箱账户和密码

未分类
图6

ok到这里就差不多,别忘了设置触发条件,在Emailext 选项卡的高级按钮下。

发送成功模板

图7
未分类

发送成功。

接下来,打包任务是不是随时都可以了,不用开启 android studio了。那么打包任务是不是可以让测试们打了呢?

引用:

设置qq 邮件 :
http://jingyan.baidu.com/article/e3c78d647d32bc3c4c85f5e5.html

Jenkins~通过WebDeploy实现自动部署

Jenkins以之前的文章中已经有所介绍,主要集成了自动化部署的功能,而对于自动化部署来说是由多个组件组成的,每个组件负责自己的事,如今天说的webDeploy,它主要实现将网站文件动态发布到另一台IIS服务器,并且它是决断安全的,最起始比net use要安全!

Jenkins自动化发布的一些组件

--------------------------jenkins核心功能-----------------

建立项目

配置项目的源代码路径(svn,git,tfs)

配置工作区里相对路径(工作区就是你的项目,相对目录就是在项目路径下再建立一个子文件夹)

获取源代码

发布源代码(配置web.config生产环境参数,iis用户,网络目录权限(local_service),msbuild)

将发布后的目标网络(msbuild+webDeploy 或者 powershell)

---------------------------------------------------

WebDeploy实现过程(IIS服务器)

  • 安装IIS管理工具
  • 开启远程连接,设置对应端口
  • 添加权限用户
  • 配置网站目录权限
  • 打开项目,配置发布,选择IIS/FTP
  • 输入发布参数,IIS服务器地址,用户名和密码,网站名称等

1、安装IIS管理工具

未分类

2、开启远程连接,设置对应端口

未分类

3、添加权限用户

未分类

4、配置网站目录权限

未分类

打开项目,配置发布,选择IIS/FTP

未分类

6、输入发布参数,IIS服务器地址,用户名和密码,网站名称等

未分类

然后发布已经后,就可以发到我们的IIS服务器了!

现在webDeploy和jenkins合并,确实有点双剑合并的感觉!

Ubuntu 16.04 安装 Jenkins

安装、启动并配置jenkins服务

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
systemctl status jenkins.service
● jenkins.service - LSB: Start Jenkins at boot time
Loaded: loaded (/etc/init.d/jenkins; bad; vendor preset: enabled)
Active: failed (Result: exit-code) since 一 2017-09-18 20:02:02 CST; 5min ago

 Docs: man:systemd-sysv-generator(8)

9月 18 20:02:02 DH9K45ER2 systemd[1]: Starting LSB: Start Jenkins at boot time...
9月 18 20:02:02 DH9K45ER2 jenkins[4785]: ERROR: No Java executable found in current PATH: /bin:/usr/bin:/sbin:/usr/sbin
9月 18 20:02:02 DH9K45ER2 jenkins[4785]: If you actually have java installed on the system make sure the executable is in the aforementioned path and that 't
9月 18 20:02:02 DH9K45ER2 systemd[1]: jenkins.service: Control process exited, code=exited status=1
9月 18 20:02:02 DH9K45ER2 systemd[1]: Failed to start LSB: Start Jenkins at boot time.
9月 18 20:02:02 DH9K45ER2 systemd[1]: jenkins.service: Unit entered failed state.
9月 18 20:02:02 DH9K45ER2 systemd[1]: jenkins.service: Failed with result 'exit-code'.
cat /etc/services | grep 8080 
http-alt 8080/tcp webcache # WWW caching service
http-alt 8080/udp

从日志看出Jenkins在默认情况下从8080端口启动。在这个系统端口中,8080已经在使用中了。所以在/etc/default/jenkins.修改Jenkins默认端口设置

修改/etc/default/目录下面的jenkins文件,将以下内容

# port for HTTP connector (default 8080; disable with -1)
HTTP_PORT=8080

修改为:

# port for HTTP connector (default 8080; disable with -1)
HTTP_PORT=8089

启动停止Jenkins

sudo service jenkins start
sudo service jenkins stop

访问路径:http://localhost:8089

其他说明

  • 安装路径:/var/lib/jenkins
  • 日志路径:/var/log/jenkins/jenkins.log

Jenkins初始化配置

1、解锁Jenkins

这里需要/var/lib/jenkins/secrets/initialAdminPassword的密码解锁Jenkins
然后把密码复制进去。

说明:按照弹框提示,找到该initialAdminPassword文件,我这里使用Docker启动Jenkins,并且把jenkins_home目录挂载到我磁盘指定目录,所以这里我只需要复制/Users/wanyang3/jenkins_home/initialAdminPassword即可,如果非挂载方式Docker启动,则需要进入容器内根据提示路径找到该文件。

2、定制 Jenkins

说明:这里若选择Install suggested plugins安装,那么jenkins就会给你推荐安装一些很有用的插件,若选择Select plugins to install安装,那么就需要自己根据业务需要选择性安装某些插件。

3、创建第一个管理员用户

说明:这里创建第一个管理员用户,也可以不设置,直接点击“Continue as admin”,进入jenkins以后再设置。

新建Pipeline Job Demo

创建一个pipeline job

/var/lib/jenkins/config.xml
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.GlobalMatrixAuthorizationSt
rategy">

<permission>hudson.model.Computer.Configure:admin</permission> 
<permission>hudson.model.Computer.Connect:admin</permission> 
<permission>hudson.model.Computer.Create:admin</permission>
<permission>hudson.model.Computer.Delete:admin</permission>
<permission>hudson.model.Computer.Disconnect:admin</permission> 
<permission>hudson.model.Hudson.Administer:admin</permission> 
<permission>hudson.model.Hudson.Read:admin</permission> 
<permission>hudson.model.Hudson.Read:anonymous</permission> 
<permission>hudson.model.Hudson.RunScripts:admin</permission> 
<permission>hudson.model.Item.Build:admin</permission> 
<permission>hudson.model.Item.Cancel:admin</permission> 
<permission>hudson.model.Item.Configure:admin</permission> 
<permission>hudson.model.Item.Create:admin</permission> 
<permission>hudson.model.Item.Delete:admin</permission> 
<permission>hudson.model.Item.Discover:admin</permission> 
<permission>hudson.model.Item.Read:admin</permission> 
<permission>hudson.model.Item.Workspace:admin</permission> 
<permission>hudson.model.Run.Delete:admin</permission> 
<permission>hudson.model.Run.Update:admin</permission> 
<permission>hudson.model.View.Configure:admin</permission> 
<permission>hudson.model.View.Create:admin</permission> 
<permission>hudson.model.View.Delete:admin</permission> 
<permission>hudson.model.View.Read:admin</permission> 
<permission>hudson.scm.SCM.Tag:admin</permission> 
</authorizationStrategy> 

</authorizationStrategy>
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">

<disableSignup>false</disableSignup> 
<enableCaptcha>false</enableCaptcha> 

修改用户密码

/var/lib/jenkins/users/user/config.xml
<hudson.security.HudsonPrivateSecurityRealm_-Details>
<passwordHash>#jbcrypt:$2a$10$DdaWzN64JgUtLdvxWIflcuQu2fgrrMSAMabF5TSrGK5nXitqK9ZMS</passwordHash>
</hudson.security.HudsonPrivateSecurityRealm_-Details>

注:密码为111111

Jenkins详细安装配置

使用docker安装Jenkins

安装docker

yum install -y docker

启动docker

service docker start

创建一个目录保存Jenkins数据

mkdir /home/data
chmod -R 777 /home/data
docker run --name myjenkins -p 8080:8080 -p 50000:50000 -v /home/data:/var/jenkins_home jenkins

启动成之后,开始配置Jenkins

使用游览器访问

http://IP:8080

这时候出现如下画面,需要输入Administrator password

未分类

执行命令

docker exec -it myjenkins cat /var/jenkins_home/secrets/initialAdminPassword

就能查到Administrator password

未分类

开始安装插件

标准安装和自定义安装,可以点击标准安装,直接开始安装

未分类

这里等的时间会比较长一点

安装成功之后,设置用户名和密码

未分类

安装成功之后显示

未分类