十大最值得注意的MySQL变量

MySQL变量很多,其中有一些MySQL变量非常值得我们注意,下面就为您介绍一些值得我们重点学习的MySQL变量,供您参考。

1、Threads_connected

首先需要注意的,想得到这个变量的值不能show variables like ‘Threads_connected’;而是
show status like ‘Threads_connected'(下面的变值也是这样的);
意思:变量的值是表示当前有多少个客户连接该mysql服务器
引申:连接数是否过多,网络时候存在问题!特别是在pconnect的情况下:)

2、Created_tmp_disk_tables

意思:在硬盘上建立的临时表数目
引申:如果这个值比较大的话,那么查询时需要建立临时表(CREATE TEMPORARY TABLE)的操作 就要消耗更多的时间

3、Handler_read_first

意思:读表索引的第一行
引申:如果这个值变化比较大的话,可以认为表索引建立的有问题,全索引的扫描操作比较多

4、Innodb_buffer_pool_wait_free

意思:This variable indicates the number of times MySQL has to wait for memory pages to be flus
引申:If this variable is high, it suggests that MySQL’s memory buffer is incorrectly configured for the amount of writes the server is currently performing.
不了解这个:)

5、Key_reads

意思:读文件系统上面的索引的次数
引申:如果这个值太大的话,就需要考虑key cache设置是否正常了

6、Max_used_connections

意思:重起后到现在最大连接数
引申:服务器负载和可能需要调节的连接数

7、Open_tables

意思:当前打开的表的数目
引申:如果这个值很低,table cache很大,则减小table cache的设置是没有问题的,如果这个值很大,并接近了table cache的值,我们就需要加大talbe cache的设置

8、Select_full_join

意思:全连接的查询数目
引申:数值过大,需要建立更多的索引来避免

9、Slow_queries

意思:慢查询的数目
引申:过大的话就要察看慢查询的日志,并且检查sql语句书写是否恰当

10、Uptime

意思:运行时间,单位秒

MySQL DELETE 删除语句加锁分析

一. 前言

在MySQL的使用过程中,对SQL加锁的类型经常感到疑惑,这让死锁分析也变得举步维艰。因此需要将MySQL的各种SQL在各个隔离级别下加的锁进行分析,以免再次分析的时候还感到疑惑,也方便用于查询。

本次分析对SQL的删除语句进行分析,主要从以下几种情况进行分析:

  1. 非唯一索引删除一条存在的记录
  2. 唯一索引删除一条存在的记录
  3. 主键删除一条存在的记录
  4. 非唯一索引删除一条不存在记录
  5. 唯一索引删除一条不存在的记录
  6. 主键删除一条不存在的记录
  7. 不同的SQL根据主键删除2条记录
  8. 非唯一索引删除一条已经标记删除的记录
  9. 唯一索引删除一条已经标记删除的记录

在使用之前需要打开innodb lock monitor,这样在查看 engine innodb status 的时候可以更加清晰的查到到锁的情况

set GLOBAL innodb_status_output_locks=ON;

二. SQL的加锁分析

相关表结构

  • 普通索引表结构
CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c1` int(11) NOT NULL DEFAULT '0',
  `c2` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_c1` (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4;
  • 唯一索引表结构
CREATE TABLE `tu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c1` int(11) NOT NULL DEFAULT '0',
  `c2` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_c1` (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4
  • 表的记录,唯一索引和普通索引的表结构均一样
  • 测试的事务隔离级别为RR。
+----+----+----+
| id | c1 | c2 |
+----+----+----+
|  2 |  3 |  2 |
|  3 |  5 |  3 |
|  4 |  8 |  4 |
|  5 | 11 |  5 |
|  9 |  9 | 20 |
| 10 |  7 | 10 |
| 11 | 20 | 15 |
| 12 | 30 | 17 |
| 13 | 25 | 16 |
| 14 | 27 | 10 |
+----+----+----+

2.1 删除SQL加锁分析

根据非唯一索引删除一条存在记录

delete from t where c1=5;
Query OK, 1 rows affected (0.00 sec)


---TRANSACTION 146749, ACTIVE 9 sec
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 104 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146749 lock mode IX
RECORD LOCKS space id 53 page no 5 n bits 72 index `idx_c1` of table `test`.`t` trx id 146749 lock_mode X
RECORD LOCKS space id 53 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 146749 lock_mode X locks rec but not gap
RECORD LOCKS space id 53 page no 5 n bits 72 index `idx_c1` of table `test`.`t` trx id 146749 lock_mode X locks gap before rec

根据非唯一索引进行删除的时候,锁情况为:

4 lock struct(s):4种锁结构,分别为IX,idx_c1和主键的行锁,还有idx_c1的gap锁
3 row lock(s):有3个行锁,除去IX的都是算在row lock里面

根据唯一索引删除一条存在记录

delete from tu where c1=5;
Query OK, 1 rows affected (0.00 sec)


---TRANSACTION 146751, ACTIVE 2 sec
3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 134 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146751 lock mode IX
RECORD LOCKS space id 45 page no 5 n bits 72 index `uniq_c1` of table `test`.`tu` trx id 146751 lock_mode X locks rec but not gap
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146751 lock_mode X locks rec but not gap

根据唯一索引进行删除的时候,锁情况为:

3 lock struct(s):3种锁结构,分别为IX,idx_c1和主键的行锁,没有gap锁
2 row lock(s):有2个行锁,除去IX的都是算在row lock里面,没有gap,因此为2个

根据主键删除一条存在记录

delete from tu where id=2;
Query OK, 1 rows affected (0.00 sec)


---TRANSACTION 146753, ACTIVE 2 sec
2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 147 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146753 lock mode IX
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146753 lock_mode X locks rec but not gap

根据主键进行删除的时候,锁情况为:

2 lock struct(s):2种锁结构,分别为IX和主键的行锁,没有gap锁
1 row lock(s):有1个行锁,就主键记录上的行锁,没有gap,因此为1个

根据非唯一索引删除一条不存在 记录

delete from t where c1 = 4;
Query OK, 0 rows affected (0.00 sec)


---TRANSACTION 146786, ACTIVE 1 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 671 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146786 lock mode IX
RECORD LOCKS space id 53 page no 5 n bits 80 index `idx_c1` of table `test`.`t` trx id 146786 lock_mode X locks gap before rec

根据非唯一索引删除一条 不存在 记录,锁情况为:

2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
1 row lock(s):有1个行锁,为非唯一索引的gap锁

根据唯一索引删除一条不存在 记录

delete from tu where c1 = 4;
Query OK, 0 rows affected (0.00 sec)


---TRANSACTION 146787, ACTIVE 2 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 711 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146787 lock mode IX
RECORD LOCKS space id 45 page no 5 n bits 72 index `uniq_c1` of table `test`.`tu` trx id 146787 lock_mode X locks gap before rec

根据唯一索引删除一条 不存在 记录,发现和非唯一索引一样,锁情况为:

2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
1 row lock(s):有1个行锁,为唯一索引的gap锁

根据主键删除一条不存在 记录

delete from tu where id = 6;
Query OK, 0 rows affected (0.00 sec)


---TRANSACTION 146831, ACTIVE 24 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 881 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146831 lock mode IX
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146831 lock_mode X locks gap before rec

根据主键删除一条 不存在 记录,发现和非唯一索引一样,锁情况为:

2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
1 row lock(s):有1个行锁,为主键上的gap锁

根据主键删除两条存在的记录

有 5 , 10 这两条记录

delete from tu where id>=5 and id<10;
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146900, ACTIVE 35 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 995 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146900 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146900 lock_mode X locks rec but not gap
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146900 lock_mode X
有 5 , 9 这两条记录
delete from tu where id>=5 and id<=9;
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146912, ACTIVE 12 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1022 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146912 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146912 lock_mode X locks rec but not gap
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146912 lock_mode X
有 4 ,10 这两条记录

delete from tu where id>4 and id<10;
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146906, ACTIVE 13 sec
2 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1011 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146906 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146906 lock_mode X
有 10 没 7 

delete from tu where id>=7 and id<=10;
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146966, ACTIVE 2 sec
2 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1172 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146966 lock mode IX
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146966 lock_mode X
有 4没 8 

delete from tu where id>=4 and id<=8;
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146972, ACTIVE 20 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1201 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146972 lock mode IX
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146972 lock_mode X locks rec but not gap
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146972 lock_mode X
有3,4两条记录

delete from tu where id in (3,4);
Query OK, 2 rows affected (0.00 sec)

---TRANSACTION 146880, ACTIVE 1 sec
2 lock struct(s), heap size 360, 2 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 928 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146880 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146880 lock_mode X locks rec but not gap

根据主键删除两条的时候,使用in的锁情况为:

2 lock struct(s):2种锁结构,分别为IX和i主键的行锁,没有gap锁
2 row lock(s):有2个行锁,就主键记录上的行锁,没有gap,因此为2个

根据主键删除两条的时候,使用>,<,>=,<=,比较符号的锁情况为:

  1. 无论如何,匹配到2条记录,因此必须会有2 row lock(s)
  2. 如果只有>,<,那么毫无疑问,是不会锁定两个边界的记录,因此他只会锁定边界到边界内的整个范围,锁的类型为X,此时为2 lock struct(s) ,3 row lock(s)
  3. 碰到 >= 的时候,判断 >= 的值是否存在,如果存在,则锁定该记录。所以除了IX,X锁,还有行锁,因此存在的时候为3 lock struct(s), 3 row lock(s)。如果不存在,和第二种是一样的,为2 lock struct(s) ,3 row lock(s) 。

非唯一索引删除一条已经标记删除的记录

Sess1                    Sess2                    Sess3
begin;
delete from t where c1=8;       
                         begin; 
                         delete from t where c1=8;  
                                                  @1 show engine innodb status
commit;     
                                                  @2 show engine innodb status
@1 show engine innodb status


---TRANSACTION 146981, ACTIVE 12 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2804 localhost msandbox updating
delete from t where c1=8
------- TRX HAS BEEN WAITING 12 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X waiting
------------------
TABLE LOCK table `test`.`t` trx id 146981 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X waiting
---TRANSACTION 146980, ACTIVE 16 sec
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 355, OS thread handle 0x7f61ab145700, query id 2802 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146980 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146980 lock_mode X
RECORD LOCKS space id 54 page no 3 n bits 80 index `PRIMARY` of table `test`.`t` trx id 146980 lock_mode X locks rec but not gap
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146980 lock_mode X locks gap before rec

@2 show engine innodb status

---TRANSACTION 146981, ACTIVE 50 sec
3 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2804 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146981 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X locks gap before rec

非唯一索引删除一条已经标记删除的记录的锁情况为:

  • 加锁等待时: 2 lock struct(s) ,持有IX锁,等待记录上的X锁
  • 加锁成功时:3 lock struct(s),持有IX,行锁,和gap锁,这个和非唯一索引删除一条不存在的记录是基本一样的,多了个因Sess1 提交成功后多获得的行锁。

唯一索引删除一条已经标记删除的记录

Sess1                     Sess2                     Sess3
begin;      
delete from tu where c1=8;      
                          begin;    
                          delete from tu where c1=8;    
                                                    @1 show engine innodb status
commit;     
                                                    @2 show engine innodb status
@1 show engine innodb status

---TRANSACTION 146984, ACTIVE 2 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2842 localhost msandbox updating
delete from tu where c1=8
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X waiting
------------------
TABLE LOCK table `test`.`tu` trx id 146984 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X waiting
---TRANSACTION 146983, ACTIVE 9 sec
3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 355, OS thread handle 0x7f61ab145700, query id 2839 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146983 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146983 lock_mode X locks rec but not gap
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146983 lock_mode X locks rec but not gap

@2 show engine innodb status

---TRANSACTION 146984, ACTIVE 23 sec
3 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2842 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146984 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X locks gap before rec

唯一索引删除一条已经标记删除的记录的锁情况为:

  • 加锁等待时: 2 lock struct(s) ,持有IX锁,等待记录上的X锁

  • 加锁成功时:3 lock struct(s),持有IX,行锁,和gap锁,和非唯一索引删除一条标记为已删除的记录的情况一模一样。

三. 总结

  1. 在非唯一索引的情况下,删除一条存在的记录是有gap锁,锁住记录本身和记录之前的gap
  2. 在唯一索引和主键的情况下删除一条存在的记录,因为都是唯一值,进行删除的时候,是不会有gap存在
  3. 非唯一索引,唯一索引和主键在删除一条不存在的记录,均会在这个区间加gap锁
  4. 通过非唯一索引和唯一索引去删除一条标记为删除的记录的时候,都会请求该记录的行锁,同时锁住记录之前的gap
  5. RC 情况下是没有gap锁的,除了遇到唯一键冲突的情况,如插入唯一键冲突。

MySQL binlog基本用法

本文只是简单的介绍mysql binlog基本用法,并不涉及到binlog的原理、格式等知识,如果需要了解这些高级的知识,请参见官方文档。

本文重点介绍–start-position和–stop-position参数的使用
–start-position的语法是

--start-position=N 

含义是从相对与二进制日志的第N偏移的事件开始读。 同理,–stop-position=N的介绍和–start-position类似。在默认的情况下, log-bin是关闭的,如下:

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

我们可以通过修改my.ini配置文件,在[mysqld] 下面添加 log-bin=日志名:

[mysqld]# The TCP/IP Port the MySQL Server will listen onport=3306log-bin=mysql-bin

修改完成之后,我们需要重启mysql服务,然后再看下是否启动了binlog

mysql> show variables like 'log_bin';+---------------+-------+| Variable_name | Value |+---------------+-------+| log_bin | ON |+---------------+-------+1 row in set (1.01 sec)

已经开启了binlog。然后我们创建一个数据库binlog

mysql> create database binlog;Query OK, 1 row affected (0.00 sec)mysql> use binlog;Database changed

然后在binlog数据库下面创建表test,并依次进行如下操作。

mysql> create table test( id int auto_increment not null primary key, val int, data varchar(20));Query OK, 0 rows affected (0.01 sec)mysql> insert into test(val, data) values (10, 'wu');Query OK, 1 row affected (0.02 sec)mysql> insert into test(val, data) values (20, 'yang');Query OK, 1 row affected (0.01 sec)mysql> insert into test(val, data) values (20, 'ping');Query OK, 1 row affected (0.01 sec)mysql> flush logs;Query OK, 0 rows affected (0.04 sec)mysql> insert into test(val, data) values (40, 'hao');Query OK, 1 row affected (0.01 sec)mysql> insert into test(val, data) values (50, 'iteblog');Query OK, 1 row affected (0.01 sec)mysql> delete from test where id between 4 and 5;Query OK, 2 rows affected (0.01 sec)mysql> insert into test(val, data) values (60, 'iteblog1');Query OK, 1 row affected (0.02 sec)mysql> flush logs;Query OK, 0 rows affected (0.05 sec)mysql> insert into test(val, data) values (70, 'ping123');Query OK, 1 row affected (0.01 sec)mysql> insert into test(val, data) values (80, 'ping163');Query OK, 1 row affected (0.01 sec)mysql> drop table test;Query OK, 0 rows affected (0.01 sec)mysql> drop database binlog;Query OK, 0 rows affected (0.00 sec)

经过上述的操作,将会在本地数据库数据存放目录下面生成以下四个文件:

mysql-bin.000001mysql-bin.000002mysql-bin.000003mysql-bin.index

*.index是索引文件,其他三个是binlog文件,我们可以用mysqlbinlog 工具来恢复数据。为了下面讲解的方便,我们先将binlog文件解析成txt文件,如下:

mysqlbinlog datamysql-bin.000001 > E:/1.txtmysqlbinlog datamysql-bin.000002 > E:/2.txtmysqlbinlog datamysql-bin.000003 > E:/3.txt

通过这三个命令,可以在E盘下生成3个文件,里面分别记录了日志文件的内容,也就是用户操作的步骤。

下面开始恢复binlog日志到Mysql数据库,因为我们需要重做第一个日志文件的所有操作,所以这里只需要将第一个日志文件全恢复就行了。

mysqlbinlog datamysql-bin.000001 | mysql -uroot -p123456

在第二个binlog里面我们进行了delete操作,我们并不想将delete的操作恢复到数据库,这样我们可以通过读取2.txt文件:

................................/*!*/;# at 653#140902 16:07:43 server id 1 end_log_pos 759 Query thread_id=1 exec_time=0 error_code=0SET TIMESTAMP=1409645263/*!*/;delete from test where id between 4 and 5/*!*/;# at 759#140902 16:07:43 server id 1 end_log_pos 786 Xid = 175COMMIT/*!*/;................................

在这个文件中,我们可以看到DELETE的操作的起始位置是653,终止位置是759.那么我们只要重做第二个日志文件的开头到653的操作,然后再从759到末尾的操作,我们就可以把数据给恢复回来,而不会DELETE数据。所以执行两个命令

mysqlbinlog datamysql-bin.000002 --stop-pos=653 | mysql -uroot -p123456mysqlbinlog datamysql-bin.000002 --start-pos=759 | mysql -uroot -p123456mysqlbinlog datamysql-bin.000003 --stop-pos=587 | mysql -uroot -p123456

好了,到这里,所有的数据全部恢复了,我们可以用下面语句查看到:

mysql> select * from test+----+------+----------+| id | val | data |+----+------+----------+| 1 | 10 | wu || 2 | 20 | yang || 3 | 20 | ping || 4 | 40 | hao || 5 | 50 | iteblog || 6 | 60 | iteblog1 || 7 | 70 | ping123 || 8 | 80 | ping163 |+----+------+----------+8 rows in set (0.00 sec)

mongodb 常用操作

连接mongodb

mongo

默认连接到本地端口27017

mongo –host 123.57.244.111 –port 27017 

连接远程数据库

插入数据

插入一条数据:

db.test.insert({title: “1111111111111111111”})

循环插入数据:

for (var i = 0; i < 100; i++) { 
db.test.insert({title: i * 1000}) 
}

插入数组:

db.test.insert([{title: “aaaaaaaaaaaaa”}, {title: “bbbbbbbbbbbb”}])

聚合

插入一个0到100的随机年龄

for (var i = 0; i < 100; i++) { 
db.test.insert({age: Math.round(Math.random() * 100)}); 
}

统计同一个年龄出现的次数

db.test.aggregate([{group: {_id: "age", count: {$sum: 1}}}])

查询

查询所有数据

db.test.find()

查询大于90000的

db.test.find({title: {$gt: 90000}})

查询大于90000小于93000

db.test.find({title: {gt:90000,lt: 93000}})

查询一条数据

db.test.findOne()

查询存在字段name的

db.test.find({name: {$exists: true}})

查询操作符

  • eq:等于gt:大于
  • gte:大于等于in:匹配数组中指定的任何值
  • lt:大于lte:小于等于
  • ne:匹配不等于指定值的所有值nin:不匹配数组中指定的值
  • size:数量为n的数组exists:查询存在某个字段
  • $type:检索集合中匹配的数据类型

删除记录

删除title大于等于2000的

db.test.remove({title: {$gte: 2000}})

删除所有记录

db.test.remove({})

limit方法

该参数指定从MongoDB中读取的记录条数

读取两条记录

db.test.find().limit(2)

skip方法

跳过指定数量的数据

读取第三条数据

db.test.find().skip(2)

sort方法

对数据进行排序,可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,而-1是用于降序排列

根据title升序排序

db.test.find().sort({title: 1})

distinct方法

返回指定字段的非重复值组成的数组

db.test.distinct(“title”)

查询创建的索引

db.test.getIndexes() 

结果:

[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "nodeblog.test"
        },
        {
                "v" : 2,
                "key" : {
                        "title" : 1,
                        "count" : 1
                },
                "name" : "title_1_count_1",
                "ns" : "nodeblog.test"
        }
]

删除索引

通过上面查到的name,如:title_1_count_1

db.test.dropIndex(“title_1_count_1”)

创建索引

db.test.createIndex({title: 1}) 

1:升序, -1:降序

创建联合索引:db.test.createIndex({title: 1, count: 1})

查看命令执行时间

在命令后面增加:explain(“executionStats”)
如:db.test.find({title: “hello99999”}).explain(“executionStats”)
结果中的executionTimeMillis就是花费的时间

删除文档

如:删除所有count大于10的文档
db.test.remove({count: {$gt: 10}})

主从复制

下面两个路径必须先创建

1>开启主服务:mongod –master –dbpath e://MongoDB/data/master –port 8888
2>开启从服务:mongod –slave –dbpath e://MongoDB//data//slave –sources 127.0.0.1:8888 –port 8881

可以看到每秒没主服务那边同步数据的日志
[replslave] syncing from host:127.0.0.1:8888

在master中插入一些数据,直接读取slave服务中的数据是不行的。所以关掉master,slave服务,然后按正常方式启动slave
服务,如:mongod –dbpath E://MongoDB//data//slave –port 8881

连接到slave,如:mongo –port 8881,查看之前从master同步过来的数据,如:db.test.find()

监控状态,每秒刷新一次

mongostat –port 27017

查看数据库

show dbs

使用数据库

use users 

只有执行了use才能查找表里的内容

查看当前数据库中的集合(表)

show collections

导出数据库

mongodump -d nodeblog -o /root/db/bk20170908 

-d:导出的数据库名
-o:导出的目录

恢复数据库

mongorestore -d nodeblog –drop e://MongoDB//data//nodeblog 

–drop:删除原有数据库内容

使用 Docker 和 Kubernetes 将 MongoDB 作

介绍

想在笔记本电脑上尝试 MongoDB?只需执行一个命令,你就会有一个轻量级的、独立的沙箱。完成后可以删除你所做的所有痕迹。

想在多个环境中使用相同的程序栈application stack副本?构建你自己的容器镜像,让你的开发、测试、运维和支持团队使用相同的环境克隆。

容器正在彻底改变整个软件生命周期:从最早的技术性实验和概念证明,贯穿了开发、测试、部署和支持。

编排工具用来管理如何创建、升级多个容器,并使之高可用。编排还控制容器如何连接,以从多个微服务容器构建复杂的应用程序。

丰富的功能、简单的工具和强大的 API 使容器和编排功能成为 DevOps 团队的首选,将其集成到连续集成(CI) 和连续交付 (CD) 的工作流程中。

这篇文章探讨了在容器中运行和编排 MongoDB 时遇到的额外挑战,并说明了如何克服这些挑战。

MongoDB 的注意事项

使用容器和编排运行 MongoDB 有一些额外的注意事项:

  • MongoDB 数据库节点是有状态的。如果容器发生故障并被重新编排,数据则会丢失(能够从副本集的其他节点恢复,但这需要时间),这是不合需要的。为了解决这个问题,可以使用诸如 Kubernetes 中的 数据卷volume 抽象等功能来将容器中临时的 MongoDB 数据目录映射到持久位置,以便数据在容器故障和重新编排过程中存留。

  • 一个副本集中的 MongoDB 数据库节点必须能够相互通信 – 包括重新编排后。副本集中的所有节点必须知道其所有对等节点的地址,但是当重新编排容器时,可能会使用不同的 IP 地址重新启动。例如,Kubernetes Pod 中的所有容器共享一个 IP 地址,当重新编排 pod 时,IP 地址会发生变化。使用 Kubernetes,可以通过将 Kubernetes 服务与每个 MongoDB 节点相关联来处理,该节点使用 Kubernetes DNS 服务提供“主机名”,以保持服务在重新编排中保持不变。

  • 一旦每个单独的 MongoDB 节点运行起来(每个都在自己的容器中),则必须初始化副本集,并添加每个节点到其中。这可能需要在编排工具之外提供一些额外的处理。具体来说,必须使用目标副本集中的一个 MongoDB 节点来执行 rs.initiate 和 rs.add 命令。

  • 如果编排框架提供了容器的自动化重新编排(如 Kubernetes),那么这将增加 MongoDB 的弹性,因为这可以自动重新创建失败的副本集成员,从而在没有人为干预的情况下恢复完全的冗余级别。

  • 应该注意的是,虽然编排框架可能监控容器的状态,但是不太可能监视容器内运行的应用程序或备份其数据。这意味着使用 MongoDB Enterprise Advanced [1] 和 MongoDB Professional [2] 中包含的 MongoDB Cloud Manager [3] 等强大的监控和备份解决方案非常重要。可以考虑创建自己的镜像,其中包含你首选的 MongoDB 版本和 MongoDB Automation Agent [4]。

使用 Docker 和 Kubernetes 实现 MongoDB 副本集

如上节所述,分布式数据库(如 MongoDB)在使用编排框架(如 Kubernetes)进行部署时,需要稍加注意。本节将介绍详细介绍如何实现。

我们首先在单个 Kubernetes 集群中创建整个 MongoDB 副本集(通常在一个数据中心内,这显然不能提供地理冗余)。实际上,很少有必要改变成跨多个集群运行,这些步骤将在后面描述。

副本集的每个成员将作为自己的 pod 运行,并提供一个公开 IP 地址和端口的服务。这个“固定”的 IP 地址非常重要,因为外部应用程序和其他副本集成员都可以依赖于它在重新编排 pod 的情况下保持不变。

下图说明了其中一个 pod 以及相关的复制控制器和服务。

未分类

逐步介绍该配置中描述的资源:

  • 从核心开始,有一个名为 mongo-node1 的容器。 mongo-node1 包含一个名为 mongo 的镜像,这是一个在 Docker Hub [5] 上托管的一个公开可用的 MongoDB 容器镜像。容器在集群中暴露端口 27107。

  • Kubernetes 的数据卷功能用于将连接器中的 /data/db 目录映射到名为 mongo-persistent-storage1 的永久存储上,这又被映射到在 Google Cloud 中创建的名为 mongodb-disk1 的磁盘中。这是 MongoDB 存储其数据的地方,这样它可以在容器重新编排后保留。

  • 容器保存在一个 pod 中,该 pod 中有标签命名为 mongo-node,并提供一个名为 rod 的(任意)示例。

  • 配置 mongo-node1 复制控制器以确保 mongo-node1 pod 的单个实例始终运行。

  • 名为 mongo-svc-a 的 负载均衡 服务给外部开放了一个 IP 地址以及 27017 端口,它被映射到容器相同的端口号上。该服务使用选择器来匹配 pod 标签来确定正确的 pod。外部 IP 地址和端口将用于应用程序以及副本集成员之间的通信。每个容器也有本地 IP 地址,但是当容器移动或重新启动时,这些 IP 地址会变化,因此不会用于副本集。

下一个图显示了副本集的第二个成员的配置。

未分类

90% 的配置是一样的,只有这些变化:

  • 磁盘和卷名必须是唯一的,因此使用的是 mongodb-disk2 和 mongo-persistent-storage2

  • Pod 被分配了一个 instance: jane 和 name: mongo-node2 的标签,以便新的服务可以使用选择器与图 1 所示的 rod Pod 相区分。

  • 复制控制器命名为 mongo-rc2

  • 该服务名为 mongo-svc-b,并获得了一个唯一的外部 IP 地址(在这种情况下,Kubernetes 分配了 104.1.4.5)

第三个副本成员的配置遵循相同的模式,下图展示了完整的副本集:

未分类

请注意,即使在三个或更多节点的 Kubernetes 群集上运行图 3 所示的配置,Kubernetes 可能(并且经常会)在同一主机上编排两个或多个 MongoDB 副本集成员。这是因为 Kubernetes 将三个 pod 视为属于三个独立的服务。

为了在区域内增加冗余,可以创建一个附加的 headless 服务。新服务不向外界提供任何功能(甚至不会有 IP 地址),但是它可以让 Kubernetes 通知三个 MongoDB pod 形成一个服务,所以 Kubernetes 会尝试在不同的节点上编排它们。

未分类

配置和启动 MongoDB 副本集所需的实际配置文件和命令可以在白皮书《启用微服务:阐述容器和编排[6]》中找到。特别的是,需要一些本文中描述的特殊步骤来将三个 MongoDB 实例组合成具备功能的、健壮的副本集。

多个可用区 MongoDB 副本集

上面创建的副本集存在风险,因为所有内容都在相同的 GCE 集群中运行,因此都在相同的可用区availability zone中。如果有一个重大事件使可用区离线,那么 MongoDB 副本集将不可用。如果需要地理冗余,则三个 pod 应该在三个不同的可用区或地区中运行。

令人惊奇的是,为了创建在三个区域之间分割的类似的副本集(需要三个集群),几乎不需要改变。每个集群都需要自己的 Kubernetes YAML 文件,该文件仅为该副本集中的一个成员定义了 pod、复制控制器和服务。那么为每个区域创建一个集群,永久存储和 MongoDB 节点是一件很简单的事情。

未分类

下一步

要了解有关容器和编排的更多信息 – 所涉及的技术和所提供的业务优势 – 请阅读白皮书《启用微服务:阐述容器和编排[7]》。该文件提供了获取本文中描述的副本集,并在 Google Container Engine 中的 Docker 和 Kubernetes 上运行的完整的说明。

mongodb 3.4 集群搭建升级版 五台集群

最新版mongodb推荐使用yaml语法来做配置,另外一些旧的配置在最新版本中已经不在生效,所以我们在生产实际搭建mongodb集群的时候做了一些改进。

和前一个版本相比,改动点有:

  • 配置文件采用yaml方式来配置
  • 生产中取消了仲裁者的角色,因为仲裁者也不会存储数据,只是起到选举的作用,线上为了保证数据安全,每份数据都会配置两个副本集,也就是每份数据存储了三份。
  • 优化配置,采用五台集群
  • 使用非root账户搭建mongodb集群。

一、环境准备

  • 系统系统 centos6.9
  • 五台服务器:192.168.0.31/32/33/34/35
  • 安装包: mongodb-linux-x86_64-3.4.6.tgz

服务器规划

未分类

端口分配:

mongos:20000
config:21000
shard1:27001
shard2:27002
shard3:27003
shard4:27004
shard5:27005

权限分配:

登录root账户,将安装目录和数据目录权限分配给日常操作(youknow)账户

chown -R youknow:youknow /usr/local/
chown -R youknow:youknow /data

二、mongodb安装

1、下载

下载 mongodb 3.4.6 安装包

curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.4.6.tgz
#解压
tar -xzvf mongodb-linux-x86_64-3.4.6.tgz -C /usr/local/
#改名
mv mongodb-linux-x86_64-3.4.6 mongodb

2、创建相关目录

根据服务器的规范,分别在对应的服务器上建立conf、mongos、config、shard1、shard2、shard3、shard4、shard5等目录,因为mongos不存储数据,只需要建立日志文件目录即可。

mkdir -p /usr/local/mongodb/conf
mkdir -p /data/mongos/log
mkdir -p /data/config/data
mkdir -p /data/config/log
mkdir -p /data/shard1/data
mkdir -p /data/shard1/log
mkdir -p /data/shard2/data
mkdir -p /data/shard2/log
mkdir -p /data/shard3/data
mkdir -p /data/shard3/log
mkdir -p /data/shard4/data
mkdir -p /data/shard4/log
mkdir -p /data/shard5/data
mkdir -p /data/shard5/log

3、环境变量

为了后续方便操作,配置mongodb的环境变量,需要切到root用户下面

vim /etc/profile
# 内容
export MONGODB_HOME=/usr/local/mongodb
export PATH=$MONGODB_HOME/bin:$PATH
# 使立即生效,在安装用户下(youknow)执行
source /etc/profile

查看mongodb版本信息mongod -v 输出版本信息表明配置环境变量成功

三、集群配置

1、config server配置服务器

在服务器33、34、35上配置以下内容:

添加配置文件:

vi /usr/local/mongodb/conf/config.conf

## content
systemLog:
  destination: file
  logAppend: true
  path: /data/config/log/config.log

# Where and how to store data.
storage:
  dbPath: /data/config/data
  journal:
    enabled: true
# how the process runs
processManagement:
  fork: true
  pidFilePath: /data/config/log/configsrv.pid

# network interfaces
net:
  port: 21000
  bindIp: 192.168.0.33

#operationProfiling:
replication:
    replSetName: config        

sharding:
    clusterRole: configsvr

启动三台服务器的config server

numactl --interleave=all mongod --config /usr/local/mongodb/conf/config.conf

登录任意一台配置服务器,初始化配置副本集

#连接
mongo 192.168.0.33:21000
#config变量
config = {
...    _id : "config",
...     members : [
...         {_id : 0, host : "192.168.0.33:21000" },
...         {_id : 1, host : "192.168.0.34:21000" },
...         {_id : 2, host : "192.168.0.35:21000" }
...     ]
... }

#初始化副本集
rs.initiate(config)

#查看分区状态
rs.status();

其中,”_id” : “configs”应与配置文件中配置的 replicaction.replSetName 一致,”members” 中的 “host” 为三个节点的ip和port

这样配置服务器就配置好了

2、配置分片、副本集

配置第一个分片副本集

在服务器 31、32、33上面做以下配置

配置文件

vi /usr/local/mongodb/conf/shard1.conf

#配置文件内容
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/shard1/log/shard1.log

# Where and how to store data.
storage:
  dbPath: /data/shard1/data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 20

# how the process runs
processManagement:
  fork: true 
  pidFilePath: /data/shard1/log/shard1.pid

# network interfaces
net:
  port: 27001
  bindIp: 192.168.0.33

#operationProfiling:
replication:
    replSetName: shard1
sharding:
    clusterRole: shardsvr

启动三台服务器的shard1 server

numactl --interleave=all mongod  --config  /usr/local/mongodb/conf/shard1.conf

登陆任意一台服务器,初始化副本集

mongo 192.168.0.31:27001
#使用admin数据库
use admin
#定义副本集配置
config = {
...    _id : "shard1",
...     members : [
...         {_id : 0, host : "192.168.0.31:27001" },
...         {_id : 1, host : "192.168.0.32:27001" },
...         {_id : 2, host : "192.168.0.33:27001" }
...     ]
... }


#初始化副本集配置
rs.initiate(config);


#查看分区状态
rs.status();

配置第二个分片副本集

在服务器32、33、34上面做以下配置

配置文件

vi /usr/local/mongodb/conf/shard2.conf

#配置文件内容
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/shard2/log/shard2.log

# Where and how to store data.
storage:
  dbPath: /data/shard2/data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 20

# how the process runs
processManagement:
  fork: true 
  pidFilePath: /data/shard2/log/shard2.pid

# network interfaces
net:
  port: 27002
  bindIp: 192.168.0.33


#operationProfiling:
replication:
    replSetName: shard2
sharding:
    clusterRole: shardsvr

启动三台服务器的shard2 server

numactl --interleave=all mongod  --config /usr/local/mongodb/conf/shard2.conf

登陆任意一台服务器,初始化副本集

mongo 192.168.0.32:27002
#使用admin数据库
use admin
#定义副本集配置
config = {
...    _id : "shard2",
...     members : [
...         {_id : 0, host : "192.168.0.32:27002" },
...         {_id : 1, host : "192.168.0.33:27002" },
...         {_id : 2, host : "192.168.0.34:27002" }
...     ]
... }


#初始化副本集配置
rs.initiate(config);

#查看分区状态
rs.status();

配置第三个分片副本集

在服务器33、34、35上面做以下配置

配置文件

vi /usr/local/mongodb/conf/shard3.conf

#配置文件内容
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/shard3/log/shard3.log

# Where and how to store data.
storage:
  dbPath: /data/shard3/data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 20
# how the process runs
processManagement:
  fork: true 
  pidFilePath: /data/shard3/log/shard3.pid

# network interfaces
net:
  port: 27003
  bindIp: 192.168.0.33


#operationProfiling:
replication:
    replSetName: shard3
sharding:
    clusterRole: shardsvr

启动三台服务器的shard3 server

numactl --interleave=all mongod  --config  /usr/local/mongodb/conf/shard3.conf

登陆任意一台服务器,初始化副本集

mongo 192.168.0.33:27003
#使用admin数据库
use admin
#定义副本集配置
config = {
...    _id : "shard3",
...     members : [
...         {_id : 0, host : "192.168.0.33:27003" },
...         {_id : 1, host : "192.168.0.34:27003" },
...         {_id : 2, host : "192.168.0.35:27003" }
...     ]
... }


#初始化副本集配置
rs.initiate(config);

#查看分区状态
rs.status();

配置第四个分片副本集

在服务器34、35、31上面做以下配置

配置文件

vi /usr/local/mongodb/conf/shard4.conf

#配置文件内容
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/shard4/log/shard4.log

# Where and how to store data.
storage:
  dbPath: /data/shard4/data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 20

# how the process runs
processManagement:
  fork: true 
  pidFilePath: /data/shard4/log/shard4.pid

# network interfaces
net:
  port: 27004
  bindIp: 192.168.0.35


#operationProfiling:
replication:
    replSetName: shard4
sharding:
    clusterRole: shardsvr

启动三台服务器的shard4 server

numactl --interleave=all mongod  --config /usr/local/mongodb/conf/shard4.conf

登陆任意一台服务器,初始化副本集

mongo 192.168.0.34:27004
#使用admin数据库
use admin
#定义副本集配置
config = {
...    _id : "shard4",
...     members : [
...         {_id : 0, host : "192.168.0.34:27004" },
...         {_id : 1, host : "192.168.0.35:27004" },
...         {_id : 2, host : "192.168.0.31:27004" }
...     ]
... }


#初始化副本集配置
rs.initiate(config);

#查看分区状态
rs.status();

配置第五个分片副本集

在服务器35、31、32上面做以下配置

配置文件

vi /usr/local/mongodb/conf/shard5.conf

#配置文件内容
# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/shard5/log/shard5.log

# Where and how to store data.
storage:
  dbPath: /data/shard5/data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 20

# how the process runs
processManagement:
  fork: true 
  pidFilePath: /data/shard5/log/shard5.pid

# network interfaces
net:
  port: 27005
  bindIp: 192.168.0.35


#operationProfiling:
replication:
    replSetName: shard5
sharding:
    clusterRole: shardsvr

启动三台服务器的shard5 server

numactl --interleave=all mongod  --config  /usr/local/mongodb/conf/shard5.conf

登陆任意一台服务器,初始化副本集

mongo 192.168.0.35:27005
#使用admin数据库
use admin
#定义副本集配置
config = {
...    _id : "shard5",
...     members : [
...         {_id : 0, host : "192.168.0.35:27005" },
...         {_id : 1, host : "192.168.0.31:27005" },
...         {_id : 2, host : "192.168.0.32:27005" }
...     ]
... }


#初始化副本集配置
rs.initiate(config);

#查看分区状态
rs.status();

至此,五个分片和副本集搭建完毕

3、配置路由服务器 mongos

以下配置在服务器31、32上执行

注意:先启动配置服务器和分片服务器,后启动路由实例

vi /usr/local/mongodb/conf/mongos.conf

systemLog:
  destination: file
  logAppend: true
  path: /data/mongos/log/mongos.log
processManagement:
  fork: true
#  pidFilePath: /usr/local/mongodb/mongos.pid

# network interfaces
net:
  port: 20000
  bindIp: 192.168.0.31
#监听的配置服务器,只能有1个或者3个 configs为配置服务器的副本集名字
sharding:
   configDB: configs/192.168.0.33:21000,192.168.0.34:21000,192.168.0.35:21000

启动二台服务器的mongos server

mongos  --config  /usr/local/mongodb/conf/mongos.conf

4、启用分片

目前搭建了mongodb配置服务器、路由服务器,各个分片服务器,不过应用程序连接到mongos路由服务器并不能使用分片机制,还需要在程序里设置分片配置,让分片生效。

登陆任意一台mongos

mongo 192.168.0.31:20000
#使用admin数据库
use  admin
#串联路由服务器与分配副本集
sh.addShard("shard1/192.168.0.31:27001,192.168.0.32:27001,192.168.0.33:27001")
sh.addShard("shard2/192.168.0.32:27002,192.168.0.33:27002,192.168.0.34:27002")
sh.addShard("shard3/192.168.0.33:27003,192.168.0.34:27003,192.168.0.35:27003")
sh.addShard("shard4/192.168.0.34:27004,192.168.0.35:27004,192.168.0.31:27004")
sh.addShard("shard5/192.168.0.35:27005,192.168.0.31:27005,192.168.0.32:27005")
#查看集群状态
sh.status()

这样mongodb的五台集群搭建就已经完成了,后期如何优化和运营请查看下一篇文章。

四、错误

rs.initiate报错

执行 rs.initiate(config); 报错:

rs.initiate(config);
{
        "ok" : 0,
        "errmsg" : "No host described in new configuration 1 for replica set shard1 maps to this node",
        "code" : 93,
        "codeName" : "InvalidReplicaSetConfig"
}

最后发现是自己的一个端口号写错了。

启动mongos报错

启动mongos的时候报错:

about to fork child process, waiting until server is ready for connections.
forked process: 1436
ERROR: child process failed, exited with error number 1

这个问题卡了我们半天,找了很多的资料,不是说清理lock文件,就是说清理log文件总无解,最后看到这个网站的提示

ERROR: child process failed, exited with error number 1

去掉了配置文件中 –fork,才将真正的错误日志打印了出来,是我们的配置文件中的路径写错了,本来是log写成了logs

原来:path: /data/logs/mongos.log

改为:path: /data/log/mongos.log

成功

为了方便大家拿取配置文件,我在github上面放置了一份: https://github.com/ityouknow/hadoop-ecosystem-examples/tree/master/mongodb/mongodb-five-cluster-conf

Redis与Memcached的区别

传统MySQL+ Memcached架构遇到的问题
  
实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,我们遇到了很多问题:

  1. MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间。
      
  2. Memcached与MySQL数据库数据一致性问题。
      
  3. Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支撑。
      
  4. 跨机房cache同步问题。

众多NoSQL百花齐放,如何选择
  
最近几年,业界不断涌现出很多各种各样的NoSQL产品,那么如何才能正确地使用好这些产品,最大化地发挥其长处,是我们需要深入研究和思考的问题,实际归根结底最重要的是了解这些产品的定位,并且了解到每款产品的tradeoffs,在实际应用中做到扬长避短,总体上这些NoSQL主要用于解决以下几种问题

  1. 少量数据存储,高速读写访问。此类产品通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的功能,实际这正是Redis最主要的适用场景。
      
  2. 海量数据存储,分布式系统支持,数据一致性保证,方便的集群节点添加/删除。
      
  3. 这方面最具代表性的是dynamo和bigtable 2篇论文所阐述的思路。前者是一个完全无中心的设计,节点之间通过gossip方式传递集群信息,数据保证最终一致性,后者是一个中心化的方案设计,通过类似一个分布式锁服务来保证强一致性,数据写入先写内存和redo log,然后定期compat归并到磁盘上,将随机写优化为顺序写,提高写入性能。
      
  4. Schema free,auto-sharding等。比如目前常见的一些文档数据库都是支持schema-free的,直接存储json格式数据,并且支持auto-sharding等功能,比如mongodb。

面对这些不同类型的NoSQL产品,我们需要根据我们的业务场景选择最合适的产品。
  
Redis适用场景,如何正确的使用
  
前面已经分析过,Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?

如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

  1. Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

  2. Redis支持数据的备份,即master-slave模式的数据备份。

  3. Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

抛开这些,可以深入到Redis内部构造去观察更加本质的区别,理解Redis的设计。

在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。Redis只会缓存所有的 key的信息,如果Redis发现内存的使用量超过了某一个阀值,将触发swap的操作,Redis根据“swappability = age*log(size_in_memory)”计 算出哪些key对应的value需要swap到磁盘。然后再将这些key对应的value持久化到磁盘中,同时在内存中清除。这种特性使得Redis可以 保持超过其机器本身内存大小的数据。当然,机器本身的内存必须要能够保持所有的key,毕竟这些数据是不会进行swap操作的。同时由于Redis将内存 中的数据swap到磁盘中的时候,提供服务的主线程和进行swap操作的子线程会共享这部分内存,所以如果更新需要swap的数据,Redis将阻塞这个 操作,直到子线程完成swap操作后才可以进行修改。

使用Redis特有内存模型前后的情况对比:

VM off: 300k keys, 4096 bytes values: 1.3G used
VM on:  300k keys, 4096 bytes values: 73M used
VM off: 1 million keys, 256 bytes values: 430.12M used
VM on:  1 million keys, 256 bytes values: 160.09M used
VM on:  1 million keys, values as large as you want, still: 160.09M used

当 从Redis中读取数据的时候,如果读取的key对应的value不在内存中,那么Redis就需要从swap文件中加载相应数据,然后再返回给请求方。 这里就存在一个I/O线程池的问题。在默认的情况下,Redis会出现阻塞,即完成所有的swap文件加载后才会相应。这种策略在客户端的数量较小,进行 批量操作的时候比较合适。但是如果将Redis应用在一个大型的网站应用程序中,这显然是无法满足大并发的情况的。所以Redis运行我们设置I/O线程 池的大小,对需要从swap文件中加载相应数据的读取请求进行并发操作,减少阻塞的时间。

如果希望在海量数据的环境中使用好Redis,我相信理解Redis的内存设计和阻塞的情况是不可缺少的。

Linux – 将memcached注册为服务

1、编写脚本

编写脚本文件如下(memcached):

#!/bin/sh
#
# memcached: Start/Stop/Restart memcached
# chkconfig: 35 33 84
# description: memcached server
MEMCACHED=/usr/local/bin/memcached
# memcached 分配的内存大小,单位M
MEMSIZE=128
USER=nobody

# memcached使用的端口
PORT01=11211
# 每个memcache 提供的最大连接数
MAXCONN=1024
# 每个memcache 的进程ID
PID01=/var/run/memcached/memcached$PORT01.pid
RETVAL=0
prog="memcached"

start() {
         echo -n $"Starting $prog: "
         $MEMCACHED -d -m $MEMSIZE -u $USER -p $PORT01 -c $MAXCONN -P $PID01
     if [ $? -eq 0 ];then
            echo "memcacheds$PORT01 servers is start ok..."
         else
            echo "memcacheds$PORT01 server not runing......"
         fi

        }

stop() {

      for i in $PID01
      do 
       kill `cat $i`
       rm -f $i
           echo  $"Stopping $prog: "
      done

     }

# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart}"
;;
esac
exit $RETVAL

2、将其放入/etc/init.d/目录下

3、为文件赋予被执行的权限

chmod /etc/init.d/memcached

4、将memcached加入chkconfig管理列表

执行命令:

chkconfig --add memcached

chkconfig memcached on

5、启动memcached服务

service memcached start

MariaDB的mysql.user表被误删除

误删除mysql.user表或者表中的数据

解决方法:

1、停掉所有mysql或mariadb服务:

~]#service mysqld stop(CentOS6)
~]#systemctl  stop mysqld.service(CentOS7)

或者

~]#pkill mysql

2、命令行启动:

~]#/bin/mysqld_safe --skip-grant-tables &

3、进入mysql库,用户信息存放在mysql库下user表:

MariaDB [none]>use mysql;

4、查看user表:

MariaDB [mysql]>select Host,User,Password from mysql.user;

5、(注意:本步骤在无mysql.user表时执行)因为误删除user表,所以现在需要在mysql库下新创建user表:

MariaDB [mysql]>create table `user` (
  `Host` char(60) collate utf8_bin NOT NULL default '',
  `User` char(16) collate utf8_bin NOT NULL default '',
  `Password` char(41) character set latin1 collate latin1_bin NOT NULL default '
',
  `Select_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Insert_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Update_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Delete_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Create_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Drop_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Reload_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Shutdown_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Process_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `File_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Grant_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `References_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Index_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Alter_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Show_db_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Super_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Create_tmp_table_priv` enum('N','Y') character set utf8 NOT NULL default 'N',

  `Lock_tables_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Execute_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Repl_slave_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Repl_client_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Create_view_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Show_view_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Create_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Alter_routine_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `Create_user_priv` enum('N','Y') character set utf8 NOT NULL default 'N',
  `ssl_type` enum('','ANY','X509','SPECIFIED') character set utf8 NOT NULL defau
lt '',
  `ssl_cipher` blob NOT NULL,
  `x509_issuer` blob NOT NULL,
  `x509_subject` blob NOT NULL,
  `max_questions` int(11) unsigned NOT NULL default '0',
  `max_updates` int(11) unsigned NOT NULL default '0',
  `max_connections` int(11) unsigned NOT NULL default '0',
  `max_user_connections` int(11) unsigned NOT NULL default '0',
  PRIMARY KEY  (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global
privileges';

6、插入root用户数据:

MariaDB [mysql]>insert into mysql.user (Host,User,Password) values(‘localhost’,’root’,password(’pa$$word’));

7、查看添加root用户的权限:

MariaDB [mysql]>select * from mysql.user where User=’root’ G

8、添加属于root用户的管理权限:

MariaDB [mysql]>update mysql.user set  Select_priv='Y',Insert_priv='Y',Update_priv='Y',Delete_priv='Y',Create_priv='Y',Drop_priv='Y',Reload_priv='Y',Shutdown_priv='Y',Process_priv='Y',File_priv='Y',Grant_priv='Y',References_priv='Y',Index_priv='Y',Alter_priv='Y',Show_db_priv='Y',Super_priv='Y',Create_tmp_table_priv='Y',Lock_tables_priv='Y',Execute_priv='Y',Repl_slave_priv='Y',Repl_client_priv='Y',Create_view_priv='Y',Show_view_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Create_user_priv='Y',Event_priv='Y',Trigger_priv='Y',Create_tablespace_priv='Y' where User='root' and Host='localhost';

9、查看是否授权成功:

MariaDB [mysql]>select * from mysql.user where User=’root’;

10、完成提交并刷新内存数据:

MariaDB [mysql]>commit;
MariaDB [mysql]>flush privileges;

11、重新启动Mariadb并登录:

~]#systemctl restart mariadb.service
~]#mysql -uroot -hlocalhost -p

输入设置的密码(此处是pa$$word)。

登陆成功

12、验证:

MariaDB [none]>show grants;
MariaDB [none]>show grants for root@localhost;

Linux下MySQL/MariaDB Galera集群搭建过程

MariaDB介绍

MariaDB是开源社区维护的一个MySQL分支,由MySQL的创始人Michael Widenius主导开发,采用GPL授权许可证。

MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。

详细介绍请参考链接:

http://mariadb.org/(官网)

http://baike.baidu.com/link?url=dFJ-My-I52YFc1mx26K804LPwZrcEWCwB4IqfA4-soYx6295BZLIe7bEFgOtt3CWZ8AYpkp1P342L4S-R4x4CK

Galera Cluster介绍

Galera Cluster是基于MySQL/innodb二次开发而成的一个支持“多主同步”的数据库主从集群,具有高可用,易于扩展等特点。

详细介绍请参考链接:

http://galeracluster.com/(官网)

Galera replication for MySQL

本文使用的Linux发行版:CentOS6.7 下载地址:https://wiki.centos.org/Download

1. 添加yum源

[root@localhost ~]# vi /etc/yum.repos.d/CentOS-MariaDB.repo

添加如下几行:

[mariadb] name = MariaDB
baseurl = http://yum.mariadb.org/5.5/rhel6-amd64
gpgkey = https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck = 1

未分类

2. 安装mariadb galera软件包

[root@localhost ~]# yum install MariaDB-Galera-server MariaDB-client galera

未分类

未分类

3. 修改防火墙配置

[root@localhost ~]# vi /etc/sysconfig/iptables

添加如下几行:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4444 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4567 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4568 -j ACCEPT

未分类

4. 重启防火墙功能

[root@localhost ~]# service iptables restart

未分类

5. 安装selinux管理工具

[root@localhost ~]# yum provides /usr/sbin/semanage
[root@localhost ~]# yum -y install policycoreutils-python

未分类

6. 修改selinux安全策略

[root@localhost ~]# semanage port -a -t mysqld_port_t -p tcp 4567
[root@localhost ~]# semanage port -a -t mysqld_port_t -p tcp 4568
[root@localhost ~]# semanage permissive -a mysqld_t

未分类

7. 启动mysql服务

[root@localhost ~]# service mysql start

8. 执行mysql安全设置

[root@localhost ~]# mysql_secure_installation

(先设置root账户密码,再一直“y”下去即可)

未分类

未分类

9. 创建用于节点同步的账号

[root@localhost ~]# mysql -uroot -p
MariaDB [(none)]> grant usage on *.* to sst@'%' identified by '123456';
MariaDB [(none)]> flush privileges;

10. 修改mysql默认字符集

MariaDB [(none)]> show variables like 'character%';
MariaDB [(none)]> set character_set_server = utf8;
MariaDB [(none)]> set character_set_database = utf8;

11. 修改集群节点配置

[root@localhost ~]# cp /usr/share/mysql/wsrep.cnf /etc/my.cnf.d/
[root@localhost ~]# vi /etc/my.cnf.d/wsrep.cnf

修改如下几行:

wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://"    #集群节点N的地址(注意把前面的"#"删掉!)
wsrep_sst_auth=sst:123456    #节点N的数据库账户和密码
  • 参数说明

“gcomm://” 是特殊的地址,仅仅是galera cluster初始化启动时候使用。
如果集群启动以后,我们关闭了第一个节点,那么再次启动的时候必须先修改”gcomm://”为其他节点的集群地址,例如wsrep_cluster_address=”gcomm://192.168.0.152″。

检查/etc/my.cnf中有没有!includedir /etc/my.cnf.d/这一行,没有则添加。

[root@localhost ~]# vi /etc/my.cnf

未分类

到这里,第1个节点的配置就完成了,然后在另一台主机上按照步骤1~11配置第2个节点,只需修改节点2的wsrep_cluster_address为节点1的IP即可,以此类推。

12. 启动集群节点

  • 检查mysql进程:[root@localhost ~]# ps aux|grep mysql
  • 停止mysql服务:[root@localhost ~]# service mysql stop
  • 启动第1个节点:[root@localhost ~]# service mysql bootstrap

未分类

启动第2、3、…个节点:[root@localhost ~]# service mysql start

未分类

(注意:启动mysql之前先检查一下服务是否已经启动,不要重复启动,如果无法停止当前mysql服务则手动kill掉mysql的进程)

13. 检查集群运行状态

[root@localhost ~]# mysql -uroot -p
MariaDB [(none)]> show status like 'wsrep%';

如果wsrep_connected=ON且wsrep_ready=ON则说明节点成功接入集群。

未分类

未分类

14. 配置集群的仲裁节点

对于只有2个节点的galera cluster和其他集群软件一样,需要面对极端情况下的“脑裂”状态。为了避免这种问题,galera引入了“arbitrator(仲裁人)”。
“仲裁人”节点上没有数据,它在集群中的作用就是在集群发生分裂时进行仲裁,集群中可以有多个“仲裁人”节点。将“仲裁人”节点加入集群的方法很简单,运行如下命令即可:
[root@localhost ~]# garbd -a gcomm://<节点IP> -g my_wsrep_cluster -d

  • 参数说明

-a 集群地址
-g 集群名称
-d 以daemon模式运行

15. 检查数据库是否符合要求

部署到集群之前,建议先检查数据库是否符合galera的要求,比如存储引擎必须是innodb、数据表必须有主键等,否则记录将不会在多台复制。

选择指定的数据库,执行以下SQL输出不符合要求的表及其原因,根据相应的原因修改即可:

select distinct concat( t.table_schema, '.', t.table_name ) as tbl, t. engine, if ( isnull(c.constraint_name), 'nopk', '' ) as nopk, if ( s.index_type = 'fulltext', 'fulltext', '' ) as ftidx, if ( s.index_type = 'spatial', 'spatial', '' ) as gisidx from information_schema. tables as t left join information_schema.key_column_usage as c on ( t.table_schema = c.constraint_schema and t.table_name = c.table_name and c.constraint_name = 'primary' ) left join information_schema.statistics as s on ( t.table_schema = s.table_schema and t.table_name = s.table_name and s.index_type in ('fulltext', 'spatial')) where t.table_schema not in ( 'information_schema', 'performance_schema', 'mysql' ) and t.table_type = 'base table' and ( t.engine <> 'innodb' or c.constraint_name is null or s.index_type in ('fulltext', 'spatial')) order by t.table_schema, t.table_name;

16. 常见问题

  • 启动mysql时出错:SST in progress, setting sleep higher. ERROR!
    • 确保本机已安装rsync:[root@localhost ~]# yum list|grep rsync
    • 确保已允许galera sst使用的端口4444、4567、4568通过防火墙并重启防火墙功能
    • 确保selinux已对端口4444开放权限:[root@localhost ~]# semanage port -a -t mysqld_port_t -p tcp 4444
  • 查看galera集群状态时wsrep_connected和wsrep_ready的值均为OFF!

打开/etc/my.cnf.d/wsrep.cnf文件,找到wsrep_cluster_address=”gcomm://”这一行,检查前面是否有”#”,如果有则删掉并重启mysql。