首页 文章 精选 留言 我的

精选列表

搜索[实践],共10001篇文章
优秀的个人博客,低调大师

spring cloud gateway实践

一、前言 gateway是spring cloud全家桶的一员,主要用作微服务的网关,是spring官方基于spring5.0,spring boot 2.0和project reactor等技术开发的网关服务,旨在为微服务提供一种简单有效的统一api路由管理方式,基于filter链的方式提供了网关的基本功能如安全、监控、埋点、限流等。 项目地址:https://spring.io/projects/spring-cloud-gateway 二、使用 1、依赖 spring cloud已集成gateway,只需要引入spring cloud的父pom,就能直接使用。 <dependencyManagement> <dependencies> <

优秀的个人博客,低调大师

React Native 分包实践

1. 实现思路 1. RN从本地中读取bundle文件进行显示 2. 将JS文件进行分包打包 3. Native实现页面跳转,每个包跳转都为一个新的Activity 4. 进行bundle文件基础包与功能包的拆分,使用Google的diff_match_patch算法生成差异文件 5. 网络下载差异文件进行合并 6. 展示新页面 2. 操作步骤 Android 1. 修改MainApplication 2. 修改MainActivity 3. 创建LocalReactActivityDelegate 4. 打第一bundle react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle/index.android.bundle --assets-dest bundle/assets/ 5. 打第二个bundle react-native bundle --platform android --dev false --entry-file src/indexNet.js --bundle-output bundle/index1.android.bundle --assets-dest bundle/assets/ 其中indexNet.js是新页面的入口文件 6. 使用google-diff-match-patch进行差异比对 3. 本地加载实现 1). 修改MainApplication public class MainApplication extends Application { private static MainApplication sInstance; /** * 获取当前对象 */ public static MainApplication getInstance() { return sInstance; } @Override public void onCreate() { super.onCreate(); sInstance = this; SoLoader.init(this, /* native exopackage */ false); } } 这里主要是将Application中实现的接口取消,转由MainActivity中提供. 2). 修改MainActivity public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "Unpacking"; } @Override protected ReactActivityDelegate createReactActivityDelegate() { return new LocalReactActivityDelegate(this, getMainComponentName()); } } 这里实现ReactActivity中的createReactActivityDelegate方法,自定义代理设置 3). LocalReactActivityDelegate代理代码 /** * 本地ReactActivity代理 */ class LocalReactActivityDelegate(activity: Activity, @Nullable mainComponentName: String) : ReactActivityDelegate(activity, mainComponentName) { private val mReactNativeHost: ReactNativeHost = object : ReactNativeHost(MainApplication.getInstance()) { /** * 返回ReactPackage对象 */ override fun getPackages(): MutableList<ReactPackage> = Arrays.asList( MainReactPackage(), CustomPackage() ) /** * 是否为开发 */ override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG /** * 返回JSBundle文件路径 */ override fun getJSBundleFile(): String? = "${Environment.getExternalStorageDirectory()}/bundle/index.android.bundle" } /** * 返回ReactNativeHost对象 */ override fun getReactNativeHost(): ReactNativeHost = mReactNativeHost } 4). Native实现页面跳转 I. 创建CustomPackage /** * 自定义ReactPackage */ class CustomPackage : ReactPackage { /** * 创建本地模块 */ override fun createNativeModules(reactContext: ReactApplicationContext?): MutableList<NativeModule> = Arrays.asList(PageModule(reactContext)) /** * 创建视图管理者 */ override fun createViewManagers(reactContext: ReactApplicationContext?): MutableList<ViewManager<View, ReactShadowNode<*>>> = Collections.emptyList() } 5). 创建PageMoudle /** * 页面管理模块 */ @ReactModule(name = "PageModule") class PageModule(reactContext: ReactApplicationContext?) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = "PageModule" /** * 开启网络上的模块页面 */ @ReactMethod fun startNetActivity() { val intent = Intent(currentActivity, NetActivity::class.java) currentActivity?.startActivity(intent) } } 6). NetActivity页面 /** * 新页面 */ class NetActivity : ReactActivity() { override fun getMainComponentName(): String? { return "NetActivity" } override fun createReactActivityDelegate(): ReactActivityDelegate = object: ReactActivityDelegate(this, mainComponentName) { override fun getReactNativeHost(): ReactNativeHost = object : ReactNativeHost(MainApplication.getInstance()) { override fun getPackages(): MutableList<ReactPackage> = Arrays.asList(MainReactPackage()) override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG override fun getJSBundleFile(): String? = "${Environment.getExternalStorageDirectory()}/bundle/index1.android.bundle" } } } 7). RN中使用 修改App.js文件 /** * Sample React Native App * https://github.com/facebook/react-native * * @format * @flow */ import React, {Component} from 'react'; import {Platform, StyleSheet, Text, View, NativeModules, TouchableOpacity} from 'react-native'; const PageModule = NativeModules.PageModule; const instructions = Platform.select({ ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', android: 'Double tap R on your keyboard to reload,\n' + 'Shake or press menu button for dev menu', }); type Props = {}; export default class App extends Component<Props> { startPage() { PageModule.startNetActivity() } render() { return ( <View style={styles.container}> <Text style={styles.welcome}>Welcome to React Native!</Text> <Text style={styles.instructions}>To get started, edit App.js</Text> <Text style={styles.instructions}>{instructions}</Text> <TouchableOpacity onPress={() => this.startPage()} > <Text style={styles.instructions}>开启新页面</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, welcome: { fontSize: 20, textAlign: 'center', margin: 10, }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5, }, }); 8). 执行打包 react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle/index.android.bundle --assets-dest bundle/assets/ --platform: 平台 --dev: 是否为开发模式 --entry-file: 入口文件 --bundle-output: bundle输出路径,这里注意bundle文件夹必须存在 --assets-dest: 资源文件存放路径 9). 测试 I. 生成之后将bundle文件夹拷贝至sdcard下 II. 如果想要在调试时可用,务必修改android/app/build.gradle文件,将bundleInDebug设置为false, 设置为false之后即不将bundle文件打包进app project.ext.react = [ entryFile : "index.js", bundleInDebug: false ] III. 运行 注:此时无法开启新的页面,因为新页面的js文件不存在 4. 开启新页面 1). RN中创建入口文件indexNet.js /** @format */ import {AppRegistry} from 'react-native'; import NetJs from './NetJs'; import {name as appName} from './NetActivity'; AppRegistry.registerComponent(appName, () => NetJs); 2). RN中创建NetActivity.json文件 { "name": "NetActivity", "displayName": "NetActivity" } 注:此处的name和displayName应当与NetActivity.kt中的getMainComponentName方法返回的一致。 3). NetJs.js文件内容 import React, {PureComponent} from 'react'; import { StyleSheet, Text, View } from 'react-native'; /** * @FileName: NetJs * @Author: mazaiting * @Date: 2018/10/9 * @Description: */ class NetJs extends PureComponent { render() { return ( <View style={styles.container}> <Text>Welcome mazaiting!</Text> </View> ) } } /** * 样式属性 */ const styles = StyleSheet.create({ container: { backgroundColor: '#DDD' } }); /** * 导出当前Module */ module.exports = NetJs; 4). 打包 react-native bundle --platform android --dev false --entry-file src/indexNet.js --bundle-output bundle/index1.android.bundle --assets-dest bundle/assets/ 5). 拷贝文件 将bundle文件夹直接拷贝到sdcard目录下,此时再重新运行APP, 即可成功显示页面。 6). 带来的问题 初始包太大,每个包都讲RN基础内容打包,此时应该比较差异,进行差异化分解。 5. 差异化 1). diff-match-patch主页 2). 测试页面 3). 依赖 implementation 'google-diff-match-patch:google-diff-match-patch:0.1' 4). 代码使用 const val FILE1 = "index.android" const val FILE2 = "index1.android" const val FILE3 = "$FILE2-$FILE1.patch" const val FILE_DIR = "E:\\android\\React-Native-Study\\Unpacking\\bundle\\" const val SUFFIX = ".bundle" object Patch { @JvmStatic fun main(args: Array<String>) { val file1 = "$FILE_DIR$FILE1$SUFFIX" val file2 = "$FILE_DIR$FILE2$SUFFIX" val file3 = "$FILE_DIR$FILE3" val patch = productPatch(file1, file2) productFile(file1, file3, patch) } /** * 生成文件 * @param fileOri 源文件 * @param fileDest 目标文件 * @param patchString 差异字符串 */ private fun productFile(fileOri: String, fileDest: String, patchString: String?) { // 创建对象 val patch = diff_match_patch() // 读取文件 val file1 = readFile(fileOri) // 获取补丁内容 val patchText = patch.patch_fromText(patchString) // 应用补丁 val patchApply = patch.patch_apply(LinkedList(patchText), file1) println("===============================================") println("结果:" + patchApply[0]) // 写入文件内容 writeResult(fileDest, patchApply[0].toString()) println("===============================================") // 获取执行结果数组,true为成功,false为失败 val patchResult: BooleanArray = patchApply[1] as BooleanArray val result = StringBuilder() patchResult.forEach { result.append("$it ") } println("result: $result") } /** * 写入文件 * @param fileDest 目标文件 * @param string 文件内容 */ private fun writeResult(fileDest: String, string: String) { // Read a file from disk and return the text contents. val output = FileWriter(fileDest) val writer = BufferedWriter(output) try { writer.write(string) } finally { writer.close() output.close() } } /** * 生成差异patch * @param fileOri 源文件 * @param fileDest 目标文件 * @return 差异字符串 */ private fun productPatch(fileOri: String, fileDest: String): String? { // 创建对象 val diff = diff_match_patch() // 读取基础文件内容 val file1 = readFile(fileOri) // 读取目标文件内容 val file2 = readFile(fileDest) // 进行差异化 val diffString = diff.diff_main(file1, file2, true) // 数组长度大于2执行 if (diffString.size > 2) { diff.diff_cleanupSemantic(diffString) } println(diffString) println("===============================================") // 生成patch内容 val patchString = diff.patch_make(file1, diffString) println(patchString) println("===============================================") // 将patch内容转为字符串 val patchText = diff.patch_toText(patchString) println(patchText) return patchText } /** * 读取文件 * @param filename 文件名 * @return 文件内容 */ @Throws(IOException::class) private fun readFile(filename: String): String { // Read a file from disk and return the text contents. val sb = StringBuilder() val input = FileReader(filename) val bufRead = BufferedReader(input) try { var line = bufRead.readLine() while (line != null) { sb.append(line).append('\n') line = bufRead.readLine() } } finally { bufRead.close() input.close() } return sb.toString() } } 6. [代码下载] (https://gitee.com/mazaiting/React-Native-Study/tree/master/Unpacking)

优秀的个人博客,低调大师

Go HttpServer 最佳实践

这是 Cloudflare 的 Filippo Valsorda 2006年发表在Gopher Academy的一篇文章, 虽然过去两年了,但是依然很有意义。 先前 crypto/tls 太慢而net/http也很年轻, 所以对于Go web server来说, 通常我们明智的做法把它放在反向代理的后面, 如nginx等,现在不需要了。 在Cloudflare我们最近试验了直接暴漏纯Go的服务作为主机。 Go 1.8的net/http 和 crypto/tls 提供了稳定的、高性能并且灵活的功能。 然后,需要做一些调优的工作,本文我们将展示怎么去调优和使web服务器更稳定。 crypto/tls 2016年了,你不会再运行一个不加密的HTTP Server,所以你需要crypto/tls。好消息使这个库已经非常快了(我们的测试),目前他的安

优秀的个人博客,低调大师

EdgeX Foundry 实践

概述 2017年4 月份, Linux 基金组织启动开源项目 EdgeX Foundry ,为物联网边缘计算开发一个标准化互操作框架。 EdgeX Foundry 项目构建于戴尔早期基于 Apache2.0 协议的 FUSE 物联网中间件框架, 无关操作系统和硬件的边缘框架。 EdgeX Foundry 旨在创造一个互操作性、即插即用、模块化的物联网边缘计算的生态系统。 总体架构 部署 环境要求 内存: 不小于4 GB 硬盘空间: 大于3 GB OS: Windows (ver 7 - 10) Ubuntu Desktop (ver 14-16) Ubuntu Server (ver 14) Ubuntu Core (ver 16) Mac OS X 10 User 模式 安装Docker Mac 在安装Docker时自动安装Docker Composehttps://docs.docker.com/engine/getstarted/https://docs.docker.com/compose/install/ 下载EdgeX compose 文件 官方文档有问题,https://github.com/edgexfoundry/developer-scripts/blob/master/compose-files/docker-compose.yml,部分模块无法下载 与Jeremy Phelps 沟通,确认该文件存在问题,模块版本变化较多 解决方案 使用https://github.com/edgexfoundry/developer-scripts/blob/master/compose-files/docker-compose-california-0.5.2.yml,支持最新版本 部分模块需要权限,需要登入docker,docker login nexus3.edgexfoundry.org:10004 -u docker -p docker 运行EdgeX 拉取镜像 启动volume镜像 启动configuration/registry 微服务 启动mongo 启动logging微服务 启动notifications微服务 启动Metadata微服务 启动scheduling微服务 启动virtual device s微服务 同理启动其他微服务 检查微服务都启动完毕 微服务端口 镜像位置 Developers模式 准备工作 Githttps://git-scm.com/downloads Mongodbhttps://www.mongodb.com/download-center?jmp=nav#community Javahttp://www.oracle.com/technetwork/java/javase/downloads/index.html Eclipsehttp://www.eclipse.org/downloads/eclipse-packages EdgeX github地址https://github.com/edgexfoundry 模块依赖关系 安装启动Mongo Database 初始化数据库 启动微服务 Google IoT Core实战 待补充

优秀的个人博客,低调大师

直播转点播实践

场景简介 直播转点播(直转点)是将直播流同步录制为点播视频,并支持媒资管理、媒体处理(转码及内容审核/智能首图等AI处理)、内容制作(云剪辑)、CDN分发加速等一系列操作,可配置工作流自动处理,也可通过API/SDK灵活触发。 准备工作 开通视频点播服务,开通指引 开通视频直播服务,开通指引 添加直转点录制配置,帮助文档 上述准备工作完成后,即可开始进行接入(注:下述文档中的仅存储、仅合成模板组需联系点播进行激活) 名词解释 直转点,结合视频点播的转码、云剪辑、AI处理、事件通知等功能,可适应多种业务场景。名词解释: 录制转码模板组:直播录制到点播同时,点播会使用该模板组对视频进行转码操作 合成转码模板组:多个录制视频进行自动合成时,点播会使用该模板组对视频进行合成+转码操作 仅存储:对直播内容进行录制后,不进行任何后续操作 仅合成:对直播内容进行合成后

优秀的个人博客,低调大师

Android 快应用实践

1. 应用配置信息 1). package.json name: 项目名称 version: 版本信息 toolkit: hap 版本 description: 描述信息 dependencies: 依赖包 2). src/manifest.json package: 应用包名 name: 应用名称 versionName: 应用版本 versionCode: 应用号 mainPlatformVersion: 最小平台版本号 icon: 应用图标 permissions: 权限 config: 配置调试级别(debug, log, info, warn, error) router: 页面路由. 用于定义页面的实际地址、跳转地址。如果ux页面没有配置路由,则不参与项目编译。一个目录下最多只能存在一个主页面文件. 其中entry:配置主页,component:页面对应的ux文件名,path:页面路径,不填则默认为页面名称(<ProjectName>/src目录下,页面目录的相对路径) display: UI显示,用于定义与UI显示相关的配置。支持定义:页面公用的默认UI显示、页面私有的UI显示. titleBarBackgroundColor:导航栏颜色; titleBarTextColor:导航栏字体颜色; menu: 是否有菜单; pages: 页面私有配置(具体页面具体配置). 2. 调试日志 console.debug('debug') console.log('log') console.info('info') console.warn('warn') console.error('error') 可以使用Android Studio的Android Monitor输出来查看日志。 3. 跳转页面 routeDetail () { // 跳转到应用内的某个页面 router.push ({ uri: '/DemoDetail' }) console.debug('打开新页面') } 4. 生命周期 onInit: 表示VM的数据(events,props,data)已经准备好 onReady: 表示VM的模板已经编译完成 onShow:显示其中一个页面 onHide: 隐藏其中一个页面 onDestroy: 释放资源 onBackPress: 用户点击返回实体按键、左上角返回菜单、调用返回API时触发该事件 onMenuPress: 菜单返回时调用

优秀的个人博客,低调大师

单元测试实践

说在前面的话: 就像阿里规约里提到, 单元测试需要满足的AIR原则 : A:Automatic(自动化) I:Independent(独立性) R:Repeatable(可重复) 工欲善其事,必先利其器. 单元测试三剑客: TestNg:单元测试框架 AssertJ:断言工具 Jmockit:mock工具 TestNg testNg是个unit test/sut框架, 支持很多功能。但本篇文章重点不是介绍testNg,简单提一下我觉得比较有用的功能 Group:测试用例分组,根据业务/逻辑分组,以更小的粒度来执行case. Parallel:并行执行,可以配置多个线程来执行case.需要注意并发问题. <suite name="My suite" thread-count="10" parallel="classes"> <test name="mocking"> <packages> <package name="com.hz.constantine.jmockit.slideshare.mocking"/> </packages> </test> <test name="faking"> <packages> <package name="com.hz.constantine.jmockit.slideshare.faking"/> </packages> </test> <test name="testng"> <packages> <package name="com.hz.constantine.jmockit.slideshare.testng"/> </packages> </test> </suite> Listener:通过监听器集成自定义的功能 DataProvider:测试用例和测试数据分离 static final String expectData = DataProviderTest.class.getSimpleName(); static final class DataProvider{ @org.testng.annotations.DataProvider(name = "str") public static Object[][] provide(){ return new Object[][]{{expectData}}; } } @Test(dataProvider = "str",dataProviderClass = DataProvider.class) public void dataProviderTest(String data){ Assert.assertEquals(data,expectData); } Timeout/ExpectExceptions:支持用例执行超时 和 异常 @Test(timeOut = 5,expectedExceptions = {ThreadTimeoutException.class,NullPointerException.class}) public void timeout(){ try { Thread.sleep(10); } catch (InterruptedException e) { } } Tips:附上TestNg Tutorial, http://testng.org/doc/documentation-main.html TestNg 集成Jmockit Java instrumentation:开发者可以构建一个独立于应用程序的代理程序,监测和协助运行在JVM上的程序,虚拟机级别的AOP实现 TestNg Listener: TestNg Listener特性提供开发者扩展TestNg的能力. Jmockit 在jmockit的世界里,它提供两套不同的语法和api. 分别是mocking和faking.下面分别针对这两套做详细的说明 -1- mocking 注解 特性 Expectations.代表一组调用关联到当期的case. Record-Replay-Verify Mode. Record: 预准备依赖和数据. Replay:执行业务. Verify:校验. RecordingResult FlexibleArgumentTest Delegate表达式 Caputring: 用在Verification里. CasCadeMock: 级联Mock 其中最重要的是Record-Replay-Verify Mode ,三段式的表述方式。即 Record: 记录,即定义数据、并mock相关的依赖. Replay:回放,即执行被测试的业务逻辑. Verify:校验,即校验逻辑是否正确.废话不多说,直接上代码: @Test(expectedExceptions = MissingInvocation.class) public void recordReplyVerifyNotInOrder() { //Record ClassUnderTest classUnderTestInstance = newClassUnderTestInstance(); final String data = this.getClass().getSimpleName(); new Expectations(classUnderTestInstance.getEye(), classUnderTestInstance.getRepository()) { { classUnderTestInstance.getEye().find(); result = data; classUnderTestInstance.getRepository().insert(data); times = 1; } }; //Replay classUnderTestInstance.action(); //Verify new VerificationsInOrder(){ { classUnderTestInstance.getRepository().insert(data); classUnderTestInstance.getEye().find(); } }; } 其中 ClassUnderTest 是被测试类,依赖了Eye 和 Repository, 在Record阶段被定义了mocking. 其他特性的测试类,可以参看我的github: https://github.com/cscpswang/java-practice -2- faking Faking unspecifiedFaking Invocation Context第2个和第3个特性依然可以参看我的github,重点说明一下特性1: 1.定义一个被测试对象,一个echoServer(在io编程中,echoServer表示接受到任何消息不做处理,直接返回原消息) class EchoServer { private Dependency dependency=new Dependency(); public String echo(String msg) { return msg; } public String run(){ return dependency.run(); } } 2.定义一个依赖类,这个类会在单测中被faking. class Dependency { public String run(){ return "dependency run"; } } 3.case,测试EchoServer. public void applyFakesWithDependency(){ final String msg = "i'm constantine"; EchoServer echoServer = new EchoServer(); final class DependencyMock extends MockUp<Dependency> { @Mock public String run(){ return msg; } } new DependencyMock(); String actualMsg = echoServer.run(); Assert.assertEquals(actualMsg,msg); } tips: spring 中使用faking api时泛型指定到具体的impl类. 被jdk 代理的类(如spring bean被aop),并不是实现类的实例. 所以要么基于接口mocking(需要mock接口的所有方法),要么使用faking. 小结: Faking和Mocking是两套jmockit的api,在其官方文档,有下面一句话: In particular, the use ofboththe Faking API and the Mocking API in the same test class should be viewed with suspicion, as it strongly indicates misuse. 我更偏爱使用Mocking api, 因为它的特性很酷。 但它不能mocking私有方法,这点有时会不太方便。 最后,如果你要下jmockit的源码,并希望研究,注意一个坑。由于jmockit基于java agent(instrumentation),你需要在你的classpath下放置一个jmockit-xxx.jar.

优秀的个人博客,低调大师

实践android的RadioButton

一个一个组件的实习过来。 package com.tw.flag.ch02_button; import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.os.Vibrator; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.TextWatcher; import android.view.MotionEvent; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.TextView; import java.util.Random; public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener, TextWatcher{ RadioGroup unit; EditText value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); unit = (RadioGroup) findViewById(R.id.unit); unit.setOnCheckedChangeListener(this); value = (EditText) findViewById(R.id.value); value.addTextChangedListener(this); } @Override public void onCheckedChanged(RadioGroup group, int checkedId) { calc(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { calc(); } protected void calc() { TextView degF = (TextView)findViewById(R.id.degF); TextView degC = (TextView)findViewById(R.id.degC); double f, c; if (unit.getCheckedRadioButtonId() == R.id.unitF) { f = Double.parseDouble(value.getText().toString()); c = (f - 32) * 5/9; } else { c = Double.parseDouble(value.getText().toString()); f = c * 9/5 + 32; } degC.setText(String.format("%.1f", c) + getResources().getString(R.string.charC)); degF.setText(String.format("%.1f", f) + getResources().getString(R.string.charF)); } }

优秀的个人博客,低调大师

Mongodb基础实践(二)

在前面的文章里面主要介绍了MongoDB的文档,集合,数据库等操作和对文档的增、删、改相关知识,接下来会总结一点有关查询的相关知识。 在MySQL中,我们知道数据查询是优化的主要内容,读写分离等技术都是可以用来处理数据库查询优化的,足以见数据库查询是每个系统中很重要的一部分,之前介绍了find的简单使用,下面会介绍一些相对比较复杂一点的查询。 一、数据查询 MySQL数据库中主要是用select 结合where子句实现数据的查询,功能特别强大,例如多表联合查询、支持正则表达式等。不在这里做过多的相关介绍。这里主要介绍MongoDB的相关查询,MongoDB中主要用find()实现数据的查询,同时也可以使用一些条件限制。 1.1显示单条数据 在上篇文章中提到了find()的使用,但是每次查询数据,都是查询所有的,显示其中的一部分,可以用it迭代。有时候我们想要查询其中的一条数据,具体操作要根据具体需求实现。 MongoDB 查询数据的语法 1 db.collection. find (query,projection) query :可选,使用查询操作符指定查询条件 projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。可以使用 pretty() 方法以易读的方式来读取数据,,语法格式如下 1 >db.col. find ().pretty() pretty() 方法以格式化的方式来显示所有文档。 例如: 1 2 3 4 5 6 7 8 9 10 11 db.winner. find ().pretty() { "_id" :ObjectId( "592e7d1caaa464fa8a557e95" ), "winne" :1955} { "_id" :ObjectId( "592e7d1eaaa464fa8a557e96" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e97" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e98" ), "winne" :1955} { "_id" :ObjectId( "592e7d21aaa464fa8a557e99" ), "winne" :1955} { "_id" :ObjectId( "592e7d22aaa464fa8a557e9a" ), "winne" :1955} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec4" ), "winne" :41} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec5" ), "winne" :42} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec6" ), "winne" :43} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec7" ), "winne" :44} 1、查询某个集合中的所有数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 db.winner. find () { "_id" :ObjectId( "592e7d1caaa464fa8a557e95" ), "winne" :1955} { "_id" :ObjectId( "592e7d1eaaa464fa8a557e96" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e97" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e98" ), "winne" :1955} { "_id" :ObjectId( "592e7d21aaa464fa8a557e99" ), "winne" :1955} { "_id" :ObjectId( "592e7d22aaa464fa8a557e9a" ), "winne" :1955} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec4" ), "winne" :41} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec5" ), "winne" :42} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec6" ), "winne" :43} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec7" ), "winne" :44} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec8" ), "winne" :45} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec9" ), "winne" :46} { "_id" :ObjectId( "592e7e14aaa464fa8a557eca" ), "winne" :47} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecb" ), "winne" :48} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecc" ), "winne" :49} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecd" ), "winne" :50} { "_id" :ObjectId( "592e7e14aaa464fa8a557ece" ), "winne" :51} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecf" ), "winne" :52} { "_id" :ObjectId( "592e7e14aaa464fa8a557ed0" ), "winne" :53} { "_id" :ObjectId( "592e7e14aaa464fa8a557ed1" ), "winne" :54} Type "it" for more 默认显示20条数据,其他数据可以输入it迭代。 2、显示一条数据 find()是输出所有结果,里面可能有些文档内容相同,但是“_id”肯定是不一样的,这时我们可以使用findOne()方法查询,或者可以使用db.winner.find({winne:1955}).limit(1)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 db.winner. find ({winne:1955}) { "_id" :ObjectId( "592e7d1caaa464fa8a557e95" ), "winne" :1955} { "_id" :ObjectId( "592e7d1eaaa464fa8a557e96" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e97" ), "winne" :1955} { "_id" :ObjectId( "592e7d1faaa464fa8a557e98" ), "winne" :1955} { "_id" :ObjectId( "592e7d21aaa464fa8a557e99" ), "winne" :1955} { "_id" :ObjectId( "592e7d22aaa464fa8a557e9a" ), "winne" :1955} 假如要查询winner集合中winne=1955的一条数据,而用 find ()查询出所有的数据,这时就可以使用findOne() db.winner.findOne({winne:1955}) { "_id" :ObjectId( "592e7d1caaa464fa8a557e95" ), "winne" :1955} 或者可以使用 db.winner. find ({winne:1955}).limit(1) { "_id" :ObjectId( "592e7d1caaa464fa8a557e95" ), "winne" :1955} 两者的区别 findOne()有点类似MySQL里面的distinct,会返回查询的第一条结果,如果搜索不到想要的数据就会 返回NULL, db.winner.findOne({winne:200888}) null db.winner. find ({winne:1955}).limit(1)方法就和MySQL里面的limit是一样的,主要是限制查询结果的条数。 3、查询满足一定条件的数据 在MySQL中查询时,可以结合where以及字段等信息查询数据,而MongoDB中也是可以的,同样可以支持一些条件判断语句。 格式 范例 RDBMS中的类似语句 等于 {<key>:<value>} db.col.find({"winne":"1995"}).pretty() wherewinne= '50' 小于 {<key>:{$lt:<value>}} db.col.find({"winne":{$lt:50}}).pretty() wherewinne< 50 小于或等于 {<key>:{$lte:<value>}} db.col.find({"winne":{$lte:50}}).pretty() wherewinne<= 50 大于 {<key>:{$gt:<value>}} db.col.find({"winne":{$gt:50}}).pretty() wherewinne> 50 大于或等于 {<key>:{$gte:<value>}} db.col.find({"winne":{$gte:50}}).pretty() wherewinne>= 50 不等于 {<key>:{$ne:<value>}} db.col.find({"winne":{$ne:50}}).pretty() wherewinne!= 50 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 $gt--------greaterthan$gte---------gtequal $lt-------- less than$lte---------ltequal $ ne -----------notequal 1、查询winner集合中winne<50的相关数据 db.winner. find ({winne:{$lt:50}}) { "_id" :ObjectId( "592e7e14aaa464fa8a557ec4" ), "winne" :41} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec5" ), "winne" :42} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec6" ), "winne" :43} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec7" ), "winne" :44} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec8" ), "winne" :45} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec9" ), "winne" :46} { "_id" :ObjectId( "592e7e14aaa464fa8a557eca" ), "winne" :47} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecb" ), "winne" :48} { "_id" :ObjectId( "592e7e14aaa464fa8a557ecc" ), "winne" :49} { "_id" :ObjectId( "592e7e17aaa464fa8a557f28" ), "winne" :41} { "_id" :ObjectId( "592e7e17aaa464fa8a557f29" ), "winne" :42} { "_id" :ObjectId( "592e7e17aaa464fa8a557f2a" ), "winne" :43} { "_id" :ObjectId( "592e7e17aaa464fa8a557f2b" ), "winne" :44} 2、查询winner集合中40=<winne<50的相关数据 db.winner. find ({winne:{$gte:40,$lt:45}}) { "_id" :ObjectId( "592e7e14aaa464fa8a557ec4" ), "winne" :41} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec5" ), "winne" :42} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec6" ), "winne" :43} { "_id" :ObjectId( "592e7e14aaa464fa8a557ec7" ), "winne" :44} { "_id" :ObjectId( "592e7e17aaa464fa8a557f28" ), "winne" :41} { "_id" :ObjectId( "592e7e17aaa464fa8a557f29" ), "winne" :42} { "_id" :ObjectId( "592e7e17aaa464fa8a557f2a" ), "winne" :43} { "_id" :ObjectId( "592e7e17aaa464fa8a557f2b" ), "winne" :44} { "_id" :ObjectId( "592e7e18aaa464fa8a557f8c" ), "winne" :41} { "_id" :ObjectId( "592e7e18aaa464fa8a557f8d" ), "winne" :42} { "_id" :ObjectId( "592e7e18aaa464fa8a557f8e" ), "winne" :43} { "_id" :ObjectId( "592e7e18aaa464fa8a557f8f" ), "winne" :44} 4、MongoDB AND 条件 MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,语法格式如下: >db.winner.find({key1:value1, key2:value2}).pretty() 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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #插入测试数据 for (i=0;i<20;i++)db.info2.insert({name: "linux" , object: "SA" , company: "docker" , phone:i}) for (i=0;i<20;i++)db.info2.insert({name: "openstack" , object: "DBA" , company: "could" , phone:i}) #检查测试数据 >db.info2. find () db.info2. find () { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f838dd276944818f7edb9" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :5} { "_id" :ObjectId( "592f838dd276944818f7edba" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :6} { "_id" :ObjectId( "592f838dd276944818f7edbb" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :7} { "_id" :ObjectId( "592f838dd276944818f7edbc" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :8} { "_id" :ObjectId( "592f838dd276944818f7edbd" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :9} { "_id" :ObjectId( "592f838dd276944818f7edbe" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :10} { "_id" :ObjectId( "592f838dd276944818f7edbf" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :11} { "_id" :ObjectId( "592f838dd276944818f7edc0" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :12} { "_id" :ObjectId( "592f838dd276944818f7edc1" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :13} { "_id" :ObjectId( "592f838dd276944818f7edc2" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :14} { "_id" :ObjectId( "592f838dd276944818f7edc3" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :15} { "_id" :ObjectId( "592f838dd276944818f7edc4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :16} { "_id" :ObjectId( "592f838dd276944818f7edc5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :17} { "_id" :ObjectId( "592f838dd276944818f7edc6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :18} { "_id" :ObjectId( "592f838dd276944818f7edc7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :19} >db.info2. find ().count() #检查数据的条数 40 #筛选name=linuxobject=SAphone<5 db.info2. find ({name: "linux" ,object: "SA" ,phone:{$lt:5}}) 执行 db.info2. find ({name: "linux" ,object: "SA" ,phone:{$lt:5}}) { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} 筛选name=linuxobject=SA5<phone<=10 db.info2. find ({name: "linux" ,object: "SA" ,phone:{ "$gt" :5, "$lte" :10}}) db.info2. find ({name: "linux" ,object: "SA" ,phone:{ "$gt" :5, "$lte" :10}}) { "_id" :ObjectId( "592f838dd276944818f7edba" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :6} { "_id" :ObjectId( "592f838dd276944818f7edbb" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :7} { "_id" :ObjectId( "592f838dd276944818f7edbc" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :8} { "_id" :ObjectId( "592f838dd276944818f7edbd" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :9} { "_id" :ObjectId( "592f838dd276944818f7edbe" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :10} 筛选5<phone<=10 db.info2. find ({phone:{ "$gt" :5, "$lte" :10}}) { "_id" :ObjectId( "592f838dd276944818f7edba" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :6} { "_id" :ObjectId( "592f838dd276944818f7edbb" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :7} { "_id" :ObjectId( "592f838dd276944818f7edbc" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :8} { "_id" :ObjectId( "592f838dd276944818f7edbd" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :9} { "_id" :ObjectId( "592f838dd276944818f7edbe" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :10} { "_id" :ObjectId( "592f838fd276944818f7edce" ), "name" : "openstack" , "object" : "DBA" , "company" : "could" , "phone" :6} { "_id" :ObjectId( "592f838fd276944818f7edcf" ), "name" : "openstack" , "object" : "DBA" , "company" : "could" , "phone" :7} { "_id" :ObjectId( "592f838fd276944818f7edd0" ), "name" : "openstack" , "object" : "DBA" , "company" : "could" , "phone" :8} { "_id" :ObjectId( "592f838fd276944818f7edd1" ), "name" : "openstack" , "object" : "DBA" , "company" : "could" , "phone" :9} { "_id" :ObjectId( "592f838fd276944818f7edd2" ), "name" : "openstack" , "object" : "DBA" , "company" : "could" , "phone" :10} 5 MongoDB OR 条件 MongoDB 除了有类似MySQL的AND条件语句外,还有OR 条件语句,OR 条件语句使用了关键字 $or,语法格式如下: >db.collections.find( { $or: [ {key1: value1}, {key2:value2} ] } ).pretty() 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 #查name=linux或者object=redis db.info2. find ( {$or:[{name: "linux" },{object: "redis" }] } ) db.info2. find (db.info2. find ( ...{$or:[{name: "linux" },{object: "redis" }]{$or:[{name: "linux" },{object: "redis" }] ... ...}} ...)) { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f838dd276944818f7edb9" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :5} { "_id" :ObjectId( "592f838dd276944818f7edba" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :6} { "_id" :ObjectId( "592f838dd276944818f7edbb" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :7} { "_id" :ObjectId( "592f838dd276944818f7edbc" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :8} { "_id" :ObjectId( "592f838dd276944818f7edbd" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :9} { "_id" :ObjectId( "592f838dd276944818f7edbe" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :10} { "_id" :ObjectId( "592f838dd276944818f7edbf" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :11} { "_id" :ObjectId( "592f838dd276944818f7edc0" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :12} { "_id" :ObjectId( "592f838dd276944818f7edc1" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :13} { "_id" :ObjectId( "592f838dd276944818f7edc2" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :14} { "_id" :ObjectId( "592f838dd276944818f7edc3" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :15} { "_id" :ObjectId( "592f838dd276944818f7edc4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :16} { "_id" :ObjectId( "592f838dd276944818f7edc5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :17} { "_id" :ObjectId( "592f838dd276944818f7edc6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :18} { "_id" :ObjectId( "592f838dd276944818f7edc7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :19} Type "it" for more >itit { "_id" :ObjectId( "592f8964d276944818f7eddc" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :0} { "_id" :ObjectId( "592f8964d276944818f7eddd" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :1} { "_id" :ObjectId( "592f8964d276944818f7edde" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :2} { "_id" :ObjectId( "592f8964d276944818f7eddf" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :3} { "_id" :ObjectId( "592f8964d276944818f7ede0" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :4} { "_id" :ObjectId( "592f8964d276944818f7ede1" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :5} { "_id" :ObjectId( "592f8964d276944818f7ede2" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :6} { "_id" :ObjectId( "592f8964d276944818f7ede3" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :7} { "_id" :ObjectId( "592f8964d276944818f7ede4" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :8} { "_id" :ObjectId( "592f8964d276944818f7ede5" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :9} 6、AND和OR综合使用 查询phone<5,name=MongoDB或者name=linux 1 2 3 4 5 6 7 8 9 10 11 12 db.info2. find ({phone:{$lt:5},$or:[{name: "MongoDB" },{name: "linux" }]}) db.info2. find ({phone:{$lt:5},$or:[{name: "MongoDB" },{name: "linux" }]}) { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f8964d276944818f7eddc" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :0} { "_id" :ObjectId( "592f8964d276944818f7eddd" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :1} { "_id" :ObjectId( "592f8964d276944818f7edde" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :2} { "_id" :ObjectId( "592f8964d276944818f7eddf" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :3} { "_id" :ObjectId( "592f8964d276944818f7ede0" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :4} 7、查询结果排序 在MySQL中是有order by条件,可以根据desc或者asc进行升序或者降序操作,而MongoDB中是可以利用sort()方法实现排序的,例如对6中的结果处理,根据phone排序。 基本语法 db.info2.find().sort({phone:1})#这里phone表示根据该key排序,1表示升序,-1表示降序。 1 2 3 4 5 6 7 8 9 10 11 db.info2. find ({phone:{$lt:5},$or:[{name: "MongoDB" },{name: "linux" }]}). sort ({phone:1}) { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f8964d276944818f7eddc" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f8964d276944818f7eddd" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f8964d276944818f7edde" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f8964d276944818f7eddf" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f8964d276944818f7ede0" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :4} 8、MongoDB Skip() 方法 在前面介绍了limit(),sort(),count()等方法,接下来要介绍一个比较有趣的skip()方法,在使用limit()的时候可以显示你要求的几条,而skip()方法是跳过几条。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 db.info2. find ({phone:{$lt:5},$or:[{name: "MongoDB" },{name: "linux" }]}). sort ({phone:1}) { "_id" :ObjectId( "592f838dd276944818f7edb4" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :0} { "_id" :ObjectId( "592f8964d276944818f7eddc" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :0} { "_id" :ObjectId( "592f838dd276944818f7edb5" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :1} { "_id" :ObjectId( "592f8964d276944818f7eddd" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f8964d276944818f7edde" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f8964d276944818f7eddf" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f8964d276944818f7ede0" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :4} 使用skip()方法 db.info2. find ({phone:{$lt:5},$or:[{name: "MongoDB" },{name: "linux" }]}). sort ({phone:1}).skip(3) { "_id" :ObjectId( "592f8964d276944818f7eddd" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :1} { "_id" :ObjectId( "592f838dd276944818f7edb6" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :2} { "_id" :ObjectId( "592f8964d276944818f7edde" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :2} { "_id" :ObjectId( "592f838dd276944818f7edb7" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :3} { "_id" :ObjectId( "592f8964d276944818f7eddf" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :3} { "_id" :ObjectId( "592f838dd276944818f7edb8" ), "name" : "linux" , "object" : "SA" , "company" : "docker" , "phone" :4} { "_id" :ObjectId( "592f8964d276944818f7ede0" ), "name" : "MongoDB" , "object" : "redis" , "company" : "winner" , "phone" :4} skip方法有点类似于MySQL里面的limit之间间隔情况。 这里介绍了有关查询的问题,在数据库中,查询是非常重要的一部分,所以介绍的篇幅也是比较多的,后期遇到其他问题也会继续总结输出。 本文转自 tianya1993 51CTO博客,原文链接:http://blog.51cto.com/dreamlinux/1931384,如需转载请自行联系原作者

优秀的个人博客,低调大师

MAPREDUCE实践篇(1)

2.1MAPREDUCE示例编写及编程规范 2.1.1编程规范 (1)用户编写的程序分成三个部分:Mapper,Reducer,Driver(提交运行mr程序的客户端) (2)Mapper的输入数据是KV对的形式(KV的类型可自定义) (3)Mapper的输出数据是KV对的形式(KV的类型可自定义) (4)Mapper中的业务逻辑写在map()方法中 (5)map()方法(maptask进程)对每一个<K,V>调用一次 (6)Reducer的输入数据类型对应Mapper的输出数据类型,也是KV (7)Reducer的业务逻辑写在reduce()方法中 (8)Reducetask进程对每一组相同k的<k,v>组调用一次reduce()方法 (9)用户自定义的Mapper和Reducer都要继承各自的父类 (10)整个程序需要一个Drvier来进行提交,提交的是一个描述了各种必要信息的job对象 1.7.2 wordcount示例编写 需求:在一堆给定的文本文件中统计输出每一个单词出现的总次数 (1)定义一个mapper类 //首先要定义四个泛型的类型 //keyin: LongWritable valuein: Text //keyout: Text valueout:IntWritable public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{ //map方法的生命周期: 框架每传一行数据就被调用一次 //key : 这一行的起始点在文件中的偏移量 //value:这一行的内容 @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //拿到一行数据转换为string String line = value.toString(); //将这一行切分出各个单词 String[] words = line.split(" "); //遍历数组,输出<单词,1> for(String word:words){ context.write(new Text(word), new IntWritable(1)); } } } (2)定义一个reducer类 //生命周期:框架每传递进来一个kv组,reduce方法被调用一次 @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { //定义一个计数器 int count = 0; //遍历这一组kv的所有v,累加到count中 for(IntWritable value:values){ count += value.get(); } context.write(key, new IntWritable(count)); } } (3)定义一个主类,用来描述job并提交job public class WordCountRunner { //把业务逻辑相关的信息(哪个是mapper,哪个是reducer,要处理的数据在哪里,输出的结果放哪里……)描述成一个job对象 //把这个描述好的job提交给集群去运行 public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job wcjob = Job.getInstance(conf); //指定我这个job所在的jar包 //wcjob.setJar("/home/hadoop/wordcount.jar"); wcjob.setJarByClass(WordCountRunner.class); wcjob.setMapperClass(WordCountMapper.class); wcjob.setReducerClass(WordCountReducer.class); //设置我们的业务逻辑Mapper类的输出key和value的数据类型 wcjob.setMapOutputKeyClass(Text.class); wcjob.setMapOutputValueClass(IntWritable.class); //设置我们的业务逻辑Reducer类的输出key和value的数据类型 wcjob.setOutputKeyClass(Text.class); wcjob.setOutputValueClass(IntWritable.class); //指定要处理的数据所在的位置 FileInputFormat.setInputPaths(wcjob, "hdfs://hdp-server01:9000/wordcount/data/big.txt"); //指定处理完成之后的结果所保存的位置 FileOutputFormat.setOutputPath(wcjob, new Path("hdfs://hdp-server01:9000/wordcount/output/")); //向yarn集群提交这个job boolean res = wcjob.waitForCompletion(true); System.exit(res?0:1); } 2.2MAPREDUCE程序运行模式 2.2.1本地运行模式 (1)mapreduce程序是被提交给LocalJobRunner在本地以单进程的形式运行 (2)而处理的数据及输出结果可以在本地文件系统,也可以在hdfs上 (3)怎样实现本地运行?写一个程序,不要带集群的配置文件(本质是你的mr程序的conf中是否有mapreduce.framework.name=local以及yarn.resourcemanager.hostname参数) (4)本地模式非常便于进行业务逻辑的debug,只要在eclipse中打断点即可 如果在windows下想运行本地模式来测试程序逻辑,需要在windows中配置环境变量: %HADOOP_HOME% = d:/hadoop-2.6.1 %PATH% = %HADOOP_HOME%\bin 并且要将d:/hadoop-2.6.1的lib和bin目录替换成windows平台编译的版本 2.2.2集群运行模式 (1)将mapreduce程序提交给yarn集群resourcemanager,分发到很多的节点上并发执行 (2)处理的数据和输出结果应该位于hdfs文件系统 (3)提交集群的实现步骤: A、将程序打成JAR包,然后在集群的任意一个节点上用hadoop命令启动 $ hadoop jar wordcount.jar cn.itcast.bigdata.mrsimple.WordCountDriver inputpath outputpath B、直接在linux的eclipse中运行main方法 (项目中要带参数:mapreduce.framework.name=yarn以及yarn的两个基本配置) C、如果要在windows的eclipse中提交job给集群,则要修改YarnRunner类 mapreduce程序在集群中运行时的大体流程: 附:在windows平台上访问hadoop时改变自身身份标识的方法之二: 3. MAPREDUCE中的Combiner (1)combiner是MR程序中Mapper和Reducer之外的一种组件 (2)combiner组件的父类就是Reducer (3)combiner和reducer的区别在于运行的位置: Combiner是在每一个maptask所在的节点运行 Reducer是接收全局所有Mapper的输出结果; (4) combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量 具体实现步骤: 1、自定义一个combiner继承Reducer,重写reduce方法 2、在job中设置: job.setCombinerClass(CustomCombiner.class) (5) combiner能够应用的前提是不能影响最终的业务逻辑 而且,combiner的输出kv应该跟reducer的输入kv类型要对应起来 本文转自yushiwh 51CTO博客,原文链接:http://blog.51cto.com/yushiwh/1913043,如需转载请自行联系原作者

优秀的个人博客,低调大师

MAPREDUCE实践篇(2)

4.1. Mapreduce中的排序初步 4.1.1需求 对日志数据中的上下行流量信息汇总,并输出按照总流量倒序排序的结果 数据如下: 13631579850661372623050300-FD-07-A4-72-B8:CMCC120.196.100.822427248124681200 1363157995052138265441015C-0E-8B-C7-F1-E0:CMCC120.197.40.4402640200 13631579910761392643565620-10-7A-28-CC-0A:CMCC120.196.100.99241321512200 1363154400022139262511065C-0E-8B-8B-B1-50:CMCC120.197.40.4402400200 4.1.2分析 基本思路:实现自定义的bean来封装流量信息,并将bean作为map输出的key来传输 MR程序在处理数据的过程中会对数据排序(map输出的kv对传输到reduce之前,会排序),排序的依据是map输出的key 所以,我们如果要实现自己需要的排序规则,则可以考虑将排序因素放到key中,让key实现接口:WritableComparable 然后重写key的compareTo方法 4.1.3实现 1、自定义的bean public class FlowBean implements WritableComparable<FlowBean>{ long upflow; long downflow; long sumflow; //如果空参构造函数被覆盖,一定要显示定义一下,否则在反序列时会抛异常 public FlowBean(){} public FlowBean(long upflow, long downflow) { super(); this.upflow = upflow; this.downflow = downflow; this.sumflow = upflow + downflow; } public long getSumflow() { return sumflow; } public void setSumflow(long sumflow) { this.sumflow = sumflow; } public long getUpflow() { return upflow; } public void setUpflow(long upflow) { this.upflow = upflow; } public long getDownflow() { return downflow; } public void setDownflow(long downflow) { this.downflow = downflow; } //序列化,将对象的字段信息写入输出流 @Override public void write(DataOutput out) throws IOException { out.writeLong(upflow); out.writeLong(downflow); out.writeLong(sumflow); } //反序列化,从输入流中读取各个字段信息 @Override public void readFields(DataInput in) throws IOException { upflow = in.readLong(); downflow = in.readLong(); sumflow = in.readLong(); } @Override public String toString() { return upflow + "\t" + downflow + "\t" + sumflow; } @Override public int compareTo(FlowBean o) { //自定义倒序比较规则 return sumflow > o.getSumflow() ? -1:1; } } 2、mapper和reducer public class FlowCount { static class FlowCountMapper extends Mapper<LongWritable, Text, FlowBean,Text > { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] fields = line.split("\t"); try { String phonenbr = fields[0]; long upflow = Long.parseLong(fields[1]); long dflow = Long.parseLong(fields[2]); FlowBean flowBean = new FlowBean(upflow, dflow); context.write(flowBean,new Text(phonenbr)); } catch (Exception e) { e.printStackTrace(); } } } static class FlowCountReducer extends Reducer<FlowBean,Text,Text, FlowBean> { @Override protected void reduce(FlowBean bean, Iterable<Text> phonenbr, Context context) throws IOException, InterruptedException { Text phoneNbr = phonenbr.iterator().next(); context.write(phoneNbr, bean); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf); job.setJarByClass(FlowCount.class); job.setMapperClass(FlowCountMapper.class); job.setReducerClass(FlowCountReducer.class); job.setMapOutputKeyClass(FlowBean.class); job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(FlowBean.class); // job.setInputFormatClass(TextInputFormat.class); FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); job.waitForCompletion(true); } } 4.2. Mapreduce中的分区Partitioner 4.2.1需求 根据归属地输出流量统计数据结果到不同文件,以便于在查询统计结果时可以定位到省级范围进行 4.2.2分析 Mapreduce中会将map输出的kv对,按照相同key分组,然后分发给不同的reducetask 默认的分发规则为:根据key的hashcode%reducetask数来分发 所以:如果要按照我们自己的需求进行分组,则需要改写数据分发(分组)组件Partitioner 自定义一个CustomPartitioner继承抽象类:Partitioner 然后在job对象中,设置自定义partitioner:job.setPartitionerClass(CustomPartitioner.class) 4.2.3实现 /** *定义自己的从map到reduce之间的数据(分组)分发规则 按照手机号所属的省份来分发(分组)ProvincePartitioner *默认的分组组件是HashPartitioner * * @author * */ public class ProvincePartitioner extends Partitioner<Text, FlowBean> { static HashMap<String, Integer> provinceMap = new HashMap<String, Integer>(); static { provinceMap.put("135", 0); provinceMap.put("136", 1); provinceMap.put("137", 2); provinceMap.put("138", 3); provinceMap.put("139", 4); } @Override public int getPartition(Text key, FlowBean value, int numPartitions) { Integer code = provinceMap.get(key.toString().substring(0, 3)); return code == null ? 5 : code; } } 4.3. mapreduce数据压缩 4.3.1概述 这是mapreduce的一种优化策略:通过压缩编码对mapper或者reducer的输出进行压缩,以减少磁盘IO,提高MR程序运行速度(但相应增加了cpu运算负担) 1、Mapreduce支持将map输出的结果或者reduce输出的结果进行压缩,以减少网络IO或最终输出数据的体积 2、压缩特性运用得当能提高性能,但运用不当也可能降低性能 3、基本原则: 运算密集型的job,少用压缩 IO密集型的job,多用压缩 4.3.2 MR支持的压缩编码 4.3.3 Reducer输出压缩 在配置参数或在代码中都可以设置reduce的输出压缩 1、在配置参数中设置 mapreduce.output.fileoutputformat.compress=false mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.DefaultCodec mapreduce.output.fileoutputformat.compress.type=RECORD 2、在代码中设置 Job job = Job.getInstance(conf); FileOutputFormat.setCompressOutput(job, true); FileOutputFormat.setOutputCompressorClass(job, (Class<? extends CompressionCodec>) Class.forName("")); 4.3.4 Mapper输出压缩 在配置参数或在代码中都可以设置reduce的输出压缩 1、在配置参数中设置 mapreduce.map.output.compress=false mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.DefaultCodec 2、在代码中设置: conf.setBoolean(Job.MAP_OUTPUT_COMPRESS, true); conf.setClass(Job.MAP_OUTPUT_COMPRESS_CODEC, GzipCodec.class, CompressionCodec.class); 4.3.5压缩文件的读取 Hadoop自带的InputFormat类内置支持压缩文件的读取,比如TextInputformat类,在其initialize方法中: public void initialize(InputSplit genericSplit, TaskAttemptContext context) throws IOException { FileSplit split = (FileSplit) genericSplit; Configuration job = context.getConfiguration(); this.maxLineLength = job.getInt(MAX_LINE_LENGTH, Integer.MAX_VALUE); start = split.getStart(); end = start + split.getLength(); final Path file = split.getPath(); // open the file and seek to the start of the split final FileSystem fs = file.getFileSystem(job); fileIn = fs.open(file); //根据文件后缀名创建相应压缩编码的codec CompressionCodec codec = new CompressionCodecFactory(job).getCodec(file); if (null!=codec) { isCompressedInput = true; decompressor = CodecPool.getDecompressor(codec); //判断是否属于可切片压缩编码类型 if (codec instanceof SplittableCompressionCodec) { final SplitCompressionInputStream cIn = ((SplittableCompressionCodec)codec).createInputStream( fileIn, decompressor, start, end, SplittableCompressionCodec.READ_MODE.BYBLOCK); //如果是可切片压缩编码,则创建一个CompressedSplitLineReader读取压缩数据 in = new CompressedSplitLineReader(cIn, job, this.recordDelimiterBytes); start = cIn.getAdjustedStart(); end = cIn.getAdjustedEnd(); filePosition = cIn; } else { //如果是不可切片压缩编码,则创建一个SplitLineReader读取压缩数据,并将文件输入流转换成解压数据流传递给普通SplitLineReader读取 in = new SplitLineReader(codec.createInputStream(fileIn, decompressor), job, this.recordDelimiterBytes); filePosition = fileIn; } } else { fileIn.seek(start); //如果不是压缩文件,则创建普通SplitLineReader读取数据 in = new SplitLineReader(fileIn, job, this.recordDelimiterBytes); filePosition = fileIn; } 4.4.更多MapReduce编程案例 4.4.1 reduce端join算法实现 1、需求: 订单数据表t_order: id date pid amount 1001 20150710 P0001 2 1002 20150710 P0001 3 1002 20150710 P0002 3 商品信息表t_product id name category_id price P0001 小米5 C01 2 P0002 锤子T1 C01 3 假如数据量巨大,两表的数据是以文件的形式存储在HDFS中,需要用mapreduce程序来实现一下SQL查询运算: select a.id,a.date,b.name,b.category_id,b.price from t_order a join t_product b on a.pid = b.id 2、实现机制: 通过将关联的条件作为map输出的key,将两表满足join条件的数据并携带数据所来源的文件信息,发往同一个reduce task,在reduce中进行数据的串联 public class OrderJoin { static class OrderJoinMapper extends Mapper<LongWritable, Text, Text, OrderJoinBean> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //拿到一行数据,并且要分辨出这行数据所属的文件 String line = value.toString(); String[] fields = line.split("\t"); //拿到itemid String itemid = fields[0]; //获取到这一行所在的文件名(通过inpusplit) String name = "你拿到的文件名"; //根据文件名,切分出各字段(如果是a,切分出两个字段,如果是b,切分出3个字段) OrderJoinBean bean = new OrderJoinBean(); bean.set(null, null, null, null, null); context.write(new Text(itemid), bean); } } static class OrderJoinReducer extends Reducer<Text, OrderJoinBean, OrderJoinBean, NullWritable> { @Override protected void reduce(Text key, Iterable<OrderJoinBean> beans, Context context) throws IOException, InterruptedException { //拿到的key是某一个itemid,比如1000 //拿到的beans是来自于两类文件的bean // {1000,amount} {1000,amount} {1000,amount} --- {1000,price,name} //将来自于b文件的bean里面的字段,跟来自于a的所有bean进行字段拼接并输出 } } } 缺点:这种方式中,join的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜 解决方案:map端join实现方式 4.4.2 map端join算法实现 1、原理阐述 适用于关联表中有小表的情形; 可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果,可以大大提高join操作的并发度,加快处理速度 2、实现示例 --先在mapper类中预先定义好小表,进行join --引入实际场景中的解决方案:一次加载数据库或者用distributedcache public class TestDistributedCache { static class TestDistributedCacheMapper extends Mapper<LongWritable, Text, Text, Text>{ FileReader in = null; BufferedReader reader = null; HashMap<String,String> b_tab = new HashMap<String, String>(); String localpath =null; String uirpath = null; //是在map任务初始化的时候调用一次 @Override protected void setup(Context context) throws IOException, InterruptedException { //通过这几句代码可以获取到cache file的本地绝对路径,测试验证用 Path[] files = context.getLocalCacheFiles(); localpath = files[0].toString(); URI[] cacheFiles = context.getCacheFiles(); //缓存文件的用法——直接用本地IO来读取 //这里读的数据是map task所在机器本地工作目录中的一个小文件 in = new FileReader("b.txt"); reader =new BufferedReader(in); String line =null; while(null!=(line=reader.readLine())){ String[] fields = line.split(","); b_tab.put(fields[0],fields[1]); } IOUtils.closeStream(reader); IOUtils.closeStream(in); } @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //这里读的是这个map task所负责的那一个切片数据(在hdfs上) String[] fields = value.toString().split("\t"); String a_itemid = fields[0]; String a_amount = fields[1]; String b_name = b_tab.get(a_itemid); //输出结果 100198.9banan context.write(new Text(a_itemid), new Text(a_amount + "\t" + ":" + localpath + "\t" +b_name )); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf); job.setJarByClass(TestDistributedCache.class); job.setMapperClass(TestDistributedCacheMapper.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); //这里是我们正常的需要处理的数据所在路径 FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); //不需要reducer job.setNumReduceTasks(0); //分发一个文件到task进程的工作目录 job.addCacheFile(new URI("hdfs://hadoop-server01:9000/cachefile/b.txt")); //分发一个归档文件到task进程的工作目录 //job.addArchiveToClassPath(archive); //分发jar包到task节点的classpath下 //job.addFileToClassPath(jarfile); job.waitForCompletion(true); } } 4.4.3 web日志预处理 1、需求: 对web访问日志中的各字段识别切分 去除日志中不合法的记录 根据KPI统计需求,生成各类访问请求过滤数据 2、实现代码: a)定义一个bean,用来记录日志数据中的各数据字段 public class WebLogBean { private String remote_addr;//记录客户端的ip地址 private String remote_user;//记录客户端用户名称,忽略属性"-" private String time_local;//记录访问时间与时区 private String request;//记录请求的url与http协议 private String status;//记录请求状态;成功是200 private String body_bytes_sent;//记录发送给客户端文件主体内容大小 private String http_referer;//用来记录从那个页面链接访问过来的 private String http_user_agent;//记录客户浏览器的相关信息 private boolean valid = true;//判断数据是否合法 public String getRemote_addr() { return remote_addr; } public void setRemote_addr(String remote_addr) { this.remote_addr = remote_addr; } public String getRemote_user() { return remote_user; } public void setRemote_user(String remote_user) { this.remote_user = remote_user; } public String getTime_local() { return time_local; } public void setTime_local(String time_local) { this.time_local = time_local; } public String getRequest() { return request; } public void setRequest(String request) { this.request = request; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getBody_bytes_sent() { return body_bytes_sent; } public void setBody_bytes_sent(String body_bytes_sent) { this.body_bytes_sent = body_bytes_sent; } public String getHttp_referer() { return http_referer; } public void setHttp_referer(String http_referer) { this.http_referer = http_referer; } public String getHttp_user_agent() { return http_user_agent; } public void setHttp_user_agent(String http_user_agent) { this.http_user_agent = http_user_agent; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.valid); sb.append("\001").append(this.remote_addr); sb.append("\001").append(this.remote_user); sb.append("\001").append(this.time_local); sb.append("\001").append(this.request); sb.append("\001").append(this.status); sb.append("\001").append(this.body_bytes_sent); sb.append("\001").append(this.http_referer); sb.append("\001").append(this.http_user_agent); return sb.toString(); } } b)定义一个parser用来解析过滤web访问日志原始记录 public class WebLogParser { public static WebLogBean parser(String line) { WebLogBean webLogBean = new WebLogBean(); String[] arr = line.split(" "); if (arr.length > 11) { webLogBean.setRemote_addr(arr[0]); webLogBean.setRemote_user(arr[1]); webLogBean.setTime_local(arr[3].substring(1)); webLogBean.setRequest(arr[6]); webLogBean.setStatus(arr[8]); webLogBean.setBody_bytes_sent(arr[9]); webLogBean.setHttp_referer(arr[10]); if (arr.length > 12) { webLogBean.setHttp_user_agent(arr[11] + " " + arr[12]); } else { webLogBean.setHttp_user_agent(arr[11]); } if (Integer.parseInt(webLogBean.getStatus()) >= 400) {//大于400,HTTP错误 webLogBean.setValid(false); } } else { webLogBean.setValid(false); } return webLogBean; } public static String parserTime(String time) { time.replace("/", "-"); returntime; } } c) mapreduce程序 public class WeblogPreProcess { static class WeblogPreProcessMapper extends Mapper<LongWritable, Text, Text, NullWritable> { Text k = new Text(); NullWritable v = NullWritable.get(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); WebLogBean webLogBean = WebLogParser.parser(line); if (!webLogBean.isValid()) return; k.set(webLogBean.toString()); context.write(k, v); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf); job.setJarByClass(WeblogPreProcess.class); job.setMapperClass(WeblogPreProcessMapper.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(NullWritable.class); FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); job.waitForCompletion(true); } } 本文转自yushiwh 51CTO博客,原文链接:http://blog.51cto.com/yushiwh/1913046,如需转载请自行联系原作者

优秀的个人博客,低调大师

Docker实践:搭建wordpress

①查看docker版本信息 先查看docker版本等信息,输入获取版本信息: [root@docker1 ~]#docker version Client: Version: 1.12.6 API version: 1.24 Package version: docker-1.12.6-61.git85d7426.el7.centos.x86_64 Go version: go1.8.3 Git commit: 85d7426/1.12.6 Built: Tue Oct 24 15:40:21 2017 OS/Arch: linux/amd64 Server: Version: 1.12.6 API version: 1.24 Package version: docker-1.12.6-61.git85d7426.el7.centos.x86_64 Go version: go1.8.3 Git commit: 85d7426/1.12.6 Built: Tue Oct 24 15:40:21 2017 OS/Arch: linux/amd64 [root@docker1 ~]#docker info Containers: 12 Running: 1 Paused: 0 Stopped: 11 Images: 133 Server Version: 1.12.6 Storage Driver: devicemapper Pool Name: docker-253:0-25843-pool Pool Blocksize: 65.54 kB Base Device Size: 10.74 GB Backing Filesystem: xfs Data file: /dev/loop0 Metadata file: /dev/loop1 Data Space Used: 11.53 GB Data Space Total: 107.4 GB Data Space Available: 1.073 GB Metadata Space Used: 15.47 MB Metadata Space Total: 2.147 GB Metadata Space Available: 1.073 GB Thin Pool Minimum Free Space: 10.74 GB Udev Sync Supported: true Deferred Removal Enabled: true Deferred Deletion Enabled: true Deferred Deleted Device Count: 0 Data loop file: /var/lib/docker/devicemapper/devicemapper/data WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device. Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata Library Version: 1.02.140-RHEL7 (2017-05-03) Logging Driver: journald Cgroup Driver: systemd Plugins: Volume: local Network: null overlay bridge host Swarm: inactive Runtimes: docker-runc runc Default Runtime: docker-runc Security Options: seccomp selinux Kernel Version: 3.10.0-693.5.2.el7.x86_64 Operating System: CentOS Linux 7 (Core) OSType: linux Architecture: x86_64 Number of Docker Hooks: 3 CPUs: 1 Total Memory: 472.3 MiB Name: docker1 ID: 7WA2:LZJV:R2X2:EV57:DFTD:4TOC:CT26:DRLS:PTMU:RE4W:SX5P:FQ4E Docker Root Dir: /var/lib/docker Debug Mode (client): false Debug Mode (server): false Registry: https://index.docker.io/v1/ WARNING: bridge-nf-call-iptables is disabled WARNING: bridge-nf-call-ip6tables is disabled Insecure Registries: 127.0.0.0/8 Registries: docker.io (secure) docker信息可以输出表示docker运行成功 ②下载mysql镜像 我们先拉下来mysql镜像,然后设定mysql密码(我设的123456),指定mysql版本为最新版(latest) [root@docker1 ~]# docker pull mysql Using default tag: latest Trying to pull repository docker.io/library/mysql ... latest: Pulling from docker.io/library/mysql f49cf87b52c1: Pull complete 78032de49d65: Pull complete 837546b20bc4: Pull complete 9b8316af6cc6: Pull complete 1056cf29b9f1: Pull complete 86f3913b029a: Pull complete 4cbbfc9aebab: Pull complete 8ffd0352f6a8: Pull complete 45d90f823f97: Pull complete ca2a791aeb35: Pull complete Digest: sha256:1f95a2ba07ea2ee2800ec8ce3b5370ed4754b0a71d9d11c0c35c934e9708dcf1 [root@docker1 ~]# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 4406:3306 -d mysql:latest 4710c01635048e6255348d40c706b93975c2bc73d8db8747ce98f8d7be82e858 [root@docker1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4710c0163504 mysql:latest "docker-entrypoint.sh" 4 seconds ago Up 3 seconds 0.0.0.0:4406->3306/tcp some-mysql 7、启动WordPress博客系统 输入一下命令,表示使用mysql来启动WordPress,且把宿主机端口8088与ubuntu端口80进行绑定。 [root@docker1 ~]# docker run --name some-wordpress --link some-mysql:mysql -p 8088:80 -d wordpress d80930fe7ad5ea3ddf8513117541140685759b4e5f22d70783acdf802504a272 [root@docker1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d80930fe7ad5 wordpress "docker-entrypoint.sh" 5 seconds ago Up 4 seconds 0.0.0.0:8088->80/tcp some-wordpress 4710c0163504 mysql:latest "docker-entrypoint.sh" 2 minutes ago Up 2 minutes 0.0.0.0:4406->3306/tcp some-mysql 此时,WordPress项目就跑起来了。 在本机输入localhost:8088,效果如下,是不是很酷!: 本文转自 Mr_sheng 51CTO博客,原文链接:http://blog.51cto.com/sf1314/2050682

优秀的个人博客,低调大师

MaxScale Binlog Server实践

简介 Part1:写在最前 在之前的博文中有说到MaxScale,作为中间件,配合MHA使用或者主从使用可实现读写分离和负载均衡,今天简单介绍下MaxScale作为Binlog Server来减少主从延迟的问题;MySQL的主从架构中,链式拓扑的架构比较容易出现主从延迟的问题。本文着重介绍MaxScale作为Binlog Server是如何降低主从延迟的。 MaxScale配合MHA请移步至: http://suifu.blog.51cto.com/9167728/1869520 Part2:本文环境 HE1:192.168.1.248 slave HE3:192.168.1.250 master HE4:192.168.1.251maxscale 架构演示 效果对比 实战 Part1:安装maxscale [root@HE4 ~]#yum -y install maxscale-2.0.1-2.centos.6.x86_64.rpm [root@HE4 ~]# mkdir -p /data/binlog [root@HE4 ~]# useradd maxscale [root@HE4 ~]# chown -R maxscale. /data/binlog [root@HE4 ~]# cat /etc/maxscale.cnf 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 [maxscale] threads=1 ##根据CPU核数设置 [Replication] type =service router=binlogrouter user=mysync passwd =MANAGER #使用主库上的repl复制账号 #权限: #GRANTREPLICATIONSLAVE,REPLICATIONCLIENTON*.*TO'repl'@'%'IDENTIFIEDBY'repl'; router_options=server_id=1251,heartbeat=30,binlogdir= /data/binlog ,transaction_safety=1,mariadb10-compatibility=1,send_slave_heartbeat=1 #server_id设置maxscale的,记得不能与主和从库重复,要唯一 #heartbeat=30秒,意思为当maxscale在30秒内没有接收到主库推送的binlog日志,发送心跳检查 #binlogdir设置接收binlog的存放路径,目录属性chown-Rmaxscale.maxscale/data/binlog #transaction_safety=1此参数用于启用binlog日志中的不完整事务检测。当MariaDBMaxScale启动时,如果当前binlog文件已损坏或找到不完整的事务,则可能会出现错误消息。在正常工作期间,binlog事件不会分配到从库,直到事务已经提交。默认值为off,设置transaction_safety=on以启用不完全事务检测。 #send_slave_heartbeat=1开启心跳检查 [ReplicationListener] type =listener service=Replication protocol=MySQLClient port=5308 #后端的从库CHANGEMASTERTO这个端口,默认5308 [CLI] type =service router=cli [CLIListener] type =listener service=CLI protocol=maxscaled port=6603 Part2:启动Maxscale [root@HE4 ~]# /etc/init.d/maxscale start Starting MaxScale: maxscale (pid 16680) is running... [ OK ] [root@HE4 ~]# /etc/init.d/maxscale status Checking MaxScale status: MaxScale (pid 16680) is running.[ OK ] Part3:从库配置 1 2 3 4 5 6 7 8 9 10 11 [root@HE1~] #mysql-umysync-pMANAGER-h192.168.1.251-P5308 WelcometotheMariaDBmonitor.Commandsendwith;or\g. YourMySQLconnection id is3196 Serverversion:10.0.02.0.1-maxscale Copyright(c)2000,2016,Oracle,MariaDBCorporationAbandothers. Type 'help;' or '\h' for help.Type '\c' to clear thecurrentinputstatement. MySQL[(none)]>CHANGEMASTERTOMASTER_HOST= '192.168.1.250' ,MASTER_USER= 'mysync' ,MASTER_PASSWORD= 'MANAGER' ,MASTER_PORT=3306,MASTER_LOG_FILE= 'mysql-bin.000005' ,MASTER_LOG_POS=20; ERROR1234(42000):Cannot set MASTER_LOG_POSto20:Permittedbinlogposis4.Specifiedmaster_log_file=mysql-bin.000005 MySQL[(none)]>CHANGEMASTERTOMASTER_HOST= '192.168.1.250' ,MASTER_USER= 'mysync' ,MASTER_PASSWORD= 'MANAGER' ,MASTER_PORT=3306,MASTER_LOG_FILE= 'mysql-bin.000005' ,MASTER_LOG_POS=4; MySQL[(none)]>startslave; QueryOK,0rowsaffected(0.00sec) 这里可以看出,Maxscale binlog server只能从位置4开始配置 配置好后,在/data/binlog下生成的binlog文件 [root@HE4 ~]# cd /data/binlog/ [root@HE4 binlog]# ls cache master.ini mysql-bin.000003 Part4:主库配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@HE3~] #mysql-uroot-p Enterpassword: WelcometotheMariaDBmonitor.Commandsendwith;or\g. YourMariaDBconnection id is7 Serverversion:10.1.16-MariaDBMariaDBServer Copyright(c)2000,2016,Oracle,MariaDBCorporationAbandothers. Type 'help;' or '\h' for help.Type '\c' to clear thecurrentinputstatement. MariaDB[(none)]>showmasterstatus; +------------------+----------+--------------+------------------+ |File|Position|Binlog_Do_DB|Binlog_Ignore_DB| +------------------+----------+--------------+------------------+ |mysql-bin.000005|652||| +------------------+----------+--------------+------------------+ 1row in set (0.00sec) MariaDB[(none)]>grantreplicationclient,replicationslaveon*.*to 'mysync' @ '192.168.1.%' identifiedby 'MANAGER' ; MariaDB[(none)]>flushprivileges; Part5:主从配置 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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 从库指向binlogserver [root@HE1~] #mysql-uroot-pMANAGER WelcometotheMariaDBmonitor.Commandsendwith;or\g. YourMariaDBconnection id is5 Serverversion:10.1.16-MariaDBMariaDBServer Copyright(c)2000,2016,Oracle,MariaDBCorporationAbandothers. Type 'help;' or '\h' for help.Type '\c' to clear thecurrentinputstatement. MariaDB[(none)]>CHANGEMASTERTOMASTER_HOST= '192.168.1.251' ,MASTER_USER= 'mysync' ,MASTER_PASSWORD= 'MANAGER' ,MASTER_PORT=5308,MASTER_LOG_FILE= 'mysql-bin.000005' ,MASTER_LOG_POS=652; QueryOK,0rowsaffected(0.02sec) MariaDB[(none)]>startslave; QueryOK,0rowsaffected(0.00sec) MariaDB[(none)]>showslavestatus\G ***************************1.row*************************** Slave_IO_State:Waiting for mastertosendevent Master_Host:192.168.1.251 Master_User:mysync Master_Port:5308 Connect_Retry:60 Master_Log_File:mysql-bin.000005 Read_Master_Log_Pos:652 Relay_Log_File:mysql-relay-bin.000002 Relay_Log_Pos:537 Relay_Master_Log_File:mysql-bin.000005 Slave_IO_Running:Yes Slave_SQL_Running:Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno:0 Last_Error: Skip_Counter:0 Exec_Master_Log_Pos:652 Relay_Log_Space:835 Until_Condition:None Until_Log_File: Until_Log_Pos:0 Master_SSL_Allowed:No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master:0 Master_SSL_Verify_Server_Cert:No Last_IO_Errno:0 Last_IO_Error: Last_SQL_Errno:0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id:1250 Master_SSL_Crl: Master_SSL_Crlpath: Using_Gtid:No Gtid_IO_Pos: Replicate_Do_Domain_Ids: Replicate_Ignore_Domain_Ids: Parallel_Mode:conservative 1row in set (0.00sec) ——总结—— 生产环境中,大多采用的是一主多从架构,例如星状拓扑和链式拓扑,星状拓扑在从库过多的情况下,会增加主库的io压力,而链式拓扑虽然缓解了主库的网络IO压力,但其缺点是:二级Slave得到最新的数据,需要再经过一层的复制才到达,期间的延迟比一主多从架构要大。而采用maxscale binlog server则避免了这类问题。由于笔者的水平有限,编写时间也很仓促,文中难免会出现一些错误或者不准确的地方,不妥之处恳请读者批评指正。 本文转自 dbapower 51CTO博客,原文链接:http://blog.51cto.com/suifu/1878847,如需转载请自行联系原作者

优秀的个人博客,低调大师

Oracle备份还原实践

一、项目背景: 1.1 客户需求: 将物理机房生产环境的Oracle/MySQL及阿里云的RDS数据库备份出来,存储到一个集中数据库存储服务器,实现异地备份,并且在还原环境内要将MySQL/Oracle以及阿里云RDS备份数据还原到测试环境,并判断还原数据库是否存在异常,检验数据的一致性,如有异常邮件或微信告警,每周生成Excel报表发送给负责人。 1.2 需求要点: 网络通信:阿里金融云/公有云/物理机房环境网络须在固定网段互通。 网络安全:各个网段隔离,有需求通信的网段开放通信,需实现端口级别控制。 数据备份:MySQL/Oracle/RDS实现数据库备份。 数据传输:采用定时crond+scp+rsync配合传输。 数据校验:保障数据库还原成功的可靠性。 监控告警:***网络中断,或数据库备份失败等需要发送通知到管理员。 报表生成:将数据库还原的信息生成报表统一发送给管理员。 1.3 解决方案: 网络通信:在阿里云端,采用深信服IPSec ***与物理机房Cisco设备隧道互通。开通传输网段,将此网段作为网络传输中转网段。 网络安全:各个网段隔离,如有跨地区或机房相互通信的需求,需将此环境下的数据传输到中转网段,再实现数据传输。使用阿里云安全组deny any,开通需要通信的白名单端口。 数据备份:MySQL及RDS采用mysqldump逻辑备份,Oracle采用expdp备份。 数据传输:采用定时crond+scp+rsync配合传输。 数据校验:导入文件存在日志,查看日志与比对库数目。 监控告警:采用smarteye自定义监控,监控***状况,同事采用自定义脚本+数据库还原异常告警。如果***中断或数据库恢复异常发送短信,邮件,微信消息通知管理员。 报表生成:每日脚本将数据库还原日志文件进行处理,最终打包统一传输到一个Python环境下,利用自编写Python脚本处理文件数据,生成报表,每周发送给管理员。 二、逻辑拓扑: 2.1 Oracle数据库备份还原: 2.1 Oracle数据库备份还原: 三、技术细节: 3.1 Oracle数据库备份还原: 创建Oracle数据库备份用户: createuserbackuseridentifiedbypwdbackuser; 默认情况下用户创建好后系统会默认给该用户分配一个表空间(users); 查看用户表空间: selectusername,default_tablespacefromdba_users; 需要为创建的用户创建自己的表空间: createtablespacebaktablesdatafile'/data/bakdir/baktab_data.dbf'size200M; 分配了表空间,此用户还无法登录,因此需要为用户授权: grantcreatesession,createtable,createview,createsequence,unlimitedtablespacetobackuser; 将创建好的表空间分配给用户: alteruserbackuserdefaulttablespacebaktables; 创建Oracle数据库备份目录: createdirectorydump_diras'/backup/backup_dir'; 查看备份目录: select*fromdba_directories; 授权备份用户对备份目录具有读写权限: Grantread,writeondirectorydump_dirtobackuser; 使用expdp导出数据库: expdpbackuser/pwdbackuserSCHEMAS=DB1dumpfile=DB1.dmpdirectory=dump_dirlog=DB1.log 查看备份日志: Oracle自动备份脚本示例: #!/bin/bash exportORACLE_BASE=/u01/app/oracle exportORACLE_HOME=$ORACLE_BASE/product/11.2.0/db_1 exportORACLE_SID=ORCL exportPATH=$ORACLE_HOME/bin:$PATH filename=`date+%Y-%m-%d`'.dmp' logname=`date+%Y-%m-%d`'.log' dump_dir="/backup/backup_dir/" backdir="dump_dir" forSchameinDB1....DB2 do /u01/app/oracle/product/11.2.0/db_1/bin/expdpbackuser/pwdbackuserSCHEMAS=${Schame}dumpfile=${Schame}-${filename}directory=${backdir}log=${Schame}-${logname} find${dump_dir}-mtime+7-name"*.dmp"-execrm-rf{}\; find${dump_dir}-mtime+7-name"*.log"-execrm-rf{}\; done Oracle 备份检查脚本: #!/bin/bash DATE=`date+%F` MON=`date+%Y-%m` M_date=`date+%Y/%m/%d` DIR='/backup/backup_dir/' FDIR='/backup/Oracle_Excel/' if[!-d${FDIR}];then mkdir-p${FDIR} fi O_FILE='/backup/Oracle_Excel/tmp_OracleB.txt' #Oracle END_TIME=`find${DIR}-name"*-${DATE}.dmp"-execls-l{}\;|awk'{print$8}'|sort-r|head-1` FILE=`find${DIR}-name"*-${DATE}.dmp"-execls{}\;|awk'{printf("%s",$1)}'` #O_SIZE=`find${DIR}-name"*-${DATE}.dmp"-execdu{}\;|awk'{sum+=$1}END{printsum/1024"M"}'` O_SIZE=`find${DIR}-name"*-${DATE}.dmp"-execdu-sh{}\;|awk'{printf("%s",$1)}'` #writefile echo-e"${M_date},ORACLE,分库备份,(逻辑)每天,22:00:00,${END_TIME},成功,${FILE},否,${O_SIZE},否\n\c">>${O_FILE} Week=`date+%w` if[${Week}-eq0];then if[!-d${FDIR}${DATE}];then mkdir-p${FDIR}${DATE} fi mv${O_FILE}${FDIR}${DATE} /usr/bin/zip-r${FDIR}OracleB_${DATE}.zip${FDIR}${DATE}/* if[$?-eq0];then /usr/bin/scp${FDIR}OracleB_${DATE}.ziproot@192.168.11.11:/user/backup/oracle/Oracle_Excel fi rm-rf${FDIR}OracleB_${DATE}.zip fi 查看数据存储服务器: 脚本拆分开,在那个步失败,可以单独进行恢复统一进行调用: SCP传输 #!/bin/bash fdate=`date+%Y-%m-%d-d'-1day'` /usr/bin/scp-P2621root@172.16.84.12:/backup/backup_dir/*-${fdate}.dmp/user/backup/oracle/oraclebak 分类归档压缩: #!/bin/bash fdate=`date+%Y-%m-%d-d'-1day'` filename=`date+%Y-%m-%d-d'-1day'`'.dmp' dump_dir="/user/backup/oracle/oraclebak" I=`ls/user/backup/oracle/oraclebak/|grep$filename|awk-F'-''{print$1}'|uniq` cd${dump_dir} forSchamein${I[*]} do tarzcf${Schame}-$filename.tar.gz${Schame}-$filename done find${dump_dir}-name"*.dmp"-execrm-rf{}\; find${dump_dir}-mtime+7-name"*.dmp.tar.gz"-execrm-rf{}\; rsync进行断点传输: #!/bin/bash #Data=`date+%Y-%m-%d""%H:%m` dir="/user/backup/oracle/" fdate=`date+%Y-%m-%d-d'-1day'` filename=`date+%Y-%m-%d-d'-1day'`'.dmp' /usr/bin/rsync-rP--timeout=3600--rsh=ssh/user/backup/oracle/oraclebak/DB1-${fdate}.dmp.tar.gzroot@172.17.130.130:/DATA/oracle/oracle_bak&&echo"$DataDB1rsyncissuccess!">>${dir}Logdir/oracle-rsync.log if["$?"=="0"];then Data=`date+%Y-%m-%d""%H:%m` /usr/bin/rsync-rP--timeout=3600--rsh=ssh/user/backup/oracle/oraclebak/DB2-${fdate}.dmp.tar.gzroot@172.17.130.130:/DATA/oracle/oracle_bak&&echo"$DataDB2rsyncissuccess!">>${dir}Logdir/oracle-rsync.log if["$?"=="0"];then unsetData Data=`date+%Y-%m-%d""%H:%m` /usr/bin/rsync-rP--timeout=3600--rsh=ssh/user/backup/oraclebak/DB3-${fdate}.dmp.tar.gzroot@172.17.130.130:/DATA/oracle/oracle_bak&&echo"$DataDB3rsyncissuccess!">>${dir}Logdir/oracle-rsync.log if["$?"=="0"];then unsetData Data=`date+%Y-%m-%d""%H:%m` echo"$Datarsyncissuccess!">>${dir}Logdir/oracle-rsync.log fi fi fi 总体调用: #!/bin/bash #Data=`date+%Y-%m-%d""%H:%m` dir="/user/backup/oracle/" /bin/bash${dir}1_oracle_scp.sh if["$?"=="0"];then Data=`date+%Y-%m-%d""%H:%m` echo"$Data1_oracle_scp.shisexecsuccess!">>${dir}Logdir/oracle-back.log&&/bin/bash${dir}2_oracle_tar.sh if["$?"=="0"];then unsetData Data=`date+%Y-%m-%d""%H:%m` echo"$Data2_oracle_tar.shisexecsuccess!">>${dir}Logdir/oracle-back.log&&/bin/bash${dir}3_oracle_rsync.sh if["$?"=="0"];then unsetData Data=`date+%Y-%m-%d""%H:%m` echo"$Data3_oracle_rsync.shisexecsuccess!">>${dir}Logdir/oracle-back.log fi fi fi 在还原服务器进行Oracle数据库还原: 首先初步核查传输过来的库数目及文件大小是否异常,如果异常可以进行重新拉取: #!/bin/bash oracle_path="/DATA/oracle/oracle_bak/" Data=`date+%Y-%m-%d""%H:%M` dir="/user/backup/oracle/" fdate=`date+%Y-%m-%d-d'-1day'` check_data=`date+%Y-%m-%d-d'-3day'` check_size=`du-sh/DATA/oracle/oracle_bak/${check_data}/|awk'{print$1}'|cut-dM-f1|awk-F.'{print$1}'` filename=`date+%Y-%m-%d-d'-1day'`'.dmp' NUM=`ls${oracle_path}*.tar.gz|wc-l` SIZE=`du-sh/DATA/oracle/oracle_bak/${fdate}/|awk'{print$1}'|cut-dM-f1|awk-F.'{print$1}'` if["$NUM"!="3"]||[$SIZE-lt${check_size}];then /usr/bin/rsync-ravP--timeout=3600--rsh=sshroot@10.199.75.14:/user/backup/oracle/oraclebak/DB1-${fdate}.dmp.tar.gz${oracle_path} if["$?"=="0"];then /usr/bin/rsync-ravP--timeout=3600--rsh=sshroot@10.199.75.14:/user/backup/oracle/oraclebak/DB2-${fdate}.dmp.tar.gz${oracle_path} if["$?"=="0"];then /usr/bin/rsync-ravP--timeout=3600--rsh=sshroot@10.199.75.14:/user/backup/oracle/oraclebak/DB3-${fdate}.dmp.tar.gz${oracle_path} fi fi fi 解压传输过来的数据库: #!/bin/bash #数据库存储文件 oracle_path="/DATA/oracle/oracle_bak/" #数据库恢复目录 repath="/DATA/oracle/oracle_restore/" #数据库导入目录 backdir="/home/oracle/app/backup_dir/" #数据库归档文件命名 dadir=`date+%Y-%m-%d-d-1day` #数据库导入日志目录 implogdir="/DATA/oracle/implogdir/" oraclecmd="/home/oracle/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus" if[!-d${implogdir}${dadir}];then mkdir-p${implogdir}${dadir} fi if[!-d${repath}];then mkdir-p${repath} fi if[!-d${oracle_path}${dadir}];then mkdir${oracle_path}${dadir} fi if[!-d${repath}${dadir}];then mkdir${repath}${dadir} fi #把压缩文件存放在日期目录 sudochown-Roracle:dba${oracle_path}* cd${oracle_path} mv*-"$dadir".dmp.tar.gz$dadir #把压缩文件解压到还原目录 cd${oracle_path}${dadir} oracle=`ls` forIin${oracle[*]} do /bin/tarzxf$I-C${repath}${dadir} done cp${repath}${dadir}/*${backdir} 进行Oracle还原库用户初始化:(此处列两个库做说明) #!/bin/bash exportORACLE_BASE=/home/oracle/app exportORACLE_HOME=$ORACLE_BASE/oracle/product/12.1.0/dbhome_1 exportORACLE_SID=ORCL exportPATH=$PATH:$HOME/bin:$ORACLE_HOME/bin #数据库恢复目录 repath="/DATA/oracle/oracle_restore/" #数据库导入目录 backdir="/home/oracle/app/backup_dir/" #数据库归档文件命名 dadir=`date+%Y-%m-%d-d-1day` #数据库导入日志目录 implogdir="/DATA/oracle/implogdir/" oraclecmd="/home/oracle/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus" cd${backdir} fordbinDB1...DBn do if["$db"=="DB1"];then sqlplus-S/nolog<<EOF conn/assysdba dropuser${db}cascade; createuser${db}identifiedbyDBuser1; alteruserDB_user1defaulttablespacetab1; grantcreatesession,createtable,createview,createprocedure,createsequence,unlimitedtablespaceto${db}; Grantread,writeondirectorydump_dirto${db}; exit; else["$db"=="GPSUSER"]; sqlplus-S/nolog<<EOF conn/assysdba dropuser${db}cascade; createuser${db}identifiedbyDBuser2; alteruserDBdefaulttablespacetab2; grantcreatesession,createtable,createview,createprocedure,createsequence,unlimitedtablespaceto${db}; Grantread,writeondirectorydump_dirto${db}; exit; EOF fi done Oracle数据库采用impdp进行导入: #!/bin/bash exportORACLE_BASE=/home/oracle/app exportORACLE_HOME=$ORACLE_BASE/oracle/product/12.1.0/dbhome_1 exportORACLE_SID=glpfin exportPATH=$PATH:$HOME/bin:$ORACLE_HOME/bin #数据库存储文件 oracle_path="/DATA/oracle/oracle_bak/" #数据库恢复目录 repath="/DATA/oracle/oracle_restore/" #数据库导入目录 backdir="/home/oracle/app/backup_dir/" #数据库归档文件命名 dadir=`date+%Y-%m-%d-d'-1day'` #数据库导入日志目录 implogdir="/DATA/oracle/implogdir/" oraclecmd="/home/oracle/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus" I=`ls/DATA/oracle/oracle_restore/$dadir/|grep$dadir|awk-F'-''{print$1}'|uniq` cd${backdir} fordbin${I[*]} do impdpsystem/51idc.comdirectory=dump_dirdumpfile=${db}-${dadir}.dmplogfile=import-${db}-${dadir}.log rm-rf${backdir}${db}-${dadir}.dmp&&mvimport-${db}-${dadir}.log${implogdir}${dadir} /bin/mail-rxuel@anchnet.com-s"Oracle-${db}-backup-mail"oraclebak@anchnet.com<${implogdir}${dadir}/import-${db}-${dadir}.log done find${oracle_path}-mtime+30-name"*.tar.gz"-execrm-rf{}\; find${implogdir}-ctime+30-typed-execrm-rf{}\; find${repath}-mtime+7-name"*.dmp"-execrm-rf{}\; find${repath}-typed-mtime+7-execrm-rf{}\; 分析导入log,处理后写入文件 #!/bin/bash date=`date+%Y/%m/%d""%H:%M` Logdir="/DATA/oracle/Logdir/" dadir=`date+%Y-%m-%d-d'-1day'` implogdir="/DATA/oracle/implogdir/" if[-d${implogdir}${dadir}];then File=`ls${implogdir}${dadir}|grep${dadir}|awk-F'-''{print$2}'|uniq` forIin${File[*]} do echo${date}>>${Logdir}${I}-import.log tail-1${implogdir}${dadir}/import-${I}-${dadir}.log>>${Logdir}${I}-import.log done fi ***监控脚本: #!/bin/bash IP=10.199.75.14 dir="/DATA/oracle/netdir/" if[!-d${dir}];then mkdir-p${dir} fi echo1>${dir}ping.lock whiletrue do Time=`date+%F` TIME="${Time}23:59" if["${data}"=="${TIME}"];then mkdir${dir}${Time}&&mv${dir}ping2.log${dir}${Time}-ping2.log mv${dir}${Time}-ping2.log${dir}${Time} fi find${dir}-mtime+7-name"*-ping2.log"-execrm-rf{}\; find${dir}-mtime+7-typed-execrm-rf{}\; data=`date+%F''%H:%M` data1=`date+%F''%H:%M:%S` echo"------------${data1}---------------">>${dir}ping2.log ping-c10${IP}>>${dir}ping2.log if[$?-eq1];then STAT=`cat${dir}ping.lock` if[${STAT}-eq1];then /usr/bin/python/DATA/oracle/netdir/GFweixin.pyxuel***-monitor"GLPfromPDC(192.168.11.11)ping金融云(10.75.128.8)中断,请检查深信服***!\nTIME:${ data1}"echo0>${dir}ping.lock else continue fi else STAT=`cat${dir}ping.lock` if[${STAT}-eq0];then /usr/bin/python/DATA/oracle/netdir/GFweixin.pyxuel***-monitor"***monitorfrom物理机(192.168.11.11)ping金融云(10.199.75.14)恢复!\nTIME:${data1}" echo1>${dir}ping.lock else continue fi fi done 如有异常会发送告警: 微信告警: 短信告警: 日志收集脚本: #!/bin/bash DATE=`date+%F` MON=`date+%Y-%m` FDIR='/user/backup/mysql/MySQL_Excel/' if[!-d${FDIR}];then mkdir-p${FDIR} fi DIR='/user/backup/mysql/sub-treasury/' MySQL_FILE='/user/backup/mysql/MySQL_Excel/tmp_MySQLB.txt' M_date=`date+%Y/%m/%d` M_SIZE=`find${DIR}-name"*-${DATE}.sql.tar.gz"-execdu{}\;|awk'{sum+=$1}END{printsum/1024"M"}'` #writefile echo-e"${M_date},MySQL,分库备份(逻辑),每天,22:00:00,22:02:00,成功,${DIR}*-${DATE}.sql.tar.gz,是,${M_SIZE},否\n\c">>${MySQL_FILE} Week=`date+%w` if[${Week}-eq0];then if[!-d${FDIR}${DATE}];then mkdir-p${FDIR}${DATE} fi mv${MySQL_FILE}${FDIR}${DATE} /usr/bin/zip-r${FDIR}MySQLB_${DATE}.zip${FDIR}${DATE}/* if[$?-eq0];then /usr/bin/scp${FDIR}MySQLB_${DATE}.ziproot@172.16.6.150:/DATA/oracle/Excel/MySQL_ZIP fi rm-rf${FDIR}MySQLB_${DATE}.zip fi 集中处理文件脚本: #!/bin/bash DATE=`date+%F` LDATE=`date+%F-d'-1day'` MON=`date+%Y-%m` DIR='/DATA/oracle/Excel/' tmp_dir='/DATA/oracle/Excel/Tmp_restore/' res_dir='/DATA/oracle/Excel/Totle_restore/' ZIP='/usr/bin/unzip' zipfunction(){ $ZIP$1-d${tmp_dir} TXT=`find${tmp_dir}-nametmp_*.txt` cp$TXT${res_dir} rm-rf${tmp_dir}* } foriin`find${DIR}-name*_${LDATE}.zip-execls{}\;` do zipfunction$i done cd${DIR}scripts/ if[$?-eq0];then /bin/python34${DIR}scripts/GLP_excel.py fi if[!-d${DIR}scripts/${MON}];then mkdir-p${DIR}scripts/${MON} fi mv${DIR}scripts/Oralce_bak.xlsx${DIR}scripts/${MON}/GLP_${DATE}.xlsx cd${DIR}scripts/${MON}/ /bin/mailx-rxuel@anchnet.com-s"Oracle-Excel-report"-a${DIR}scripts/${MON}/GLP_${DATE}.xlsxOraclebak@anchnet.com<${DIR}scripts/${MON}/GLP_${DATE}.xlsx rm-rf${DIR}Totle_restore/* Python脚本将文件集中处理生成Excel:(python写的不是很好,初步完成生成报表功能。) #!/bin/envpython34 importxlsxwriter #定义excel对象workbook workbook=xlsxwriter.Workbook("Oracle.xlsx") #MySQLsheet格式定义 worksheet_M=workbook.add_worksheet('MySQL备份详情表') worksheet_M.set_column('A:K',12) worksheet_M.set_row(0,17) worksheet_M.set_column('C:C',20) worksheet_M.set_column('H:H',58) #MySQLDWsheet格式定义 worksheet_MDW=workbook.add_worksheet('MySQL-DW备份详情表') worksheet_MDW.set_column('A:K',12) worksheet_MDW.set_row(0,17) worksheet_MDW.set_column('C:C',20) worksheet_MDW.set_column('H:H',58) #Oraclesheet格式定义 worksheet_O=workbook.add_worksheet('ORACLE备份详情表') worksheet_O.set_column('A:K',12) worksheet_O.set_row(0,17) worksheet_O.set_column('G:H',40) worksheet_O.set_column('C:C',16) #NFSsheet格式定义 worksheet_N=workbook.add_worksheet('NFS备份详情表') worksheet_N.set_column('A:K',14) worksheet_N.set_row(0,17) worksheet_N.set_column('H:H',59) #定义表头格式 merge_format=workbook.add_format({ 'bold':1, 'border':1, 'align':'center', 'valign':'vcenter', 'fg_color':'#FAEBD7' }) #表各项目名称格式 name_format=workbook.add_format({ 'bold':1, 'border':1, 'align':'center', 'valign':'vcenter', 'fg_color':'#E0FFFF' }) #表内容格式 normal_format=workbook.add_format({ 'align':'center' }) #写入个表项目名称函数 defset_title(file_list,row,col,worksheet): foriinfile_list: worksheet.write(row,col,i,name_format) col+=1 #写入表内容函数 defset_content(file_content,row,worksheet): withopen(file_content,'r')asF: foriinF: listnum=list(i.split(',')) col=0 forjinlistnum: worksheet.write(row,col,j,normal_format) col+=1 row+=1 #写入MySQLsheet表头 worksheet_M.merge_range('A1:K1','MySQL备份详情表',merge_format) worksheet_M.merge_range('A12:K12','MySQL还原详情表',merge_format) #写入MySQLDWsheet表头 worksheet_MDW.merge_range('A1:K1','MySQL-DW备份详情表',merge_format) worksheet_MDW.merge_range('A12:K12','MySQL-DW还原详情表',merge_format) #写入Oraclesheet表头 worksheet_O.merge_range('A1:K1','ORACLE备份详情表',merge_format) worksheet_O.merge_range('A11:K11','ORACLE传输详情表',merge_format) worksheet_O.merge_range('A21:K21','ORACLE还原详情表',merge_format) #写入NFSsheet表头 worksheet_N.merge_range('A1:J1','NFS备份详情表',merge_format) worksheet_N.merge_range('A12:J12','vsftp备份详情表',merge_format) #定义MySQL表各项目名称 MySQL_Baklist=['备份日期','备份对象','备份类型','备份周期','备份开始时间','备份结束时间','备份状态','备份文件','是否压缩','备份文件大小','是否补备'] MySQL_Reslist=['还原日期','还原对象','还原类型','还原周期','还原开始时间','还原状态','库数目','库数目对比、还原详情','补还原'] #定义MySQL-DW表各项目名称 MySQLDW_Baklist=['备份日期','备份对象','备份类型','备份周期','备份开始时间','备份结束时间','备份状态','备份文件','是否压缩','备份文件大小','是否补备'] MySQLDW_Reslist=['还原日期','还原对象','还原类型','还原周期','还原开始时间','还原状态','库数目','库数目对比、还原详情','补还原'] #定义Oracle表各项目名称 Oracle_Baklist=['备份日期','备份对象','备份类型','备份周期','备份开始时间','备份结束时间','备份状态','备份文件','是否压缩','备份文件大小','是否补备'] Oracle_Tralist=['传输日期','传输对象','类型','传输周期','开始时间','scp传输打包状态','rsync状态','rsync文件','rsync重传'] Oracle_Reslist=['还原日期','还原对象','还原类型','还原周期','开始时间','还原状态','还原详情','补还原'] #定义NFS表各项目名称 Rsync_list=['备份日期','备份对象','备份类型','备份周期','备份开始时间','备份结束时间','备份状态','日志文件','是否压缩','是否补备'] Vsftp_list=['备份日期','备份对象','备份类型','备份周期','备份开始时间','备份结束时间','备份状态','日志文件','是否压缩','是否补备'] #写入MySQL表项目名称 set_title(MySQL_Baklist,row=1,col=0,worksheet=worksheet_M) set_title(MySQL_Reslist,row=12,col=0,worksheet=worksheet_M) #写入MySQL-DW表项目名称 set_title(MySQLDW_Baklist,row=1,col=0,worksheet=worksheet_MDW) set_title(MySQLDW_Reslist,row=12,col=0,worksheet=worksheet_MDW) #写入Oracle表项目名称 set_title(Oracle_Baklist,row=1,col=0,worksheet=worksheet_O) set_title(Oracle_Tralist,row=11,col=0,worksheet=worksheet_O) set_title(Oracle_Reslist,row=21,col=0,worksheet=worksheet_O) #写入NFS表项目名称 set_title(Rsync_list,row=1,col=0,worksheet=worksheet_N) set_title(Vsftp_list,row=12,col=0,worksheet=worksheet_N) #定义MySQL内容数据文件路径 MySQLB_file='/DATA/oracle/Excel/Totle_restore/tmp_MySQLB.txt' MySQLR_file='/DATA/oracle/Excel/Totle_restore/tmp_MySQLR.txt' #定义MySQL-DW内容数据文件路径 MySQLDWB_file='/DATA/oracle/Excel/Totle_restore/tmp_MySQLDWB.txt' MySQLDWR_file='/DATA/oracle/Excel/Totle_restore/tmp_MySQLDWR.txt' #定义Oracle内容数据文件路径 OracleB_file='/DATA/oracle/Excel/Totle_restore/tmp_OracleB.txt' OracleT_file='/DATA/oracle/Excel/Totle_restore/tmp_OracleT.txt' OracleR_file='/DATA/oracle/Excel/Totle_restore/tmp_OracleR.txt' #定义NFS内容数据文件路径 Rsync_file='/DATA/oracle/Excel/Totle_restore/tmp_rsync.txt' Vsftp_file='/DATA/oracle/Excel/Totle_restore/tmp_vsftpd.txt' #写入MySQL数据内容 set_content(MySQLB_file,row=2,worksheet=worksheet_M) set_content(MySQLR_file,row=13,worksheet=worksheet_M) #写入MySQL数据内容 set_content(MySQLDWB_file,row=2,worksheet=worksheet_MDW) set_content(MySQLDWR_file,row=13,worksheet=worksheet_MDW) #写入Oracle数据内容 set_content(OracleB_file,row=2,worksheet=worksheet_O) set_content(OracleT_file,row=12,worksheet=worksheet_O) set_content(OracleR_file,row=22,worksheet=worksheet_O) #写入NFS数据内容 set_content(Rsync_file,row=2,worksheet=worksheet_N) set_content(Vsftp_file,row=13,worksheet=worksheet_N) #关闭workbook workbook.close() 报表查看:

优秀的个人博客,低调大师

Greenplum 模糊查询 实践

标签 PostgreSQL , Greenplum , orafunc , 阿里云HybridDB for PostgreSQL , reverse , like , 模糊查询 背景 文本搜索的需求分为: 1、全匹配,如: select * from table where column = 'xxxx'; 2、后模糊,如: select * from table where column like 'xxxx%'; 3、前模糊,如: select * from table where column like '%xxxx'; 4、前后模糊,如: select * from table where column like '%xxxx%'; 《中文模糊查询性能优化 by PostgreSQL trgm》 5、正则,如: select

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册