MongoDB 分片的原理、搭建、应用

原文:http://blog.51cto.com/13643643/2148825

什么是分片

高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。
MongoDB分片是使用多个服务器存储数据的方法,以支持巨大的数据存储和对数据进行操作。分片技术可以满足MongoDB数据量大量增长的需求,当一台MongoDB服务器不足以存储海量数据或者不足以提供可接受的读写吞吐量时,我们就可以通过在多台服务器上分割数据,使得数据库系统能存储和处理更多的数据。

MongoDB分片优势

分片为应对高吞吐量与大数据量提够了方法

  • 使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,群集可以提高自己的存储容量。比如,当插入一条数据时,应用只需要访问存储这条数据的分片。
  • 使用分片减少了每个分片村存储的数据
    分片的优势在于提供类似线性增长的架构,提高数据可用性,提高大型数据库查询服务器的性能。当MongoDB单点数据库服务器存储成为瓶颈、单点数据库服务器的性能成为瓶颈或需要部署大型应用以充分利用内存时,可以使用分片技术。

    MongoDB分片群集的组成

  • Shard:分片服务器,用于存储实际的数据块,实际生产环境中一个shard server 角色可以由几台服务器组成一个Peplica Set 承担,防止主机单点故障。
  • Config Server:配置服务器,存储了整个分片群集的配置信息,其中包括chunk信息。
  • Routers:前端路由,客户端由此接入,且让整个群集看上去像单一数据库,前端应用可以透明使用。

环境准备

  • 系统版本:CenTos 7
  • 软件版本:MongoDB4.0
  • 关闭防火墙及selinux
    systemctl stop firewalld.service
    setenforce 0
IP:172.16.10.26 IP:172.16.10.27 IP:172.16.10.29
mongos(27017) mongos(27017) mongos(27017)
config(30000) config(30000) config(30000)
shard1主节点(40001) shard1副节点(40001) shard1仲裁节点(40001)
shard2仲裁节点(40002) shard2主节点(40002) shard2副节点(40002)
shard1副节点(40003) shard1仲裁节点(40003) shard1主节点(40003)

部署MongoDB分片群集

群集部署的搭建思路,利用三台服务器,分别安装mongodb数据库,每台服务器创建五个实例(mongos、configs、shard1、shard2、shard3)。三台不同的服务器上的相同名称的实例,创建为一个复制集,分别包括主节点,副节点,仲裁节点。mongos不需创建复制集,config不需指定主副节点及仲裁节点,但是要创建复制集。三台服务器的操作步骤略有差别,但是大多是都是重复操作,步骤完全一致。

安装MongoDB数据库

安装支持软件和mongodb

yum install openssl-devel -y
tar zxf mongodb-linux-x86_64-rhel70-4.0.0.tgz -C /usr/local
mv /usr/local/mongodb-linux-x86_64-rhel70-4.0.0 /usr/local/mongodb  //解压即完成安装

创建数据存储目录和日志存储目录

路由服务器不存储数据,因此就不需要创建数据存储目录,只需创建config、shard1、shaed2、shard3即可,日志文件创建完成之后还需要给予权限。

mkdir -p /data/mongodb/logs/
mkdir /etc/mongodb/
mkdir /data/mongodb/config/
mkdir /data/mongodb/shard{1,2,3}
touch /data/mongodb/logs/shard{1,2,3}.log
touch /data/mongodb/logs/mongos.log
touch /data/mongodb/logs/config.log
chmod 777 /data/mongodb/logs/*.log

创建管理用户,修改目录权限

useradd -M -u 8000 -s /sbin/nologin mongo
chown -R mongo.mongo /usr/local/mongodb
chown -R mongo.mongo /data/mongodb

设置环境变量

echo "PATH=/usr/local/mongodb/bin:$PATH" >> /etc/profile
source /etc/profile

系统内存优化

ulimit -n 25000
ulimit -u 25000
sysctl -w vm.zone_reclaim_mode=0
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag  //*注意*这些优化都是临时的,重启失效

部署配置服务器

创建配置文件

#vim /etc/mongodb/config.conf
pidfilepath = /data/mongodb//logs/config.pid           //pid文件位置
dbpath = /data/mongodb/config/                             //数据文件存放位置
logpath = /data/mongodb//logs/config.log               //日志文件位置
logappend = true                 
bind_ip = 0.0.0.0                                                     //监听地址
port = 30000                                                           //端口号
fork = true 
replSet=configs                                                      //复制集名称
configsvr = true
maxConns=20000                                                  //最大连接数

将配置文件发送到其他服务器

scp /etc/mongodb/config.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/config.conf root@172.16.10.29:/etc/mongodb/

MongoDB(4.0)分片——大数据的处理之道

启动config实例

mongod -f /etc/mongodb/config.conf  //三台服务器操作一致

配置复制集(任一台操作即可)

mongo --port 30000                    //建议三台服务器都进入数据库,方便查看角色变更
config={_id:"configs",members:[{_id:0,host:"172.16.10.26:30000"},{_id:1,host:"172.16.10.27:30000"},{_id:2,host:"172.16.10.29:30000"}]}    //创建复制集
rs.initiate(config)                //初始化复制集

MongoDB(4.0)分片——大数据的处理之道
MongoDB(4.0)分片——大数据的处理之道

部署shard1分片服务器

创建配置文件

#vim /etc/mongodb/shard1.conf
pidfilepath = /data/mongodb//logs/shard1.pid
dbpath = /data/mongodb/shard1/
logpath = /data/mongodb//logs/shard1.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40001
fork = true
replSet=shard1
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard1.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard1.conf root@172.16.10.29:/etc/mongodb/

启动shard1实例

mongod -f /etc/mongodb/shard1.conf  //三台服务器操作一致

配置shard1复制集

在shard分片服务器的创建中,需要注意的点是,不是在任一台服务器上创建都能成功的,如果选择在预先设置为仲裁节点的服务器上创建复制集会报错。以shard1分片服务器为例,可以在172.16.10.26和172.16.10.27服务器上创建复制集,在172.16.10.29上创建则会失败,因为在复制集创建之前,172.16.10.29已经被设置为仲裁节点。
MongoDB(4.0)分片——大数据的处理之道

mongo --port 40001    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard1",members:[{_id:0,host:"172.16.10.26:40001",priority:2},{_id:1,host:"172.16.10.27:40001",priority:1},{_id:2,host:"172.16.10.29:40001",arbiterOnly:true}]}
rs.initiate(config)

部署shard2分片服务器

创建配置文件

#vim /etc/mongodb/shard2.conf
pidfilepath = /data/mongodb//logs/shard2.pid
dbpath = /data/mongodb/shard2/
logpath = /data/mongodb//logs/shard2.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40002
fork = true
replSet=shard2
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard2.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard2.conf root@172.16.10.29:/etc/mongodb/

启动shard2实例

mongod -f /etc/mongodb/shard2.conf  //三台服务器操作一致

配置shard复制集(非仲裁节点服务器)

mongo --port 40002    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard2",members:[{_id:0,host:"172.16.10.26:40002",arbiterOnly:true},{_id:1,host:"172.16.10.27:40002",priority:2},{_id:2,host:"172.16.10.29:40002",priority:1}]}
rs.initiate(config)

部署shard3分片服务器

创建配置文件

#vim /etc/mongodb/shard3.conf
pidfilepath = /data/mongodb//logs/shard3.pid
dbpath = /data/mongodb/shard3/
logpath = /data/mongodb//logs/shard3.log
logappend = true
journal = true
quiet = true
bind_ip = 0.0.0.0
port = 40003
fork = true
replSet=shard3
shardsvr = true
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/shard3.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/shard3.conf root@172.16.10.29:/etc/mongodb/

启动shard3实例

mongod -f /etc/mongodb/shard3.conf  //三台服务器操作一致

配置shard复制集(非仲裁节点服务器)

mongo --port 40003    //建议三台服务器都进入数据库,方便查看角色变更
use admin
config={_id:"shard3",members:[{_id:0,host:"172.16.10.26:40003",priority:1},{_id:1,host:"172.16.10.27:40003",arbiterOnly:true},{_id:2,host:"172.16.10.29:40003",priority:2}]}
rs.initiate(config);

部署路由服务器

创建配置文件

pidfilepath = /data/mongodb/logs/mongos.pid
logpath=/data/mongodb/logs/mongos.log
logappend = true
bind_ip = 0.0.0.0
port = 27017
fork = true
configdb = configs/172.16.10.26:30000,172.16.10.27:30000,172.16.10.29:30000
maxConns=20000

将配置文件发送到其他服务器

scp /etc/mongodb/mongos.conf root@172.16.10.27:/etc/mongodb/
scp /etc/mongodb/mongos.conf root@172.16.10.29:/etc/mongodb/

启动mongos实例

mongos -f /etc/mongodb/mongos.conf  //三台服务器操作一致*注意*这里是“mongos”而非“mongod”

启用分片功能

mongo    //因为默认端口即是27017,所以此处不接端口号
mongos> use admin
mongos> sh.addShard("shard1/172.16.10.26:40001,172.16.10.27:40001,172.16.10.29:40001")
mongos> sh.addShard("shard2/172.16.10.26:40002,172.16.10.27:40002,172.16.10.29:40002")
mongos> sh.status()           //查看群集状态
//此处先添加两各分片服务器,还有一个,待会添加

MongoDB(4.0)分片——大数据的处理之道

测试服务器分片功能

设置分片chunk大小

mongos> use config
switched to db config
mongos> db.settings.save({"_id":"chunksize","value":1})   //设置块大小为1M是方便实验,不然就需要插入海量数据
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "chunksize" })

模拟写入数据

mongos> use python
switched to db python
mongos> show collections
mongos> for(i=1;i<=50000;i++){db.user.insert({"id":i,"name":"jack"+i})}
//在python库的user表中循环写入五万条数据
WriteResult({ "nInserted" : 1 })

启用数据库分片

mongos>sh.enableSharding("python")
//数据库分片就有针对性,可以自定义需要分片的库或者表,毕竟也不是所有数据都是需要分片操作的

MongoDB(4.0)分片——大数据的处理之道

为表创建的索引

创建索引的规则是不能一致性太高,要具有唯一性,例如序号,比如性别这一类重复性太高的就不适合做索引

mongos> db.user.createIndex({"id":1})   //以”id“为索引

MongoDB(4.0)分片——大数据的处理之道

启用表分片

mongos> sh.shardCollection("python.user",{"id":1})

MongoDB(4.0)分片——大数据的处理之道

查看分片情况

mongos> sh.status()
--- Sharding Status --- 
            ···省略内容
  shards:
        {  "_id" : "shard1",  "host" : "shard1/172.16.10.26:40001,172.16.10.27:40001",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/172.16.10.27:40002,172.16.10.29:40002",  "state" : 1 }
            ···省略内容
                        chunks:
                                shard1  3
                                shard2  3
                        { "id" : { "$minKey" : 1 } } -->> { "id" : 9893 } on : shard1 Timestamp(2, 0) 
                        { "id" : 9893 } -->> { "id" : 19786 } on : shard1 Timestamp(3, 0) 
                        { "id" : 19786 } -->> { "id" : 29679 } on : shard1 Timestamp(4, 0) 
                        { "id" : 29679 } -->> { "id" : 39572 } on : shard2 Timestamp(4, 1) 
                        { "id" : 39572 } -->> { "id" : 49465 } on : shard2 Timestamp(1, 4) 
                        { "id" : 49465 } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 5) 

手动添加分片服务器,查看分片情况是否发生变化

mongos> use admin
switched to db admin
mongos> sh.addShard("172.16.10.26:40003,172.16.10.27:40003,172.16.10.29:40003")
mongos> sh.status()
--- Sharding Status --- 
           ···省略内容
  shards:
        {  "_id" : "shard1",  "host" : "shard1/172.16.10.26:40001,172.16.10.27:40001",  "state" : 1 }
        {  "_id" : "shard2",  "host" : "shard2/172.16.10.27:40002,172.16.10.29:40002",  "state" : 1 }
        {  "_id" : "shard3",  "host" : "shard3/172.16.10.26:40003,172.16.10.29:40003",  "state" : 1 }
           ···省略内容
                        chunks:
                                shard1  2
                                shard2  2
                                shard3  2
                        { "id" : { "$minKey" : 1 } } -->> { "id" : 9893 } on : shard3 Timestamp(6, 0) 
                        { "id" : 9893 } -->> { "id" : 19786 } on : shard1 Timestamp(6, 1) 
                        { "id" : 19786 } -->> { "id" : 29679 } on : shard1 Timestamp(4, 0) 
                        { "id" : 29679 } -->> { "id" : 39572 } on : shard3 Timestamp(5, 0) 
                        { "id" : 39572 } -->> { "id" : 49465 } on : shard2 Timestamp(5, 1) 
                        { "id" : 49465 } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(1, 5) 

服务器又对数据进行重新分片,当你再次移除一个分片服务器,此时又会对数据再次进行分片处理,MongoDB对数据的处理非常灵活

PHP Prime Number Function

//快速计算某个值以内的质数
function getPrimeNumbers($n)
{
    $sieve = [];
    for($i = 1; $i <= $n; $i++) {
        $sieve[$i] = $i;
    }

    $i =2;
    while($i * $i <= $n) {
        if(isset($sieve[$i])) {
            $k = $i;
            while ($k * $i <= $n) {
                unset($sieve[$k * $i]);
                $k++;
            }
        }
        $i++;
    }
    return $sieve;
}

php 抽奖概率算法

/* 
 * 不同概率的抽奖原理就是把0到*(比重总数)的区间分块
 * 分块的依据是物品占整个的比重,再根据随机数种子来产生0-* 中的某个数
 * 判断这个数是落在哪个区间上,区间对应的就是抽到的那个物品。
 * 随机数理论上是概率均等的,那么相应的区间所含数的多少就体现了抽奖物品概率的不同。
 */  


function get_rand($proArr)
{
$result = array();
foreach ($proArr as $key => $val) { 
    $arr[$key] = $val['v']; 
}  
$proSum = array_sum($arr);      // 计算总权重
$randNum = mt_rand(1, $proSum);
$d1 = 0;
$d2 = 0;
for ($i=0; $i < count($arr); $i++)
{
    $d2 += $arr[$i];
    if($i==0)
    {
        $d1 = 0;
    }
    else
    {
        $d1 += $arr[$i-1];
    }
    if($randNum >= $d1 && $randNum <= $d2)
    {
        $result = $proArr[$i];
        break; // 注意这里,当我们已经匹配到奖品时,就应该直接退出循环
    }
}
unset ($arr); 
return $result;


}

mysql中字符和数字在执行sql时进行类型转换

转自:http://blog.csdn.net/likunlun1234/article/details/41045299

https://www.cnblogs.com/micrari/p/6358652.html

select * from v_samp_register where is_end=1;
select * from v_samp_register where is_end=’1′;
is_end是int型,数据库中is_end是1或者0,上面两条语句执行结果怎么就一致了呢???

因为mysql不确定是哪个版本后,执行sql时,会按where条件的类型进行类型自动转换————感觉误导人

Mysql中前边有0的数据,0会被舍去的问题

最近由于项目的需求,需要频繁地拉取不同数据库中的数据,拉取数据的过程中,各种问题,十分悲催,真所谓,一个疏忽,你就要被推倒重来…

在经历了无数次被推倒又站起来,然后又被推倒的艰苦奋斗历程之后,终于完成了数据的迁移、更新、同步、修复。总结一下这期间遇到的部分问题:

1、Mysql中前边有0的数据,0会被舍去的问题

如一条数据为0371xxx,存入数据库后数据变为371xxx

(1)如果字段类型必须为int,可以修改字段,增加zero fill,alter table 表名称 modify 字段名称 int  not null zero fill;

(2)如果不必须为int,修改字段类型为字符串类型,另外sql语句中该字段数据一定要加引号,不加引号即使为字符串类型也会丢失前边的0

2、某些数据部分字段出现错误,而很多数据未出错字段又已做过修改

在数据库中增加一个isexists字段,默认为0.

主键判断数据是否存在,存在更新相应字段,不存在插入,更新与插入的数据isexists设置为1

更新完成后删除isexists为0的数据。确认数据无误后删除isexists字段。

公司项目代码中,某枚举字段数据库表中类型是char(1),在代码中,误以为是TINYINT,所以用数字筛选,后来发现结果不对。发现了一个现象,用数字0筛选会把所有的记录给筛选出来。
经过排查发现是在MySQL查询语句中,’abc’如果和’0’比较结果显然是不等的,但如果’abc’和0比较呢?结果居然是相等的。

随意测试了几下,结果如下:

  1. mysql> select ‘abc’=0;
  2. +———+
  3. | ‘abc’=0 |
  4. +———+
  5. | 1 |
  6. +———+
  7. 1 row in set, 1 warning (0.00 sec)
  1. mysql> select ‘0abc’=0;
  2. +———-+
  3. | ‘0abc’=0 |
  4. +———-+
  5. | 1 |
  6. +———-+
  7. 1 row in set, 1 warning (0.00 sec)
  1. mysql> select ’01abc’=0;
  2. +———–+
  3. | ’01abc’=0 |
  4. +———–+
  5. | 0 |
  6. +———–+
  7. 1 row in set, 1 warning (0.00 sec)
  1. mysql> select ‘013abc’=13;
  2. +————-+
  3. | ‘013abc’=13 |
  4. +————-+
  5. | 1 |
  6. +————-+
  7. 1 row in set, 1 warning (0.00 sec)

以上几个查询通过查看MySQL给出的警告,都可以看到类似如下的信息

  1. mysql> show warnings;
  2. +———+——+——————————————–+
  3. | Level | Code | Message |
  4. +———+——+——————————————–+
  5. | Warning | 1292 | Truncated incorrect DOUBLE value: ‘013abc’ |
  6. +———+——+——————————————–+
  7. 1 row in set (0.00 sec)

查阅MySQL 5.7官方文档中关于比较的章节,其中说明Strings are automatically converted to numbers and numbers to strings as necessary.。也就是说在比较的时候,String是可能会被转为数字的。

而对于数字开头的字符串来说,转为数字的结果就是截取前面的数字部分

  1. mysql> select cast(‘123abc’ as signed);
  2. +—————————–+
  3. | cast(‘123abc’ as signed) |
  4. +—————————–+
  5. | 123 |
  6. +—————————–+
  7. 1 row in set, 1 warning (0.00 sec)
  1. mysql> select cast(‘123.45abc’ as decimal(5,2));
  2. +———————————–+
  3. | cast(‘123.45abc’ as decimal(5,2)) |
  4. +———————————–+
  5. | 123.45 |
  6. +———————————–+
  7. 1 row in set (0.00 sec)
  1. mysql> select cast(‘abc’ as signed);
  2. +———————–+
  3. | cast(‘abc’ as signed) |
  4. +———————–+
  5. | 0 |
  6. +———————–+
  7. 1 row in set, 1 warning (0.00 sec)

“`

而对于开头部分不能截取出数字的字符串来说,转换的结果自然就是0了。

关于字符串类型与整数直接进行比较的坑,说穿了就是MySQL中字符串转为数字的逻辑,没遇到过确实可能不太清楚,遇到过一次以后经验就是,看清楚数据库表字段,尽量避免字符串与数字的直接比较。

此外,书写sql语句的时候务必注意不要犯类型的错误,也许查出来的结果是对的,但是由于类型不匹配的原因,将会导致表索引无法用上