php soap

SOAP 是基于XML和HTTP通讯协议,XML各个平台,各种语言都支持的一种语言。

WSDL 是网络服务描述语言(Web Services Description Language),是一种使用XML格式的文档。这种文档可描述某个Web Service。可规定服务的位置,及服务提供的操作。

不同语言之间需要通信(例如:PHP,Java,c),可以通过SOAP,WSDL使不同操作系统,不同技术的编程语言互相通信。

blob.png

SOAP有两种操作方式,NO-WSDL 与 WSDL。

NO-WSDL模式:使用参数来传递要使用的信息

WSDL模式: 使用WSDL文件名作为参数,并从WSDL中提取服务所需的信息。(每次修改都需要修改client与server的wsdl文件,没有NO-WSDL模式灵活,以后再介绍这种模式的使用)

SOAP中主要用到三个类,SOAPServer,SOAPClient,SOAPFault

NO-WSDL模式:

soapHandle.class.php 处理请求

<?php  
class soapHandle{  
    public function strtolink($url=''){  
        return sprintf('<a href="%s">%s</a>', $url, $url);  
    }  
}  
?>

server.php soap服务端

<?php  
  
// 服务器验证  
if ($_SERVER['PHP_AUTH_USER']!='fdipzone' || $_SERVER['PHP_AUTH_PW']!='123456') {  
      header('WWW-Authenticate: Basic realm="MyFramework Realm"');  
      header('HTTP/1.0 401 Unauthorized');  
      echo "You must enter a valid login ID and password to access this resource.\n";  
      exit;  
}  
  
require("soapHandle.class.php"); // 处理请求的class  
  
try{  
    $server = new SOAPServer(null, array('uri'=>'http://demo.fdipzone.com/soap/server.php'));  
    $server->setClass('soapHandle'); //设置处理的class  
    $server->handle();  
}catch(SOAPFault $f){  
    echo $f->faultString; // 打印出错信息  
}  
  
?>

client.php soap客户端

< ?php

try {
    $client = new SOAPClient(null, array('location' = >'http://demo.fdipzone.com/soap/server.php', // 设置server路径  
    'uri' = >'http://demo.fdipzone.com/soap/server.php', 'login' = >'fdipzone', // HTTP auth login  
    'password' = >'123456' // HTTP auth password  
    ));

    echo $client - >strtolink('http://blog.csdn.net/fdipzone').'<br>'; // 直接调用server方法  
    echo $client - >__soapCall('strtolink', array('http://blog.csdn.net/fdipzone')); // 间接调用server方法  
} catch(SOAPFault $e) {
    echo $e - >getMessage();
}

? >

Header验证例子:

server.php

<?php  
  
// 服务器验证    
if ($_SERVER['PHP_AUTH_USER']!='fdipzone' || $_SERVER['PHP_AUTH_PW']!='123456') {  
    header('WWW-Authenticate: Basic realm="NMG Terry"');  
    header('HTTP/1.0 401 Unauthorized');  
    echo "You must enter a valid login ID and password to access this resource.\n";  
    exit();  
}  
  
require 'SOAPHandle.class.php';  
  
$config = array(  
        'uri' => 'http://demo.fdipzone.com/soap/server.php'  
);  
  
$oHandle = new SOAPHandle;  
  
// no wsdl mode  
try{  
  
    $server = new SOAPServer(null, $config);  
    $server->setObject($oHandle);  
    $server->handle();  
  
}catch(SOAPFault $f){  
  
    echo $f->faultString;  
  
}  
  
?>

client.php

< ?php

$config = array('location' = >'http://demo.fdipzone.com/soap/server.php', 'uri' = >'http://demo.fdipzone.com/soap/server.php', 'login' = >'fdipzone', 'password' = >'123456', 'trace' = >true);

try {

    $auth = array('fdipzone', '654321');

    // no wsdl  
    $client = new SOAPClient(null, $config);
    $header = new SOAPHeader('http://demo.fdipzone.com/soap/server.php', 'auth', $auth, false, SOAP_ACTOR_NEXT);
    $client - >__setSoapHeaders(array($header));

    $revstring = $client - >revstring('123456');
    $strtolink = $client - >__soapCall('strtolink', array('http://blog.csdn.net/fdipzone', 'fdipzone blog', 1));
    $uppcase = $client - >__soapCall('uppcase', array('Hello World'));

    echo $revstring.'<br>';
    echo $strtolink.'<br>';
    echo $uppcase.'<br>';

} catch(SOAPFault $e) {
    echo $e - >getMessage();
}

? >

SOAPHandle.class.php

<?php  
  
class SOAPHandle{ // class start  
  
    // header 驗證  
    public function auth($auth){  
        if($auth->string[0]!='fdipzone' || $auth->string[1]!='654321'){  
            throw new SOAPFault('Server', 'No Permission');  
        }  
    }  
  
    // 反轉字符串  
    public function revstring($str=''){  
        return strrev($str);  
    }  
  
    // 字符傳轉連接  
    public function strtolink($str='', $name='', $openwin=0){  
        $name = $name==''? $str : $name;  
        $openwin_tag = $openwin==1? ' target="_blank" ' : '';  
        return sprintf('<a href="%s" %s>%s</a>', $str, $openwin_tag, $name);  
    }  
  
    // 字符串轉大寫  
    public function uppcase($str){  
        return strtoupper($str);  
    }  
  
  
} // class end  
  
?>

SOAPHeader 第四与第五个参数说明:

Must Understand

这个参数指明了, 是否服务端必须要响应SoapHeader, 如果这个参数为真, 而服务端并不能识别响应的Header,则会引发一个Soap Fault(Header not understood)。

SOAP_ACTOR_NEXT

actor指明了SoapHeader要传递给谁, 被哪个Service处理。

SOAP_ACTOR_NEXT的意思就是, 下一个接受到这个请求头的Service。

在SoapServer的构造函数中, 我们可以指明一个Server的Actor, 比如:

<?php  
$config = array(  
            'uri' => 'http://demo.fdipzone.com/soap/server.php',  
            'actor' => 'myserver'  
);  
$server = new SOAPServer(null, $config);  
?>

然后就可以在Client的SoapHeader中, 通过设置actor是myserver, 来让指定的Server来获得我们设置的头部的信息。

WSDL找到感觉不错的文章 以后可能会用到

http://blog.csdn.net/cwt0408/article/details/6952936

Linux系统下用find命令查找最近修改过的文件

linux的终端上,没有windows的搜索那样好用的图形界面工具,但find命令确是很强大的。

  比如按名字查找一个文件,可以用 find / -name targetfilename 。 唉,如果只知道名字,不知道地点,这样也不失为一个野蛮有效的方法。 

  按时间查找也有参数 -atime 访问时间 -ctime 改变状态的时间 -mtime修改的时间。但要注意,这里的时间是以24小时为单位的。查看man手册后使用,你会很迷惑: -mtime n: Files data was last modified n*24 hours ago. 字面上的理解是最后一次修改发生在n个24小时以前的文件,但实际上

  find ./ -mtime 0:返回最近24小时内修改过的文件。

  find ./ -mtime 1 : 返回的是前48~24小时修改过的文件。而不是48小时以内修改过的文件。

  那怎么返回10天内修改过的文件?find还可以支持表达式关系运算,所以可以把最近几天的数据一天天的加起来:

  find ./ -mtime 0 -o -mtime 1 -o -mtime 2 ……虽然比较土,但也算是个方法了。

  还有没有更好的方法,我也想知道。

  另外, -mmin参数-cmin / – amin也是类似的。

 

wanqi@wanqi-System-Product-Name:~/huiye_QRD_e8/7x27a-11302301$ find ./packages/apps/ -name *.java -mtime 2
./packages/apps/filebrowser2/src/com/android/filebrowser/MyFile.java
./packages/apps/filebrowser2/gen/com/android/filebrowser/Manifest.java
./packages/apps/filebrowser2/gen/com/android/filebrowser/R.java
./packages/apps/Settings/src/com/android/settings/multisimsettings/MultiSimManager2.java
./packages/apps/Settings/src/com/android/settings/multisimsettings/MultiSimUtil.java
./packages/apps/Camera/src/com/android/camera/ListPreference.java
./packages/apps/Camera/src/com/android/camera/ui/PreferenceAdapter.java
wanqi@wanqi-System-Product-Name:~/huiye_QRD_e8/7x27a-11302301$ find ./packages/apps/ -name *.java -mtime 2
./packages/apps/filebrowser2/src/com/android/filebrowser/MyFile.java
./packages/apps/filebrowser2/gen/com/android/filebrowser/Manifest.java
./packages/apps/filebrowser2/gen/com/android/filebrowser/R.java

python中的urlencode与urldecode

当url地址含有中文,或者参数有中文的时候,这个算是很正常了,但是把这样的url作为参数传递的时候(最常见的callback),需要把一些中文甚至'/'做一下编码转换。

一、urlencode

urllib库里面有个urlencode函数,可以把key-value这样的键值对转换成我们想要的格式,返回的是a=1&b=2这样的字符串,比如:

>>> from urllib import urlencode 
>>> data = { 
... 'a': 'test', 
... 'name': '魔兽' 
... } 
>>> print urlencode(data) 
a=test&amp;name=%C4%A7%CA%DE

如果只想对一个字符串进行urlencode转换,怎么办?urllib提供另外一个函数:quote()

>>> from urllib import quote 
>>> quote('魔兽') 
'%C4%A7%CA%DE'

二、urldecode

当urlencode之后的字符串传递过来之后,接受完毕就要解码了——urldecode。urllib提供了unquote()这个函数,可没有urldecode()!

>>> from urllib import unquote 
>>> unquote('%C4%A7%CA%DE') '\xc4\xa7\xca\xde' 
>>> print unquote('%C4%A7%CA%DE') 
魔兽

三、讨论

在做urldecode的时候,看unquote()这个函数的输出,是对应中文在gbk下的编码,在对比一下quote()的结果不难发现,所谓的urlencode就是把字符串转车gbk编码,然后把\x替换成%。如果你的终端是utf8编码的,那么要把结果再转成utf8输出,否则就乱码。

可以根据实际情况,自定义或者重写urlencode()、urldecode()等函数。

python爬虫练手

51无聊谢了个爬虫玩

python写这类小东西真的很方便

__author__='LeoKim'
from bs4 import BeautifulSoup
 
import re
import urllib.request, urllib.parse, http.cookiejar
import json
import time
import pymysql

conn=pymysql.connect(host='localhost',user='root',passwd='superhero',db='python_test',port=3306,charset='utf8')
cur=conn.cursor()#获取一个游标

#通过链接获取每页的小区名
def getVillage(url):
	cj = http.cookiejar.CookieJar()
	opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
	opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'),
	('Cookie', 'select_city=320100; lianjia_uuid=c73af582-9ed7-42ed-9738-bbd4688c67e0; UM_distinctid=15bb9f33ca387c-0ac15874ad5d0d-6a11157a-1fa400-15bb9f33ca4a02; _jzqckmp=1; all-lj=c28812af28ef34a41ba2474a2b5c52c2; _jzqx=1.1493473537.1493544561.2.jzqsr=nj%2Elianjia%2Ecom|jzqct=/ershoufang/gulou/.jzqsr=nj%2Elianjia%2Ecom|jzqct=/xiaoqu/pg1/; _gat=1; _gat_past=1; _gat_global=1; _gat_new_global=1; _gat_dianpu_agent=1; _smt_uid=59049861.8870a59; CNZZDATA1253492138=835595246-1493470448-null%7C1493541950; CNZZDATA1254525948=1922726511-1493470772-null%7C1493540995; CNZZDATA1255633284=630946367-1493469955-null%7C1493543402; CNZZDATA1255604082=270979082-1493468920-null%7C1493544528; _qzja=1.1520598967.1493473405458.1493480837509.1493544561423.1493544849473.1493544851953.0.0.0.29.3; _qzjb=1.1493544561423.10.0.0.0; _qzjc=1; _qzjto=10.1.0; _jzqa=1.2414222906473966000.1493473537.1493480838.1493544561.3; _jzqc=1; _jzqb=1.10.10.1493544561.1; _ga=GA1.2.1108117219.1493473408; _gid=GA1.2.2091828031.1493544853; lianjia_ssid=5c8ebd96-81f4-4430-bfda-6d941fcb8663')]

	urllib.request.install_opener(opener)

	html_bytes = urllib.request.urlopen(url).read()
	html_string = html_bytes.decode('utf-8')
	return html_string



def start(start_url):
	html_doc = getVillage(start_url)
	soup = BeautifulSoup(html_doc, 'html.parser')

	#获取所有页数和现在页数
	totalPageNoDiv=soup.find("div","house-lst-page-box")
	Page = eval(totalPageNoDiv.attrs['page-data'])

	totalPageNo = Page['totalPage']
	curPage = Page['curPage']

	print('当前正在抓取第'+str(curPage)+'页,共'+str(totalPageNo)+'页.')


	#获取小区内容
	divs = soup.find_all("div","title")
	for div in divs:
		a_tag = div.find("a",target="_blank")
		if(a_tag):
			#插入数据库
			sql = "INSERT INTO `village` (`name`) VALUES (%s)"
			cur.execute(sql, (a_tag.string))

	curPage = curPage + 1;
	if(totalPageNo == curPage-1):
		print('执行完毕.')
	else:
		time.sleep(10)
		start_url = "http://nj.lianjia.com/xiaoqu/pg"+str(curPage)
		start(start_url)


totalPageNo=1
curPage=1

start_url = "http://nj.lianjia.com/xiaoqu/pg"+str(curPage)
start(start_url)


cur.close()#关闭游标
conn.close()#释放数据库资源
__author__='LeoKim'
from bs4 import BeautifulSoup
import pymysql
import urllib.request, urllib.parse, http.cookiejar
from urllib import parse
import pymysql

conn=pymysql.connect(host='localhost',user='root',passwd='superhero',db='python_test',port=3306,charset='utf8')
cur=conn.cursor()#获取一个游标

def getgeohash(keyword):
	key={
		'keyword':keyword
	}


	url='https://mainsite-restapi.ele.me/v2/pois?extras%5B%5D=count&geohash=wtsm0ss7yfj8&limit=20&type=nearby&'+parse.urlencode(key)

	cj = http.cookiejar.CookieJar()
	opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
	opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'),
	('Cookie', 'ubt_ssid=but8xnmtkpfrbvypd9z3hxaa5i8ugmj0_2017-04-29; _utrace=edd9bb6de13caed667d2cf273d73fc0a_2017-04-29')]

	urllib.request.install_opener(opener)

	html_bytes = urllib.request.urlopen(url).read()
	html_string = html_bytes.decode('utf-8')
	soup = BeautifulSoup(html_string, 'html.parser')

	try:
		info = eval(soup.prettify())
		if len(info) and info is not None:
			return info[0]
		else:
			return 'error'	
	except:
		return 'error'
	

sql = "SELECT id,name FROM `village` where geohash is null"
cur.execute(sql)
data = cur.fetchall()

for d in data:
	print(d[0])

	geohash=''
	latitude=''
	longitude=''

	gh=getgeohash(d[1])

	if gh=='error':
		geohash='error'
		latitude=''
		longitude=''
	else:
		geohash = gh['geohash']
		latitude = gh['latitude']
		longitude = gh['longitude']

	print(geohash,latitude,longitude)

# gh['geohash'] is None

	sql = "UPDATE `village` SET geohash=%s,latitude=%s,longitude=%s where id=%s"
	cur.execute(sql, (geohash,latitude,longitude,d[0]))
	
cur.close()#关闭游标
conn.close()#释放数据库资源
__author__='LeoKim'
from bs4 import BeautifulSoup
import pymysql
import urllib.request, urllib.parse, http.cookiejar
from urllib import parse
import pymysql
import json
import re

conn=pymysql.connect(host='localhost',user='root',passwd='superhero',db='python_test',port=3306,charset='utf8')
cur=conn.cursor()#获取一个游标

def getstore(village_id,geohash,latitude,longitude,limit):
	key={
		'geohash':geohash,
		'latitude':latitude,
		'longitude':longitude,
		'limit':limit
	}


	url='https://mainsite-restapi.ele.me/shopping/restaurants?extras%5B%5D=activities&offset=0&terminal=web'+parse.urlencode(key)

	cj = http.cookiejar.CookieJar()
	opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
	opener.addheaders = [('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36'),
	('Cookie', 'ubt_ssid=but8xnmtkpfrbvypd9z3hxaa5i8ugmj0_2017-04-29; _utrace=edd9bb6de13caed667d2cf273d73fc0a_2017-04-29')]

	urllib.request.install_opener(opener)

	html_bytes = urllib.request.urlopen(url).read()
	html_string = html_bytes.decode('utf-8')
	soup = BeautifulSoup(html_string, 'html.parser')

	info = soup.prettify()
	jsonData = json.loads(info)
	

	for data in jsonData:

		print(data['id'])
		print(village_id)
		print(data['name'])
		print(data['recent_order_num'])
		print(data['address'])
		print(data['order_lead_time'])
		print(data['float_delivery_fee'])

		average_cost=0
		if 'average_cost' in data:
			cost = re.findall(r'\d+', data['average_cost'])
			average_cost=cost[0]
			print(average_cost)

		print(data['rating'])
		print('---------------------------------------------')

		shop_id = data['id']
		name = data['name']
		address = data['address']
		recent_order_num = data['recent_order_num']
		order_lead_time = data['order_lead_time']
		float_delivery_fee = data['float_delivery_fee']
		rating = data['rating']

		sql = "INSERT INTO `store` (`shop_id`,`village_id`,`name`,`address`,`recent_order_num`,`order_lead_time`,`float_delivery_fee`, `average_cost`, `rating`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
		cur.execute(sql, (shop_id,village_id,name, address, recent_order_num, order_lead_time, float_delivery_fee, average_cost, rating))


# getstore('wtst84g4g0u','31.91988','118.83238',30)

sql = "SELECT id,name,geohash,latitude,longitude FROM `village` where id >482 and geohash is not null"
cur.execute(sql)
data = cur.fetchall()

for d in data:
	village_id=d[0]
	geohash = d[2]
	latitude = d[3]
	longitude = d[4]

	getstore(village_id,geohash,latitude,longitude,30)


cur.close()#关闭游标
conn.close()#释放数据库资源

MYSQL mysqldump备份实验

0. meta data lock实现(mysql 5.5后引入的)

http://blog.leokim.cn/2017/05/25/mysql-metadata-lockmdl/

1.分析muysqldump流程

2. 利用mysqldump做备份建一个从库(online不停业务)
3. 理解binary log在备份中的作用
4. 利用全备+binary log恢复到某个时间点
==========
5. 利用innobackupex 做备份及恢复 3307, 3306
6. 利用innobackupext做增备及恢复 3307, 3306
7. 利用innobackupex加binary log恢复到某个时间点

8. 表空间传输5.6, 5.5

扩展知识:

9. 关于mysql 5.5以下mysqlbinlog(第三方版本)的闪回
10. mysql binary logs格式浅析

DDL没办法在事务里保护

blob.png

/usr/local/mysql/bin/mysqldump -S /tmp/mysql_3306.sock –master-data=2 –single-transaction leokim >leokim_1_full.sql

GTID下可以不要"–master-data=2"这个参数

more leokim_1_full.sql

blob.png

blob.png

 人生就这么悲剧了,数据没了,这个时候就需要恢复

需要注意在dump的时候如果是gtid会有一个SET @@GLOBAL.GTID.PURGED 会直接报错

blob.png

mysql -S /tmp/mysql_3306.sock leo_bak<leokim_1_full.sql

 

blob.png

利用全备恢复 但是少了我们全备后添加的memcache

blob.png

在全备文件里找到master_log_pos = 858

所以我们在binlog里找到858这个位置 恢复从这个位置开始 到truncate之间的数据就可以了

blob.png

blob.png

mysqlbinlog -v --base64-output=decode-rows --start-position=858 --stop-position=1149  mybinlog.000001 > 1_bak.sql
use leokim
rename table leo to leo_bak;
rename table leo_bak.leo to leo;
select * from leo;

blob.png

mysqlbinlog --start-position=858 --stop-position=1062  mybinlog.000001 | mysql -S /tmp/mysql_3306.sock

现在看mongodb就恢复出来了

blob.png

如果开启了gtid 要reset master之后再做才行,gtid会认为自己已经做过了就不会恢复

所以恢复要找个别的地方恢复 然后再恢复到主库上 不能在主库上reset master

在测试库里回复完了单独做一次mysqldump 然后到出来放到生产库里恢复

gtid开启的话 要加上-f强制执行

或者加上–set -gtid-purged=OFF

有GTID的环境里做恢复特别要小心

binlog恢复一定要按顺序恢复不能跳着恢复

 

 

python

 r 使用 原始字符串

任意参数的列表

>>> def concat(*args, sep="/"):
...     return sep.join(args)
...>>> concat("earth", "mars", "venus")'
earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")'
earth.mars.venus'

分拆参数

>>> list(range(3, 6))            # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args))            # call with arguments unpacked from a list
[3, 4, 5]
>>> def parrot(voltage, state='a stiff', action='voom'):
...     print("-- This parrot wouldn't", action, end=' ')
...     print("if you put", voltage, "volts through it.", end=' ')
...     print("E's", state, "!")
...>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

SQL四种语言:DDL,DML,DCL,TCL

1.DDL(Data Definition Language)数据库定义语言statements are used to define the database structure or schema.

DDL是SQL语言的四大功能之一。
用于定义数据库的三级结构,包括外模式、概念模式、内模式及其相互之间的映像,定义数据的完整性、安全控制等约束
DDL不需要commit.
CREATE
ALTER
DROP
TRUNCATE
COMMENT
RENAME

2.DML(Data Manipulation Language)数据操纵语言statements are used for managing data within schema objects.

由DBMS提供,用于让用户或程序员使用,实现对数据库中数据的操作。
DML分成交互型DML和嵌入型DML两类。
依据语言的级别,DML又可分成过程性DML和非过程性DML两种。
需要commit.
SELECT
INSERT
UPDATE
DELETE
MERGE
CALL
EXPLAIN PLAN
LOCK TABLE

3.DCL(Data Control Language)数据库控制语言  授权,角色控制等
GRANT 授权
REVOKE 取消授权

4.TCL(Transaction Control Language)事务控制语言
SAVEPOINT 设置保存点
ROLLBACK  回滚
SET TRANSACTION

SQL主要分成四部分:
(1)数据定义。(SQL DDL)用于定义SQL模式、基本表、视图和索引的创建和撤消操作。
(2)数据操纵。(SQL DML)数据操纵分成数据查询和数据更新两类。数据更新又分成插入、删除、和修改三种操作。
(3)数据控制。包括对基本表和视图的授权,完整性规则的描述,事务控制等内容。
(4)嵌入式SQL的使用规定。涉及到SQL语句嵌入在宿主语言程序中使用的规则。

MYSQL 备份

blob.png

blob.png

blob.png

blob.png

blob.png

·  不完全备份(上面少写了)

冷备:把机器关掉 copy数据库文件

热备(hot backup):在数据是运行中,进行的备份

增倍:在上一次备份的基础上做一个增加的备份,今天相对于昨天的变更备份

不完全备份:只备份一张表两张表

blob.png

ntsqldump单进程不太完美

mydumper多进程 sql层备份

xtrabackup基本上大家都在用

在有slave的情景下为什么还要有备份?

人为误操作 在主库上truncate table 从库上也会执行truncate table

mysql 5.6引入delay replication

可以指定从库和主库延迟多长时间 比如说一小时 一小时内主库上有误操作可以不同步到从库上

sql_thread 执行同步 不应用就行了

blob.png

只备份一个表的话只有select权限就可以 如果要一致性就要有lock tables权限

涉及到存储过程和视图就要把show view 和 trigger权限都给上 要不然会报错权限不足

blob.png

造成问题:

  1. 热数据冲掉

  2. 冷数据加载 备份时间长

blob.png

–trigger=false(2个减号)

-t –no-create-info(Don't write table createion info. 不要创建schema) 

-d  不要数据只要表结构

-R存储过程

-n 不要创建库

-E  event 

-f 重置备份

建议每个分开备份 

特别对于数据文件单独放

如果数据库不超过100G 并且内存又很大 可以使用

如果已经100G 内存只有16G 就不要考虑使用mysqldump了

考虑的场景是内存和数据差不多的场景采取用 如果超过3倍以上就不要考虑了 

因为用起来太慢了

blob.png

会有一个问题 如果这个表没有id字段会报错

所以加上where条件的话 要明确这个条件和表是有内容的

mysqldump -t –where="id<10" db1 tb1

这样可以简单的从一个表里拿到一部分去做测试方便拿到子集

DDL不受事务管理 保护不了一致性

一致性快照:拿到一个数据在某一时间一致性的数据 t1时间的备份拿到t1时间的镜像

–set-gtid-purged 会带着gtid的值 表明多少gtid之前的不要了 这样一个标识

默认dump出来的代码:insert into tb1 values(),();

-c参数之后变成:insert into tb1(c1,c2,c3) values(c1,c2,c3);

会变成一个完整的格式,支持对原来表结构进行修改

-h:host

-u :用户

-P:端口号

-p:密码

blob.png

blob.png

不同版本的mysqldump变化很频繁

–replace -> replace into

blob.png

后面再演示

利用mysqldump备份有什么问题?

要注意:

1. 数据的内存的比例

2.mysqldump备份很慢,热数据冲掉冷数据加载时间过程备份时间过长

3.建议只备份数据量小的

4.Innodb表 –single-transanction可以不锁表

5.mysqldump还是单进程的(mydumper多线程并行sql层备份  https://launchpad.net/mydumper)

blob.png

还是基于sql层的备份

blob.png

blob.png

blob.png

mysql本身官方的东西太弱了

很多优秀的东西都是开源的

工具:percona-tools

备份:xtrabackup

监控:zabbix


blob.png

Xtrabackup 是类似于官方的企业备份工具 完全开源

Hot Onloine backup tool for MYSQL

图里的Memory是Innodb Buffer Pool

数据更新时 

1.把数据文件拉到内存更新 

2.把日志写到redo log

3.更新完了再把数据回写到data file里

在一个完整的事务环境保护下作了redo undo相应措施

redo 是为防止在update更新时没有完全提交 崩掉了 可以通过redo 来恢复到数据文件里

image.png

这个16K 是代表page size 可以自己定义

从data file里拿到内存的是page size的大小 这个就是操作最小单位
比如说以下示例:

    select * from user where user_id=100;

    除了会把user_id=100的page全读取到buffer pool里

    还会预读后面的内容 每次取满page size大小

    最小单位基于page size 5.6可以调整

    这个时候就体现出key value缓存的好处

    key value缓存就是一条记录缓存不是基于page的缓存

    没有预读直接获取准确值

    innodb_buffer_pool_size 是基于page的缓存

    每次操作读回来的大小是page size效率上会比key value缓存慢

blob.png

innodb crash recovery

如果更新中挂掉了 可以通过redo log恢复

100G 库 5.5引入innodb fast crash recovery 2,3分钟恢复

5.1之前要很久(1个多小时) 可以在errorlog里可以看到进度

blob.png

在内存里备份的时候先形成xtrabackup log

把数据库的任何变成以redo log的形式写入到xtrabackup log文件里

在这里面把数据文件全部copy走

任何变更都在这个文件里有了

相当于在内存挂掉之后用redo log恢复一下做一次crash recovery恢复

很快把数据文件恢复出来

Xtrabackup其实就时利用innodb的crash recovery原理

blob.png

blob.png

数据恢复是经过内存在内存里合并写入到数据文件里

对一个业务很繁忙的系统备份出来xtrabackup log很大 不要再高峰时间备份

fast recovery 的逻辑

  1. 依赖于redo log(redolog会记录哪个page变更,之后能得到一个page no)

  2. 拿到page no把数据(原始页)从备份的数据里读到内存里

  3. 拿到内存之后修改合并

  4. 持久化到数据文件

blob.png

blob.png

XtraBackup 2.4.1 GA版本发布,终于支持了MySQL 5.7的物理备份

tokudb不支持 可能会按非事务引擎来备份


blob.png

innodb/xtradb可以hot backup

myisam非事务引擎都是要加锁的

blob.png

innobackupex 是调用xtrabackup

中间2个会影响效率不建议使用

xtrabackup不会copy *.frm (表结构文件)

blob.png

ibdata里有什么数据?

1、system tablespace(系统表空间)
ibdata files包含:(共享表空间文件)
(1)、internal data dictionary:数据字典
(2)、insert buffer:插入buffer
(3)、rollback segments:回滚表空间
(3)、undo pages:回滚页
(4)、Double write buffer:写入缓冲区

(5)、undo tablespace 

参照叶的课程

mysql 5.6 引入了一个特性: undo tablespace可以独立配置

blob.png

  1. 先形成xtabackup log文件记录内存里的变更

  2. copy innodb数据文件 .ibd , ibdata1

  3. 设置读锁为了一致性

  4. copy表空间,表结构等文件

  5. 拿到binlog位置

  6. 释放锁

  7. 停掉copy 结束

全事务引擎的–no-lock

blob.png

在做这个的时候是调用xtrabackup来copy的

blob.png

/etc/my.cnf /etc/mysql/my.cnf

/usr/local/mysql/etc/my.cnf

如果不是标准安装需要制定配置文件

指定备份目录

innobackupex –defaults-file=/path/my.cnf /backup/to/path/

依赖变量:

datadir 

socket

如果当前用户连不到mysql里需要提供用户密码

–user=

–password=

–host=

–port=

–apply-log

备份完之后会形成backup-my.cnf

blob.png

数据库刚开始备份时形成xtrabackup_info

xrtabackup_binlog_info: 获得binlog位置show master status的输出

–apply-log之后会形成

xtabackup_slave_info

xtrabackup_binlog_post_innodb

一般情况下xrtabackup_binlog_info应该和xtrabackup_binlog_post_innodb 是相等的

如果不相等以xtrabackup_binlog_post_innodb 为准

xtrabackup_logfile是最早形成的

backup-my.cny有数据库的redu log大小定义

blob.png
    blob.png

blob.png

这里没有gtid比较不爽 不知道现在有没有(https://sanwen8.cn/p/22b9Mii.html)

blob.png

这是测试环境里没有写入 to_lsn和last_lsn是一样的

如果在live里写入会很多 to_lsn和last_lsn会差很大

lsn是log sql no在系统show  engine innodb status\G里面输出

blob.png

其实就是字节大小

blob.png

blob.png

ibdata1:100M:autoextend

被同事改成了1G 

已经完成初始化后,再改,然后重启能不能启动?为什么?

实际的datafile 大于1G可以起来小于1G起不来

ibdata1:10M:autoextend

改小可以起来

因为:定义的值要<=实际的文件 关键在autoextend 只要比定义值小就起不来 大就没问题

innodb_log_file_in_group :log个数

innodb_log_file_size :log大小

这2个参数关系到重新做apply log(好像是这个听的不是太清楚)的时候需要把这2个初始化出来

blob.png

innodb_log_file_size

5.5要重新导入导出重建数据库

5.6可以修改重启就可以了

untitled.bmp

blob.png

指定上一次备份的文件在哪

blob.png

拿到之后主要是找xtabackup_info 找上一次备份的last_lsn找到后把大于这个lsn的data page全部copy一遍,copy到下图位置

blob.png

得到lsn后也可以有偷懒的做法 在做增量备份之前可以指定last_lsn在哪,在这个地方直接指定一个lsn就可一直接备份增量也不用指定上个备份目录在哪,这个做法就是大于这个lsn的page全把他copy一份

这样就是简单的做了一个增量

blob.png

blob.png

恢复:

full_1

incr_1

incr_2

如果incr_1 增量坏了

那就悲剧了

blob.png

data-page, index-page

更紧凑备份:只要data-page不要index-page

恢复的时候要重建index-page

blob.png

innodb_file_per_table:独立表空间

blob.png

include-tables-file=/path/tblist.txt //指定备份哪些表

–database //指定备份哪个库

blob.png

恢复的时候要多一个-export,会多一个tb.exp

紧凑备份

好处是减少备份集,备份小一点

blob.png

紧凑备份恢复的时候要索引重建

blob.png

恢复的时间会比平常时间多好几倍时间 因为索引要重建

========================================================

blob.png

blob.png

export之后除了之前的tb_a.frm, tb_ibd

会生成一个tb_a.cfg

然后把这个文件scp到远程空间上 或者scp到需要导入的那台机器上

导入的机器上需要创建一个同样的表结构

需要把当前表空间干掉blob.png

干掉之后如果没有表空间导进来的话就完蛋了

tb_a.ibd 表空间删掉了

把表空间import进来: alter table tb_a  import tablespace;

这个特性是MySQL 5.6引入的

blob.png

blob.png

基本每天一个全被+每天备份相应的binlog

比如:

早上4点做全备

4点前的binlog也要copy一下

通过天全备+今天的binlog 备份

可以还原到昨天的备份到今天任何一个时间点的备份

还原到今天某个时间点需要什么东西呢?

昨天凌晨4点的全量到今天凌晨4点的全量是什么呢? 是今天今天4点的binlog

重要的放到HDFS上

blob.png