Redis设置密码,保护数据安全

1.设置密码方法

在 redis.conf 的配置文件中修改参数 requirepass 的值为需要设置的密码,

保存配置文件后,重启Redis就可以。

建议 设置比较复杂和长的密码,防止被暴力破解。

 

2.redis-cli连接redis方式

(1)连接:redis-cli -p 6391

(2)认证:AUTH  密码

建议 连接后使用AUTH 密码认证 ,防止history查看执行命令历史,泄露密码。

 

3.修改默认端口号

建议不要使用默认端口6379,防止被调用到。

 

数据安全无小事!

 

Java SE 8 并发增强

1.原子值

java5开始,提供了一些原子操作的类,如AtomicInteger、AtomicLong等

这些类提供了诸如incrementAndGet这样的原子操作方法。

单数如果想进行复杂操作,则需要使用compareAndSet进行循环处理

do {

   // .. 计算

} while (!atomicLong.compareAndSet(old, new));

在java8中提供了updateAndGet和accumulateAndGet方法

atomicLong,updateAndGet(x -> Max.max(x, observed));

atomicLong.accumulateAndGet(observed, Math::max);

同时也提供了返回原始值的对应方法:getAndUpdate、getAndAccumulate

——————————————————

 

当大量线程访问同一个原始值时,由于乐观锁重试次数太多会导致性能下降

Java8为此提供了LongAdder和LongAccumulator解决该问题

其思想为将初始值变为多个中立元素,计算时不同线程可以对不同元素进行操作,最后再将操作结果合并。

 

例如:

LongAccumulator adder = new LongAccumulator (Long::sum, 0);

adder.accumulate(value);

此时在LongAccumulator 中包含多个中立元素a1,a2…aN.该例子下中立元素初始值都为零。当调用accumulate方法累加value时,这些变量的其中之一被更新为ai = ai op v。在这个实力中ai = ai + v;

而最后调用get方法的时候,结果为a1 op a2 op … aN. 在上述例子中为a1+a2+…aN

——————————————————

java8中还添加了StampedLock类实现乐观读

调用tryOptimisticRead方法时会获取一个印戳,当读取值并检测印戳有效,则可以使用这个值,否则会获得一个阻塞所有写锁的读锁

例:

复制代码
public class Vector {  
   private int size;  
   private Object[] elements;  
   private StampedLock lock = new StampedLock();  
   public Object get(int n) {  
        long stamp = lock.tryOptimisticRead();  
        Object[] currentElements = elements;  
        int currentSize = size;  
        if (!lock.validate(stamp)) { //  Someone else had a write lock  
             stamp = lock.readLock(); //  Get a pessimistic lock  
             currentElements = elements;  
             currentSize = size;  
             lock.unlockRead(stamp);  
        }  
    return n < currentSize ? currentElements[n] : null;  
  }  
...  
复制代码

2.ConcurrentHashMap改进

 

1. 更新值

concurrentHashMap在更新数值的时候虽然是线程安全的,但是在计算更新值的时候由于不能保证线程安全,更新的值可能是错误的。

一种补救措施是使用replace

例:

1 do {  
2    oldValue = map.get(word);  
3    newValue = oldValue == null ? 1 : oldValue + 1;  
4 } while (!map.replace(key, oldValue, newValue));  

此外还可以使用利用原子对象,例如CuncurrentHashMap

map.putIfAbsent(word, new LongAdder());  
map.get(word).increment();  

如果需要复杂计算,compute方法可以通过一个函数来计算新的值

map.compute(word, (k, v) -> v == null ? 1 : v + 1);  

xxxIfPresent和xxxIfAbsent方法分别表示已经存在值或者尚未存在值的情况下才进行操作

 

 

merge方法可以在key第一次加入时做一些特殊操作,第二个参数表示键尚未存在时的初始值

map.merge(word, 1L, (existingValue, newValue) -> existingValue + newValue);  

map.merge(word, 1L, Long::sum);  

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

 

2. 批量数据操作

・search会对每个键值对领用一个函数,直到函数返回非null,search会终止并返回函数结果

・reduce会通过提供的累计函数,将所有键值对组合起来

・foreach会对所有键值对应用一个函数

每个操作都有4个版本:

• operation Keys : 对键操作
• operation Values : 对值操作
• operation: 对键和值操作
• operation Entries : 对  Map.Entry 对象操作.

 

以search为例,有以下几个方法:

U searchKeys(long threshold, BiFunction f)
U searchValues(long threshold, BiFunction f)
U search(long threshold, BiFunction f)
U searchEntries(long threshold, BiFunction, ? extends U> f)

threshold为并行阀值,如果包含的元素数量超过阀值,操作会以并行方式执行,如果希望永远以单线程执行,请使用Long.MAX_VALUE

 

foreach和reduce方法除了上述形式外,还有另一种形式,可以提供一个转换器函数,首先会应用转换器函数,然后再将结果传递给消费者函数

map.forEach(threshold,  
(k, v) -> k + " -> " + v, // Transformer  
System.out::println); // Consumer  

 

Integer maxlength = map.reduceKeys(threshold,  
String::length, // Transformer  
Integer::max); // Accumulator  

对于int、long和double,reduce操作提供了专门的方法。以toXXX开头,需要将输入值转换为原始类型值,并指定一个默认值和累加器函数

long sum = map.reduceValuesToLong(threshold,  
Long::longValue, // Transformer to primitive type  
0, // Default value for empty map  
Long::sum); // Primitive type accumulator  

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

 

3. Set视图

java8没有提供concurrenHashSet类,但是可以通过concurrentHashMap类通过虚假值获得一个映射

静态方法newKeySet会返回一个Set对象,它实际上是对ConcurrentHashMap对象的封装。

Set words = ConcurrentHashMap.newKeySet();  

如果你已经有一个映射,keySet方法会返回所有键的Set,但是你不能向这个set中添加元素,因为无法向map添加相应的值

 

于是,一个接收默认值的keySet方法可以解决上述问题,通过这个默认值向set中添加元素

Set words = map.keySet(1L);  
words.add("Java");  

key=java, value = 1L

 

 

 

3.并行数组操作

Arrays提供许多并行化操作

parallelSort可以进行并行排序,并且可以指定范围

1 String contents = new String(Files.readAllBytes(  
2 Paths.get("alice.txt")), StandardCharsets.UTF_8); // Read file into string  
3 String[] words = contents.split("[\\P{L}]+"); // Split along nonletters  
4 Arrays.parallelSort(words);  

 

 1 values.parallelSort(values.length / 2, values.length); // 对上半部排序  

 

parallelSetAll方法会根据提供的计算函数对参数values的每一个值进行计算并更新

Arrays.parallelSetAll(values, i -> i % 10);  
// Fills values with 0 1 2 3 4 5 6 7 8 9 0 1 2 . . . 

parallelPrefix将数组中每个元素替换为指定关联操作前缀的积累

假设array [1, 2, 3, 4, ...],执行完Arrays.parallelPrefix(values, (x, y) -> x * y)之后,array的结果为

[1, 1 × 2, 1 × 2 × 3, 1 × 2 × 3 × 4, ...]

 

4.可完成的Future

在过去,Future获取结果的方法为get,并且调用后会一直阻塞等待get返回结果

CompletableFuture提供了“当结果可用时,再按照提供的方式处理”的功能

1 CompletableFuture contents = readPage(url);  
2 CompletableFuture> links = contents.thenApply(Parser::getLinks);  

thenApply方法不会被阻塞,它会返回另一个Future对象,当第一个Future对象完成时,它的结果会发给getLinks方法

 

 

Future流水线类似Steam流水线,经过一个或多个转换过程,最后由一个终止操作结束。

如下代码可以启动一个流水线

1 CompletableFuture contents  
2 = CompletableFuture.supplyAsync(() -> blockingReadPage(url));  

另外还有一个runAsync方法,接收Runnable参数,返回CompletableFuture

 

 

接下来可以调用thenApply或者thenApplyAsync方法,在同一个线程或者另一个线程中运行另一个操作。

最终这些步骤执行完毕,需要将结果保存在某个地方,需要一个终止操作,例如:

1 CompletableFuture links  
2 = CompletableFuture.supplyAsync(() -> blockingReadPage(url))  
3 .thenApply(Parser::getLinks)  
4 .thenAccept(System.out::println);  

thenAccept方法接收一个Consumer接口(返回类型为void),理想情况下不需要调用Future的get方法

 

以下是一些常用方法:

thenCompose方法做的事就是,假设同时有两个调用链,T->CompletableFuture和U->CompletableFuture在连续调用的情况下,合并为T->CompletableFuture

类似的常用方法如下:

Java8并发教程:Threads和Executors

原文地址  原文作者:Benjamin Winterberg 译者:张坤

欢迎阅读我的Java8并发教程的第一部分。这份指南将会以简单易懂的代码示例来教给你如何在Java8中进行并发编程。这是一系列教程中的第一部分。在接下来的15分钟,你将会学会如何通过线程,任务(tasks)和 exector services来并行执行代码。

  • 第一部分:Threads和Executors
  • 第二部分:同步和锁

并发在Java5中首次被引入并在后续的版本中不断得到增强。在这篇文章中介绍的大部分概念同样适用于以前的Java版本。不过我的代码示例聚焦于Java8,大量使用lambda表达式和其他新特性。如果你对lambda表达式不属性,我推荐你首先阅读我的Java 8 教程

Threads 和 Runnables

所有的现代操作系统都通过进程和线程来支持并发。进程是通常彼此独立运行的程序的实例,比如,如果你启动了一个Java程序,操作系统产生一个新的进程,与其他程序一起并行执行。在这些进程的内部,我们使用线程并发执行代码,因此,我们可以最大限度的利用CPU可用的核心(core)。

Java从JDK1.0开始执行线程。在开始一个新的线程之前,你必须指定由这个线程执行的代码,通常称为task。这可以通过实现Runnable——一个定义了一个无返回值无参数的run()方法的函数接口,如下面的代码所示:

Runnable task = () -> {
    String threadName = Thread.currentThread().getName();
    System.out.println("Hello " + threadName);
};

task.run();

Thread thread = new Thread(task);
thread.start();

System.out.println("Done!");

因为Runnable是一个函数接口,所以我们利用lambda表达式将当前的线程名打印到控制台。首先,在开始一个线程前我们在主线程中直接运行runnable。

控制台输出的结果可能像下面这样:

Hello main
Hello Thread-0
Done!

或者这样:

Hello main
Done!
Hello Thread-0

由于我们不能预测这个runnable是在打印’done’前执行还是在之后执行。顺序是不确定的,因此在大的程序中编写并发程序是一个复杂的任务。

我们可以将线程休眠确定的时间。在这篇文章接下来的代码示例中我们可以通过这种方法来模拟长时间运行的任务。

Runnable runnable = () -> {
    try {
        String name = Thread.currentThread().getName();
        System.out.println("Foo " + name);
        TimeUnit.SECONDS.sleep(1);
        System.out.println("Bar " + name);
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
};

Thread thread = new Thread(runnable);
thread.start();

当你运行上面的代码时,你会注意到在第一条打印语句和第二条打印语句之间存在一分钟的延迟。TimeUnit在处理单位时间时一个有用的枚举类。你可以通过调用Thread.sleep(1000)来达到同样的目的。

使用Thread类是很单调的且容易出错。由于并发API在2004年Java5发布的时候才被引入。这些API位于java.util.concurrent包下,包含很多处理并发编程的有用的类。自从这些并发API引入以来,在随后的新的Java版本发布过程中得到不断的增强,甚至Java8提供了新的类和方法来处理并发。

接下来,让我们走进并发API中最重要的一部——executor services。

Executors

并发API引入了ExecutorService作为一个在程序中直接使用Thread的高层次的替换方案。Executos支持运行异步任务,通常管理一个线程池,这样一来我们就不需要手动去创建新的线程。在不断地处理任务的过程中,线程池内部线程将会得到复用,因此,在我们可以使用一个executor service来运行和我们想在我们整个程序中执行的一样多的并发任务。

下面是使用executors的第一个代码示例:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);

});

// => Hello pool-1-thread-1

Executors类提供了便利的工厂方法来创建不同类型的 executor services。在这个示例中我们使用了一个单线程线程池的 executor。

代码运行的结果类似于上一个示例,但是当运行代码时,你会注意到一个很大的差别:Java进程从没有停止!Executors必须显式的停止-否则它们将持续监听新的任务。

ExecutorService提供了两个方法来达到这个目的——shutdwon()会等待正在执行的任务执行完而shutdownNow()会终止所有正在执行的任务并立即关闭execuotr。

这是我喜欢的通常关闭executors的方式:

try {
    System.out.println("attempt to shutdown executor");
    executor.shutdown();
    executor.awaitTermination(5, TimeUnit.SECONDS);
    }
catch (InterruptedException e) {
    System.err.println("tasks interrupted");
}
finally {
    if (!executor.isTerminated()) {
        System.err.println("cancel non-finished tasks");
    }
    executor.shutdownNow();
    System.out.println("shutdown finished");
}

executor通过等待指定的时间让当前执行的任务终止来“温柔的”关闭executor。在等待最长5分钟的时间后,execuote最终会通过中断所有的正在执行的任务关闭。

Callables 和 Futures

除了Runnable,executor还支持另一种类型的任务——Callable。Callables也是类似于runnables的函数接口,不同之处在于,Callable返回一个值。

下面的lambda表达式定义了一个callable:在休眠一分钟后返回一个整数。

Callable task = () -> {
    try {
        TimeUnit.SECONDS.sleep(1);
        return 123;
    }
    catch (InterruptedException e) {
        throw new IllegalStateException("task interrupted", e);
    }
};

Callbale也可以像runnbales一样提交给 executor services。但是callables的结果怎么办?因为submit()不会等待任务完成,executor service不能直接返回callable的结果。不过,executor 可以返回一个Future类型的结果,它可以用来在稍后某个时间取出实际的结果。

ExecutorService executor = Executors.newFixedThreadPool(1);
Future future = executor.submit(task);

System.out.println("future done? " + future.isDone());

Integer result = future.get();

System.out.println("future done? " + future.isDone());
System.out.print("result: " + result);

在将callable提交给exector之后,我们先通过调用isDone()来检查这个future是否已经完成执行。我十分确定这会发生什么,因为在返回那个整数之前callable会休眠一分钟、

在调用get()方法时,当前线程会阻塞等待,直到callable在返回实际的结果123之前执行完成。现在future执行完毕,我们可以在控制台看到如下的结果:

future done? false
future done? true
result: 123

Future与底层的executor service紧密的结合在一起。记住,如果你关闭executor,所有的未中止的future都会抛出异常。

executor.shutdownNow();
future.get();

你可能注意到我们这次创建executor的方式与上一个例子稍有不同。我们使用newFixedThreadPool(1)来创建一个单线程线程池的 execuot service。 这等同于使用newSingleThreadExecutor不过使用第二种方式我们可以稍后通过简单的传入一个比1大的值来增加线程池的大小。

Timeouts

任何future.get()调用都会阻塞,然后等待直到callable中止。在最糟糕的情况下,一个callable持续运行——因此使你的程序将没有响应。我们可以简单的传入一个时长来避免这种情况。

ExecutorService executor = Executors.newFixedThreadPool(1);

    Future future = executor.submit(() -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        return 123;
    }
    catch (InterruptedException e) {
        throw new IllegalStateException("task interrupted", e);
    }
});

    future.get(1, TimeUnit.SECONDS);

运行上面的代码将会产生一个TimeoutException

Exception in thread "main" java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask.get(FutureTask.java:205)

你可能已经猜到为什么会抛出这个异常。我们指定的最长等待时间为1分钟,而这个callable在返回结果之前实际需要两分钟。

invokeAll

Executors支持通过invokeAll()一次批量提交多个callable。这个方法结果一个callable的集合,然后返回一个future的列表。

ExecutorService executor = Executors.newWorkStealingPool();

List> callables = Arrays.asList(
        () -> "task1",
        () -> "task2",
        () -> "task3");

executor.invokeAll(callables)
    .stream()
    .map(future -> {
        try {
            return future.get();
        }
        catch (Exception e) {
            throw new IllegalStateException(e);
        }
    })
    .forEach(System.out::println);

在这个例子中,我们利用Java8中的函数流(stream)来处理invokeAll()调用返回的所有future。我们首先将每一个future映射到它的返回值,然后将每个值打印到控制台。如果你还不属性stream,可以阅读我的Java8 Stream 教程

invokeAny

批量提交callable的另一种方式就是invokeAny(),它的工作方式与invokeAll()稍有不同。在等待future对象的过程中,这个方法将会阻塞直到第一个callable中止然后返回这一个callable的结果。

为了测试这种行为,我们利用这个帮助方法来模拟不同执行时间的callable。这个方法返回一个callable,这个callable休眠指定 的时间直到返回给定的结果。

Callable callable(String result, long sleepSeconds) {
    return () -> {
        TimeUnit.SECONDS.sleep(sleepSeconds);
        return result;
    };
}

 

我们利用这个方法创建一组callable,这些callable拥有不同的执行时间,从1分钟到3分钟。通过invokeAny()将这些callable提交给一个executor,返回最快的callable的字符串结果-在这个例子中为任务2:

ExecutorService executor = Executors.newWorkStealingPool();

List> callables = Arrays.asList(
callable("task1", 2),
callable("task2", 1),
callable("task3", 3));

String result = executor.invokeAny(callables);
System.out.println(result);

// => task2

上面这个例子又使用了另一种方式来创建executor——调用newWorkStealingPool()。这个工厂方法是Java8引入的,返回一个ForkJoinPool类型的 executor,它的工作方法与其他常见的execuotr稍有不同。与使用一个固定大小的线程池不同,ForkJoinPools使用一个并行因子数来创建,默认值为主机CPU的可用核心数。

ForkJoinPools 在Java7时引入,将会在这个系列后面的教程中详细讲解。让我们深入了解一下 scheduled executors 来结束本次教程。

Scheduled Executors

我们已经学习了如何在一个 executor 中提交和运行一次任务。为了持续的多次执行常见的任务,我们可以利用调度线程池。

ScheduledExecutorService支持任务调度,持续执行或者延迟一段时间后执行。

下面的实例,调度一个任务在延迟3分钟后执行:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime());
ScheduledFuture future = executor.schedule(task, 3, TimeUnit.SECONDS);

TimeUnit.MILLISECONDS.sleep(1337);

long remainingDelay = future.getDelay(TimeUnit.MILLISECONDS);
System.out.printf("Remaining Delay: %sms", remainingDelay);

调度一个任务将会产生一个专门的future类型——ScheduleFuture,它除了提供了Future的所有方法之外,他还提供了getDelay()方法来获得剩余的延迟。在延迟消逝后,任务将会并发执行。

为了调度任务持续的执行,executors 提供了两个方法scheduleAtFixedRate()scheduleWithFixedDelay()。第一个方法用来以固定频率来执行一个任务,比如,下面这个示例中,每分钟一次:

ScheduledExecutorService executor =     Executors.newScheduledThreadPool(1);

Runnable task = () -> System.out.println("Scheduling: " + System.nanoTime());

int initialDelay = 0;
int period = 1;
executor.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS);

另外,这个方法还接收一个初始化延迟,用来指定这个任务首次被执行等待的时长。

请记住:scheduleAtFixedRate()并不考虑任务的实际用时。所以,如果你指定了一个period为1分钟而任务需要执行2分钟,那么线程池为了性能会更快的执行。

在这种情况下,你应该考虑使用scheduleWithFixedDelay()。这个方法的工作方式与上我们上面描述的类似。不同之处在于等待时间 period 的应用是在一次任务的结束和下一个任务的开始之间。例如:

ScheduledExecutorService executor =         Executors.newScheduledThreadPool(1);

Runnable task = () -> {
    try {
        TimeUnit.SECONDS.sleep(2);
        System.out.println("Scheduling: " + System.nanoTime());
    }
    catch (InterruptedException e) {
        System.err.println("task interrupted");
    }
};

executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS);

这个例子调度了一个任务,并在一次执行的结束和下一次执行的开始之间设置了一个1分钟的固定延迟。初始化延迟为0,任务执行时间为0。所以我们分别在0s,3s,6s,9s等间隔处结束一次执行。如你所见,scheduleWithFixedDelay()在你不能预测调度任务的执行时长时是很有用的。

这是并发系列教程的第以部分。我推荐你亲手实践一下上面的代码示例。你可以从 Github 上找到这篇文章中所有的代码示例,所以欢迎你fork这个repo,给我星星

MySQL5.7增量备份恢复全实战

一. 简介

1. 增量备份

  增量备份是指在一次全备份或上一次增量备份后,以后每次的备份只需备份与前一次相比增加或者被修改的文件。这就意味着,第一次增量
备份的对象是进行全备后所产生的增加和修改的文件;第二次增量备份的对象是进行第一次增量备份后所产生的增加和修改的文件,如此类推。
  这种备份方式最显著的优点就是:没有重复的备份数据,因此备份的数据量不大,备份所需的时间很短。但增量备份的数据恢复是比较麻烦的。
必须具有上一次全备份和所有增量备份(一旦丢失或损坏其中的一个增量,就会造成恢复的失败),并且它们必须沿着从全备份到依次增量备份
的时间顺序逐个恢复,因此这就极大地延长了恢复时间。
  假如我们有一个数据库,有20G的数据,每天会增加10M的数据,数据库每天都要全量备份一次,这样的话服务器的压力比较大,因此我们只
需要备份增加的这部分数据,这样减少服务器的负担。

2. binlog简介

  binlog日志由配置文件的log-bin参数来启用,MySQL服务器将在指定目录下创建两个文件XXX-bin.001和xxx-bin.index,若配置选项
没有给出文件名,Mysql将使用主机名称命名这两个文件,其中.index文件包含一份全体日志文件的清单。
  Mysql会把用户对所有数据库的内容和结构的修改情况记入XXX-bin.n文件,而不会记录 SELECT和没有实际更新的UPDATE语句。
  当MySQL数据库停止或重启时,服务器会把日志文件记入下一个日志文件,Mysql会在重启时生成一个新的binlog日志文件,文件序号递
增,此外,如果日志文件超过max_binlog_size系统变量配置的上限时,也会生成新的日志文件。

二 binlog操作总结

2.1 开启binlog日志

修改 MySQL 的配置文件my.cnf 如下:
[mysqld] 
log-bin=/MySQL/my3306/log/binlog/binlog  
binlog_format = row 

#其中 log_bin若不显示指定存储目录,则默认存储在mysql的datadir参数指定的目录下
#binlog_format的几种格式:(STATEMENT,ROW和MIXED):
STATEMENT:基于SQL语句的复制(statement-based replication, SBR)    
ROW:基于行的复制(row-based replication, RBR)    
MIXED:混合模式复制(mixed-based replication, MBR)

#启动后会产生mysql-bin.*这样的文件,每启动一次,就会增加一个或者多个.


[root@localhost binlog]# cd /MySQL/my3306/log/binlog
[root@localhost binlog]# ll
total 28
-rw-r-----. 1 mysql mysql 177 Jun  6 00:03 binlog.000001
-rw-r-----. 1 mysql mysql 177 Jun  6 00:03 binlog.000002
-rw-r-----. 1 mysql mysql 981 Jun  6 00:07 binlog.000003
-rw-r-----. 1 mysql mysql 177 Jun  6 00:07 binlog.000004
-rw-r-----. 1 mysql mysql 177 Jun  7 01:39 binlog.000005
-rw-r-----. 1 mysql mysql 154 Jun  7 02:47 binlog.000006
-rw-r-----. 1 mysql mysql 234 Jun  7 02:47 binlog.index

2.2查看binlog相关参数

mysql> show variables like 'log_bin%';
+---------------------------------+---------------------------------------+
| Variable_name                   | Value                                 |
+---------------------------------+---------------------------------------+
| log_bin                         | ON                                    |
| log_bin_basename                | /MySQL/my3306/log/binlog/binlog       |
| log_bin_index                   | /MySQL/my3306/log/binlog/binlog.index |
| log_bin_trust_function_creators | OFF                                   |
| log_bin_use_v1_row_events       | OFF                                   |
+---------------------------------+---------------------------------------+

可以看到MySQL5.7中,log_bin参数如果指定了目录和名称,则被拆分为三个参数:log_bin,log_bin_basename,log_bin_index
分别对应 binlog是否开启,binlog名.index名

2.3 查看binlog日志内容

[root@localhost binlog]# mysqlbinlog /MySQL/my3306/log/binlog/binlog.000001
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#180606  0:03:38 server id 101  end_log_pos 123 CRC32 0xe534323e        Start: binlog v 4, server v 5.7.22-log created 180606  0:03:38 at startup
ROLLBACK/*!*/;
BINLOG '
Gl0XWw9lAAAAdwAAAHsAAAAAAAQANS43LjIyLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAaXRdbEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA
AT4yNOU=
'/*!*/;
# at 123
#180606  0:03:38 server id 101  end_log_pos 154 CRC32 0x9c28789d        Previous-GTIDs
# [empty]
# at 154
#180606  0:03:39 server id 101  end_log_pos 177 CRC32 0xac9e5d49        Stop
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

2.4 一些常用操作

mysql> show master logs;  #查看数据库所有日志文件。 
mysql> show binlog events \g;  #查看当前使用的binlog文件信息。 
mysql> show binlog events in 'binlog.000001';  #查看指定的binlog文件信息。 
mysql> flush logs;  #将内存中log日志写磁盘,保存在当前binlog文件中,并产生一个新的binlog日志文件。 
mysql> flush logs; reset master;  #删除所有二进制日志,并重新(binlog.000001)开始记录。

三 MySQL备份实例(全备 + 基于 binlog的增备)

本环境基于CentOS7.2+MySQL5.7

3.1 查看当前数据库binlog文件

mysql> show master logs;
+---------------+-----------+
| Log_name      | File_size |
+---------------+-----------+
| binlog.000001 |       177 |
| binlog.000002 |       177 |
| binlog.000003 |       981 |
| binlog.000004 |       177 |
| binlog.000005 |       177 |
| binlog.000006 |       154 |
+---------------+-----------+


[root@localhost binlog]# cd /MySQL/my3306/log/binlog/
[root@localhost binlog]# ll -h
total 28K
-rw-r-----. 1 mysql mysql 177 Jun  6 00:03 binlog.000001
-rw-r-----. 1 mysql mysql 177 Jun  6 00:03 binlog.000002
-rw-r-----. 1 mysql mysql 981 Jun  6 00:07 binlog.000003
-rw-r-----. 1 mysql mysql 177 Jun  6 00:07 binlog.000004
-rw-r-----. 1 mysql mysql 177 Jun  7 01:39 binlog.000005
-rw-r-----. 1 mysql mysql 154 Jun  7 02:47 binlog.000006
-rw-r-----. 1 mysql mysql 234 Jun  7 02:47 binlog.index

3.2模拟数据

mysql> create database test_backup;  
mysql> use test_backup 
mysql> create table t_test (c1 int(10), c2 varchar(20)) engine=innodb; 
mysql>  insert into t_test values (1, 'test1'),(2, 'test2'),(3, 'test3'),(4, 'test4'), 
        (5, 'test5'),(6, 'test6'),(7, 'test7'),(8, 'test8'),(9, 'test9'),(10, 'test10'); 

3.3 将全量数据进行备份

[root@localhost binlog]# mysqldump -uroot -proot  --socket=/MySQL/my3306/run/mysql.sock --port=3306 --single-transaction --master-data=2 test_backup >/tmp/test_backup20180611.sql 
 
#记录备份的日志位置,将来作为增量还原的起点
[root@localhost binlog]# cat /tmp/test_backup20180611.sql|grep "CHANGE MASTER" 
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000006', MASTER_LOG_POS=996;

3.4 准备第一份增量数据

mysql> use test_backup; 
mysql> create table increment (c1 int(10), c2 varchar(20)) engine=innodb; 
mysql> insert into increment values (11, 'increment1'),(12, 'increment2'),(13, 'increment3'),(14, 'increment4'),(15, 'increment5'); 

3.5 将第一份增量数据进行备份

#将日志刷到当前的binlog文件中,也就是binlog.000006,数据库再有新的数据更新会记录在新的binlog(binlog.000007)里面.
mysql> flush logs;  

[root@localhost binlog]# ll
total 32
-rw-r-----. 1 mysql mysql  177 Jun  6 00:03 binlog.000001
-rw-r-----. 1 mysql mysql  177 Jun  6 00:03 binlog.000002
-rw-r-----. 1 mysql mysql  981 Jun  6 00:07 binlog.000003
-rw-r-----. 1 mysql mysql  177 Jun  6 00:07 binlog.000004
-rw-r-----. 1 mysql mysql  177 Jun  7 01:39 binlog.000005
-rw-r-----. 1 mysql mysql 1658 Jun 10 21:22 binlog.000006
-rw-r-----. 1 mysql mysql  154 Jun 10 21:22 binlog.000007
-rw-r-----. 1 mysql mysql  273 Jun 10 21:22 binlog.index

#拷贝binlog文件
[root@localhost binlog]# cp binlog.000006 /tmp/

3.6 准备第二份增量数据

mysql> use test_backup; 
mysql> insert into increment values (16, 'increment16'),(17, 'increment17'),(18, 'increment18'),(19, 'increment19'),(20, 'increment20');

3.7 将第二份增量数据进行备份

#将日志刷到当前的binlog文件中,也就是binlog.000007,数据库再有新的数据更新会记录在新的binlog(binlog.000008)里面.
mysql> flush logs;  

[root@localhost binlog]# ll
total 36
-rw-r-----. 1 mysql mysql  177 Jun  6 00:03 binlog.000001
-rw-r-----. 1 mysql mysql  177 Jun  6 00:03 binlog.000002
-rw-r-----. 1 mysql mysql  981 Jun  6 00:07 binlog.000003
-rw-r-----. 1 mysql mysql  177 Jun  6 00:07 binlog.000004
-rw-r-----. 1 mysql mysql  177 Jun  7 01:39 binlog.000005
-rw-r-----. 1 mysql mysql 1658 Jun 10 21:22 binlog.000006
-rw-r-----. 1 mysql mysql  603 Jun 10 22:04 binlog.000007
-rw-r-----. 1 mysql mysql  154 Jun 10 22:04 binlog.000008
-rw-r-----. 1 mysql mysql  312 Jun 10 22:04 binlog.index

#拷贝binlog文件
[root@localhost binlog]# cp binlog.000007 /tmp/

四. mysql还原实例分析(全备还原+基于binlog的增备还原)

#模拟数据库故障,即删除全备数据及增备数据库。
mysql> drop database test_backup;
Query OK, 2 rows affected (0.06 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

4.1 还原全备数据

mysql>create database test_backup;

[root@localhost binlog]# mysql -uroot   -proot  test_backup   use test_backup;
Database changed
mysql> show tables;
+-----------------------+
| Tables_in_test_backup |
+-----------------------+
| t_test                |
+-----------------------+

mysql> select * from t_test;
+------+--------+
| c1   | c2     |
+------+--------+
|    1 | test1  |
|    2 | test2  |
|    3 | test3  |
|    4 | test4  |
|    5 | test5  |
|    6 | test6  |
|    7 | test7  |
|    8 | test8  |
|    9 | test9  |
|   10 | test10 |
+------+--------+

4.2 还原第一个增备文件

从全备文件里的position值:LOG_FILE=’binlog.000006′, MASTER_LOG_POS=996 开始还原

[root@localhost tmp]# mysqlbinlog --start-position=996   binlog.000006 | mysql -uroot -proot

#查看数据:
mysql> select * from test_backup.increment; 
+------+------------+
| c1   | c2         |
+------+------------+
|   11 | increment1 |
|   12 | increment2 |
|   13 | increment3 |
|   14 | increment4 |
|   15 | increment5 |
+------+------------+

第一份增量数据还原成功!

4.3 还原第二个增备文件(方法同上)

[root]mysqlbinlog  mysqlbinlog    binlog.000007 | mysql -uroot -proot

查看数据:

mysql> select * from test_backup.increment;
+------+-------------+
| c1   | c2          |
+------+-------------+
|   11 | increment1  |
|   12 | increment2  |
|   13 | increment3  |
|   14 | increment4  |
|   15 | increment5  |
|   16 | increment16 |
|   17 | increment17 |
|   18 | increment18 |
|   19 | increment19 |
|   20 | increment20 |
+------+-------------+ 

全部数据还原成功!

转自:https://www.cnblogs.com/chinesern/p/9182962.html

mysql备份与还原

一、备份常用操作基本命令

1、备份命令mysqldump格式

   格式:mysqldump -h主机名  -P端口 -u用户名 -p密码 –database 数据库名 > 文件名.sql 

2、备份MySQL数据库为带删除表的格式

备份MySQL数据库为带删除表的格式,能够让该备份覆盖已有数据库而不需要手动删除原有数据库。

mysqldump  –add-drop-table -uusername -ppassword -database databasename > backupfile.sql

3、直接将MySQL数据库压缩备份

mysqldump -hhostname -uusername -ppassword -database databasename | gzip > backupfile.sql.gz

4、备份MySQL数据库某个(些)表

mysqldump -hhostname -uusername -ppassword databasename specific_table1 specific_table2 > backupfile.sql

5、同时备份多个MySQL数据库

mysqldump -hhostname -uusername -ppassword –databases databasename1 databasename2 databasename3 > multibackupfile.sql仅仅备6、仅备份份数据库结构

mysqldump –no-data –databases databasename1 databasename2 databasename3 > structurebackupfile.sql

7、备份服务器上所有数据库

mysqldump –all-databases > allbackupfile.sql

8、还原MySQL数据库的命令

mysql -hhostname -uusername -ppassword databasename < backupfile.sql

9、还原压缩的MySQL数据库

gunzip < backupfile.sql.gz | mysql -uusername -ppassword databasename

10、将数据库转移到新服务器

mysqldump -uusername -ppassword databasename | mysql –host=*.*.*.* -C databasename

11、–master-data 和–single-transaction

   在mysqldump中使用–master-data=2,会记录binlog文件和position的信息 。–single-transaction会将隔离级别设置成repeatable-commited

12、导入数据库

常用source命令,用use进入到某个数据库,mysql>source d:\test.sql,后面的参数为脚本文件。

13、查看binlog日志

查看binlog日志可用用命令 mysqlbinlog  binlog日志名称|more

 

14、general_log

General_log记录数据库的任何操作,查看general_log 的状态和位置可以用命令show variables like “general_log%”  ,开启general_log可以用命令set global general_log=on

二、增量备份

小量的数据库可以每天进行完整备份,因为这也用不了多少时间,但当数据库很大时,就不太可能每天进行一次完整备份了,这时候就可以使用增量备份。增量备份的原理就是使用了mysql的binlog志。

 

1、首先做一次完整备份:

mysqldump -h10.6.208.183 -utest2 -p123  -P3310 –single-transaction  –master-data=2  test>test.sql这时候就会得到一个全备文件test.sql

在sql文件中我们会看到:
— CHANGE MASTER TO MASTER_LOG_FILE=’bin-log.000002′, MASTER_LOG_POS=107;是指备份后所有的更改将会保存到bin-log.000002二进制文件中。
2、在test库的t_student表中增加两条记录,然后执行flush logs命令。这时将会产生一个新的二进制日志文件bin-log.000003,bin-log.000002则保存了全备过后的所有更改,既增加记录的操作也保存在了bin-log.00002中。

3、再在test库中的a表中增加两条记录,然后误删除t_student表和a表。a中增加记录的操作和删除表a和t_student的操作都记录在bin-log.000003中。

 

三、恢复

1、首先导入全备数据

mysql -h10.6.208.183 -utest2 -p123  -P3310 < test.sql,也可以直接在mysql命令行下面用source导入

2、恢复bin-log.000002

   mysqlbinlog bin-log.000002 |mysql -h10.6.208.183 -utest2 -p123  -P3310  

3、恢复部分 bin-log.000003

   在general_log中找到误删除的时间点,然后更加对应的时间点到bin-log.000003中找到相应的position点,需要恢复到误删除的前面一个position点。

可以用如下参数来控制binlog的区间

–start-position 开始点 –stop-position 结束点

–start-date 开始时间  –stop-date  结束时间

找到恢复点后,既可以开始恢复。

  mysqlbinlog mysql-bin.000003 –stop-position=208 |mysql -h10.6.208.183 -utest2 -p123  -P3310

转自:https://www.cnblogs.com/adolfmc/p/9822389.html