第十六天
redis-sentinel
- 监控
- 自动选主,切换(6381 slaveof no one)
- 2号从库(6382)指向新主库(6381)
- 应用透明
- 自动处理故障节点
sentinel搭建过程
mkdir /data/26380
cd /data/26380
vim sentinel.conf
port 26380
dir "/data/26380"
sentinel monitor mymaster 127.0.0.1 6380 1
#### mymaster是自己定义的集群名
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster 123
### 启动:
redis-sentinel /data/26380/sentinel.conf &>/tmp/sentinel.log &
### 停主库测试:
redis-cli -p 6380 shutdown
redis-cli -p 6381
info replication
### 启动源主库(6380),看状态。
sentinel管理命令
redis-cli -p 26380
SENTINEL masters :列出所有被监视的主服务器
SENTINEL slaves
SENTINEL get-master-addr-by-name
: 返回给定名字的主服务器的 IP 地址和端口号 SENTINEL reset
: 重置所有名字和给定模式 pattern 相匹配的主服务器。 SENTINEL failover
: 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移。
redis cluster(3.3.3)
介绍
高性能
1、在多分片节点中,将16384个槽位,均匀分布到多个分片节点中
2、存数据时,将key做crc16(key),然后和16384进行取模,得出槽位值(0-16383之间)
3、根据计算得出的槽位值,找到相对应的分片节点的主节点,存储到相应槽位上
4、如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储
==高可用==
在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof的功能,同时当主节点down,实现类似于sentinel的自动failover的功能。
1、redis会有多组分片构成(3组)
2、redis cluster 使用固定个数的slot存储数据(一共16384slot)
3、每组分片分得1/3 slot个数(0-5500 5501-11000 11001-16383)
4、基于CRC32(key) % 16384 ====》值 (槽位号)。
==规划、搭建过程==
规划
### 6个redis实例,一般会放到3台硬件服务器
### 注:在企业规划中,一个分片的两个主从分到不同的物理机,防止硬件主机宕机造成的整个分片数据丢失。
### 端口号:7000-7005
==搭建过程==
### 1.安装集群插件
#### EPEL源安装ruby支持
yum install ruby rubygems -y
#### 使用国内源
gem sources -l
gem sources -a http://mirrors.aliyun.com/rubygems/
gem sources --remove https://rubygems.org/
gem sources -l
gem install redis -v 3.3.3
### 2.集群节点准备
mkdir /data/700{0..5}
cat > /data/7000/redis.conf <> /data/7001/redis.conf <> /data/7002/redis.conf <> /data/7003/redis.conf <> /data/7004/redis.conf <> /data/7005/redis.conf <
==集群节点管理==
增加节点
### 1.增加新节点
mkdir /data/7006
mkdir /data/7007
cat > /data/7006/redis.conf < /data/7007/redis.conf <
删除节点
### 1.将需要删除节点slot移走
redis-trib.rb reshard 127.0.0.1:7000
#### 这个交互可能要做多次重复的命令
### 2.删除一个节点
#### 删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点
redis-trib.rb del-node 127.0.0.1:7006 8ff9ef5b78e6da62bd7b362e1fe190cba19ef5ae
redis-trib.rb del-node 127.0.0.1:7007 xxxxx
redis的多api支持
python为例
yum install -y python36
python3 -V
yum install -y python36-pip
pip3 install redis
pip3 install redis-py-cluster
### ++++++++++++源码方式+++++++++++++++
#### https://redis.io/clients
#### 下载redis-py-master.zip
###安装驱动:
unzip redis-py-master.zip
cd redis-py-master
python3 setup.py install
### redis cluster的连接并操作(python2.7.2以上版本才支持redis cluster,我们选择的是3.6)
#### https://github.com/Grokzen/redis-py-cluster
###安装redis-cluser的客户端程序
cd redis-py-cluster-unstable
python3 setup.py install
### +++++++++++++++++++++++++++++++++
对redis的单实例(单节点)进行连接操作
redis-server /data/6379/redis.conf
python3
>>>import redis
>>>r = redis.StrictRedis(host='10.0.0.51', port=6379, db=0,password='123456')
>>>r.set('oldboy', 'oldguo')
>>>r.get('oldboy')
sentinel集群连接并操作
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf
redis-sentinel /data/26380/sentinel.conf &
## 导入redis sentinel包
>>>from redis.sentinel import Sentinel
##指定sentinel的地址和端口号
>>> sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1)
##测试,获取以下主库和从库的信息
>>> sentinel.discover_master('mymaster')
>>> sentinel.discover_slaves('mymaster')
配置读写分离
### 写节点
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1,password="123")
### 读节点
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1,password="123")
###读写分离测试 key
>>> master.set('oldboy', '123')
>>> slave.get('oldboy')
python连接rediscluster集群测试
python3
>>> from rediscluster import RedisCluster
>>> startup_nodes = [{"host":"127.0.0.1", "port": "7000"},{"host":"127.0.0.1", "port": "7001"},{"host":"127.0.0.1", "port": "7002"}]
### Note: decode_responses must be set to True when used with python3
>>> rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
>>> rc.set("foo", "bar")
True
>>> print(rc.get("foo"))
'bar'
一些概念
缓存穿透
概念
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。
解决方案
采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。
缓存雪崩
概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
缓存击穿
概念
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
解决方案
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。
第十七天
mongodb介绍
https://docs.mongoing.com/
逻辑结构
Mongodb 逻辑结构 MySQL逻辑结构
库database 库
集合(collection) 表
文档(document) 数据行
安装部署
系统准备
(1)redhat或centos6.2以上系统
(2)系统开发包完整
(3)ip地址和hosts文件解析正常
(4)iptables防火墙&SElinux关闭
-
(5)关闭大页内存机制
-
为什么要关闭?
Transparent Huge Pages (THP) is a Linux memory management system
that reduces the overhead of Translation Lookaside Buffer (TLB)
lookups on machines with large amounts of memory by using larger memory pages.
However, database workloads often perform poorly with THP,
because they tend to have sparse rather than contiguous memory access patterns.
You should disable THP on Linux machines to ensure best performance with MongoDB.
其他系统关闭参照官方文档:
https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/
-
### root用户下在vi /etc/rc.local最后添加如下代码
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag
mongodb安装
### 1.创建所需用户和组
useradd mongod
passwd mongod
### 2.创建所需目录
mkdir -p /mongodb/conf
mkdir -p /mongodb/log
mkdir -p /mongodb/data
### 3.上传并解压软件到指定位置
cd /data
tar xf mongodb-linux-x86_64-rhel70-3.6.12.tgz
cp -r /data/mongodb-linux-x86_64-rhel70-3.6.12/bin/ /mongodb
### 4.设置目录权限
chown -R mongod:mongod /mongodb
### 5.设置用户环境变量
su - mongod
vi .bash_profile
export PATH=/mongodb/bin:$PATH
source .bash_profile
### 6.启动mongodb
mongod --dbpath=/mongodb/data --logpath=/mongodb/log/mongodb.log --port=27017 --logappend --fork
### 7.登录mongodb
mongo
### 8.使用配置文件
#### YAML模式
#### --系统日志有关
systemLog:
destination: file
path: "/mongodb/log/mongodb.log" --日志位置
logAppend: true --日志以追加模式记录
#### --数据存储有关
storage:
journal:
enabled: true
dbPath: "/mongodb/data" --数据路径的位置
#### --进程控制
processManagement:
fork: true --后台守护进程
pidFilePath: --pid文件的位置,一般不用配置,可以去掉这行,自动生成到data中
#### --网络配置有关
net:
bindIp: -- 监听地址
port: -- 端口号,默认不配置端口号,是27017
#### --安全验证有关配置
security:
authorization: enabled --是否打开用户名密码验证
#### --以下是复制集与分片集群有关
replication:
oplogSizeMB:
replSetName: ""
secondaryIndexPrefetch: "all"
sharding:
clusterRole:
archiveMovedChunks:
---for mongos only
replication:
localPingThresholdMs:
sharding:
configDB:
---
### YAML例子
cat > /mongodb/conf/mongo.conf < /etc/systemd/system/mongod.service <
mongodb常用基本操作
默认存在的库
- test:登录时默认存在的库
- 管理MongoDB有关的系统库
- admin库:系统预留库,MongoDB系统管理库
- local库:本地预留库,存储关键日志
- config库:MongoDB配置信息库
show databases/show dbs
show tables/show collections
use admin
db/select database()
命令种类
-
db对象相关命令
db.[TAB][TAB] db.help() db.oldboy.[TAB][TAB] db.oldboy.help()
-
rs复制集有关
rs.[TAB][TAB] rs.help()
-
sh分片集群
sh.[TAB][TAB] sh.help()
mongodb对象操作
-
库的操作
> use test > db.dropDatabase() { "dropped" : "test", "ok" : 1 }
-
集合操作
db.createCollection('a') { "ok" : 1 } db.createCollection('b') ### 当插入一个文档的时候,一个集合就会自动创建。 use oldboy db.test.insert({name:"zhangsan"}) ### test是oldboy库中的一个表 db.stu.insert({id:101,name:"zhangsan",age:20,gender:"m"}) show tables; db.stu.insert({id:102,name:"lisi"}) db.stu.insert({a:"b",c:"d"}) db.stu.insert({a:1,c:2})
-
==文档操作==
### 数据录入: for(i=0;i<10000;i++){db.log.insert({"uid":i,"name":"mongodb","age":6,"date":new Date()})} ### 查询数据行数: > db.log.count() ### 全表查询: > db.log.find() ### 每页显示50条记录: > DBQuery.shellBatchSize=50; ### 按照条件查询 > db.log.find({uid:999}) ### 以标准的json格式显示数据 > db.log.find({uid:999}).pretty() { "_id" : ObjectId("5cc516e60d13144c89dead33"), "uid" : 999, "name" : "mongodb", "age" : 6, "date" : ISODate("2019-04-28T02:58:46.109Z") } ## 删除集合中所有记录 > db.log.remove({}) ### 查看集合存储信息 > db.log.totalSize() #### 集合中索引+数据压缩存储之后的大小
用户及权限管理
验证库: 建立用户时use到的库,在使用用户时,要加上验证库才能登陆。
对于管理员用户,必须在admin下创建.
- 建用户时,use到的库,就是此用户的验证库
- 登录时,必须明确指定验证库才能登录
- 通常,管理员用的验证库是admin,普通用户的验证库一般是所管理的库设置为验证库
- 如果直接登录到数据库,不进行use,默认的验证库是test,不是我们生产建议的.
- 从3.6 版本开始,不添加bindIp参数,默认不让远程登录,只能本地管理员登录。
用户创建语法
基本语法说明:
user:用户名
pwd:密码
roles:
role:角色名
db:作用对象
role:root, readWrite,read,......
use admin
db.createUser
{
user: "",
pwd: "",
roles: [
{ role: "",
db: "" } | "",
...
]
}
### 验证数据库:
mongo -u oldboy -p 123 10.0.0.53/oldboy
### 创建超级管理员:管理所有数据库(必须use admin再去创建)
$ mongo
use admin
db.createUser(
{
user: "root",
pwd: "root123",
roles: [ { role: "root", db: "admin" } ]
}
)
### 创建应用用户
use oldboy
db.createUser(
{
user: "app01",
pwd: "app01",
roles: [ { role: "readWrite" , db: "oldboy" } ]
}
)
mongo -uapp01 -papp01 app
用户管理
### 验证用户
db.auth('root','root123')
### 登录验证
mongo -uroot -proot123 admin
mongo -uroot -proot123 10.0.0.53/admin
#### 或者
mongo
use admin
db.auth('root','root123')
### 查看用户
use admin
db.system.users.find().pretty()
#### 或者
mongo -uroot -proot123 10.0.0.53/admin
db.system.users.find().pretty()
### 删除用户(root身份登录,use到验证库)
db.createUser({user: "app02",pwd: "app02",roles: [ { role: "readWrite" , db: "oldboy1" } ]})
mongo -uroot -proot123 10.0.0.53/admin
use oldboy1
db.dropUser("app02")
mongodb复制集RS(replicationset)
基本原理
基本构成是1主2从的结构,自带互相监控投票机制(==Raft(MongoDB) Paxos(mysql MGR 用的是变种)==)
如果发生主库宕机,复制集内部会进行投票选举,选择一个新的主库替代原有主库对外提供服务。同时复制集会自动通知客户端程序,主库已经发生切换了。应用就会连接到新的主库。
replicationset配置过程详解
规划
三个以上的mongodb节点(或多实例)
环境准备
28017、28018、28019、28020
### 多套目录
su - mongod
mkdir -p /mongodb/28017/conf /mongodb/28017/data /mongodb/28017/log
mkdir -p /mongodb/28018/conf /mongodb/28018/data /mongodb/28018/log
mkdir -p /mongodb/28019/conf /mongodb/28019/data /mongodb/28019/log
mkdir -p /mongodb/28020/conf /mongodb/28020/data /mongodb/28020/log
### 多套配置文件
cat > /mongodb/28017/conf/mongod.conf <
==复制集管理操作==
### 查看复制集状态
rs.status(); //查看整体复制集状态
rs.isMaster(); // 查看当前是否是主节点
rs.conf(); //查看复制集配置信息
### 添加,删除节点
rs.remove("ip:port"); // 删除一个节点
rs.add("ip:port"); // 新增从节点
rs.addArb("ip:port"); // 新增仲裁节点
#### 添加 arbiter节点
##### 1、连接到主节点
mongo --port 28018 admin
##### 2、添加仲裁节点
rs.addArb("10.0.0.53:28020")
##### 3、查看节点状态
rs.isMaster()
{
"hosts" : [
"10.0.0.53:28017",
"10.0.0.53:28018",
"10.0.0.53:28019"
],
"arbiters" : [
"10.0.0.53:28020"
],
### 查看副本集的配置信息
rs.conf()
### 查看副本集各成员的状态
rs.status()
特殊从节点
![](http://img.e-com-net.com/image/info10/48e188cb61954a069d0fe270a9e7524d.jpg)
![](http://img.e-com-net.com/image/info10/f7133ce2cee24cbe8e29239cef9b518c.jpg)
介绍
arbiter节点:主要负责选主过程中的投票,但是不存储任何数据,也不提供任何服务
hidden节点:隐藏节点,不参与选主,也不对外提供服务。
delay节点:延时节点,数据落后于主库一段时间,因为数据是延时的,也不应该提供服务或参与选主,所以通常会配合hidden(隐藏)
一般情况下会将delay+hidden一起配置使用
### 配置延时节点
cfg=rs.conf()
cfg.members[2].priority=0
cfg.members[2].hidden=true
cfg.members[2].slaveDelay=120
cfg.members[2].votes=0
rs.reconfig(cfg)
### 取消以上配置
cfg=rs.conf()
cfg.members[2].priority=1
cfg.members[2].hidden=false
cfg.members[2].slaveDelay=0
cfg.members[2].votes=1
rs.reconfig(cfg)
### 配置成功后,通过以下命令查询配置后的属性
rs.conf()
mongodb sharding cluster分片集群
规划
10个实例:38017-38026
(1)configserver:38018-38020
3台构成的复制集(1主两从,不支持arbiter)38018-38020(复制集名字configsvr)
(2)shard节点:
sh1:38021-23 (1主两从,其中一个节点为arbiter,复制集名字sh1)
sh2:38024-26 (1主两从,其中一个节点为arbiter,复制集名字sh2)
(3) mongos:38017
shard节点配置过程
### 目录创建
mkdir -p /mongodb/38021/conf /mongodb/38021/log /mongodb/38021/data
mkdir -p /mongodb/38022/conf /mongodb/38022/log /mongodb/38022/data
mkdir -p /mongodb/38023/conf /mongodb/38023/log /mongodb/38023/data
mkdir -p /mongodb/38024/conf /mongodb/38024/log /mongodb/38024/data
mkdir -p /mongodb/38025/conf /mongodb/38025/log /mongodb/38025/data
mkdir -p /mongodb/38026/conf /mongodb/38026/log /mongodb/38026/data
### 配置文件
#### 第一组复制集:21-23
cat > /mongodb/38021/conf/mongodb.conf < /mongodb/38024/conf/mongodb.conf <
config节点配置
注:configserver 可以是一个节点,官方建议复制集。configserver不能有arbiter。
新版本中,要求必须是复制集。
注:mongodb 3.4之后,虽然要求config server为replica set,但是不支持arbiter
### 目录创建
mkdir -p /mongodb/38018/conf /mongodb/38018/log /mongodb/38018/data
mkdir -p /mongodb/38019/conf /mongodb/38019/log /mongodb/38019/data
mkdir -p /mongodb/38020/conf /mongodb/38020/log /mongodb/38020/data
### 配置文件
cat > /mongodb/38018/conf/mongodb.conf <
mongos节点配置
无状态的
mkdir -p /mongodb/38017/conf /mongodb/38017/log
cat > /mongodb/38017/conf/mongos.conf <
分片集群添加节点
### 连接到其中一个mongos(10.0.0.51),做以下配置
#### (1)连接到mongs的admin数据库
su - mongod
mongo 10.0.0.51:38017/admin
#### (2)添加分片
db.runCommand( { addshard : "sh1/10.0.0.51:38021,10.0.0.51:38022,10.0.0.51:38023",name:"shard1"} )
db.runCommand( { addshard : "sh2/10.0.0.51:38024,10.0.0.51:38025,10.0.0.51:38026",name:"shard2"} )
#### (3)列出分片
db.runCommand( { listshards : 1 } )
#### (4)整体状态查看
sh.status();
==mongodb使用分片集群==
range分片配置及测试
### 激活数据库分片功能
mongo --port 38017 admin
admin> ( { enablesharding : "数据库名称" } )
#### eg:
admin> db.runCommand( { enablesharding : "test" } )
### 指定分片键对集合分片
#### 创建索引
use test
> db.vast.ensureIndex( { id: 1 } )
#### 开启分片
use admin
> db.runCommand( { shardcollection : "test.vast",key : {id: 1} } )
### 集合分片验证
use test
test> for(i=1;i<1000000;i++){ db.vast.insert({"id":i,"name":"shenzheng","age":70,"date":new Date()}); }
test> db.vast.stats()
### 分片结果测试
#### shard1:
mongo --port 38021
db.vast.count();
#### shard2:
mongo --port 38024
db.vast.count();
==hash分片==
###(1)对于oldboy开启分片功能
mongo --port 38017 admin
use admin
admin> db.runCommand( { enablesharding : "oldboy" } )
###(2)对于oldboy库下的vast表建立hash索引
use oldboy
oldboy> db.vast.ensureIndex( { id: "hashed" } )
###(3)开启分片
use admin
admin > sh.shardCollection( "oldboy.vast", { id: "hashed" } )
###(4)录入10w行数据测试
use oldboy
for(i=1;i<100000;i++){ db.vast.insert({"id":i,"name":"shenzheng","age":70,"date":new Date()}); }
###(5)hash分片结果测试
mongo --port 38021
use oldboy
db.vast.count();
mongo --port 38024
use oldboy
db.vast.count();
mongodb分片集群的查询及管理
### 判断是否shard集群
db.runCommand({ isdbgrid : 1})
### 列出所有分片信息
db.runCommand({ listshards : 1})
### 列出开启分片的数据库
use config
db.databases.find( { "partitioned": true } )
#### 或者:
db.databases.find() //列出所有数据库分片情况
### 查看分片的片键
db.collections.find().pretty()
{
"_id" : "test.vast",
"lastmodEpoch" : ObjectId("58a599f19c898bbfb818b63c"),
"lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
"dropped" : false,
"key" : {
"id" : 1
},
"unique" : false
}
### 查看分片的详细信息
sh.status()
### 删除分片节点
####(1)确认blance是否在工作
sh.getBalancerState()
####(2)删除shard2节点(谨慎)
db.runCommand( { removeShard: "shard2" } )
#### 注意:删除操作一定会立即触发blancer。
balancer操作
介绍
mongos的一个重要功能,自动巡查所有shard节点上的chunk的情况,自动做chunk迁移。
什么时候工作?
1、自动运行,会检测系统不繁忙的时候做迁移
2、在做节点删除的时候,立即开始迁移工作
3、balancer只能在预设定的时间窗口内运行有需要时可以关闭和开启blancer(备份的时候)
mongos> sh.stopBalancer()
mongos> sh.startBalancer()
自定义自动平衡的时间段
https://docs.mongodb.com/manual/tutorial/manage-sharded-cluster-balancer/#schedule-the-balancing-window
### connect to mongos
use config
sh.setBalancerState( true )
db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "3:00", stop : "5:00" } } }, true )
sh.getBalancerWindow()
sh.status()
### 关于集合的balancer(了解下)
#### 关闭某个集合的balance
sh.disableBalancing("students.grades")
#### 打开某个集合的balancer
sh.enableBalancing("students.grades")
#### 确定某个集合的balance是开启或者关闭
db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance;
第十八天
mongodb的备份恢复工具
工具介绍
- mongoexport、mongoimport
- mongodump、mongorestore
应用场景总结:
mongoexport/mongoimport:==json csv==
1、异构平台迁移 mysql <---> mongodb
2、同平台,跨大版本:mongodb 2 ----> mongodb 3
mongodump/mongorestore:日常备份恢复时使用.
导出工具mongoexport
- 参数说明
- -h:指明数据库宿主机的IP
- -u:指明数据库的用户名
- -p:指明数据库的密码
- -d:指明数据库的名字
- -c:指明collection的名字
- -f:指明要导出那些列
- -o:指明到要导出的文件名
- -q:指明导出数据的过滤条件
- --authenticationDatabase admin
### 1.单表备份至json格式
mongoexport -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy -c log -o /mongodb/log.json
#### 注:备份文件的名字可以自定义,默认导出了JSON格式的数据。
### 2. 单表备份至csv格式
#### 如果我们需要导出CSV格式的数据,则需要使用----type=csv参数:
mongoexport -uroot -proot123 --port 27017 --authenticationDatabase admin -d test -c log --type=csv -f uid,name,age,date -o /mongodb/log.csv
导入工具mongoimport
- 参数说明
- -h:指明数据库宿主机的IP
- -u:指明数据库的用户名
- -p:指明数据库的密码
- -d:指明数据库的名字
- -c:指明collection的名字
- -f:指明要导入那些列
- -j, --numInsertionWorkers=
number of insert operations to run concurrently (defaults to 1)
### 1.恢复json格式表数据到log1
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy -c log1 /mongodb/log.json
### 2.恢复csv格式的文件到log2
####上面演示的是导入JSON格式的文件中的内容,如果要导入CSV格式文件中的内容,则需要通过--type参数指定导入格式,具体如下所示:
####(1)csv格式的文件头行,有列名字
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy -c log2 --type=csv --headerline --file /mongodb/log.csv
####(2)csv格式的文件头行,没有列名字
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy -c log3 --type=csv -f id,name,age,date --file /mongodb/log.csv
#### --headerline:指明第一行是列名,不需要导入。
mongodump、mongorestore
- 参数说明
- -h:指明数据库宿主机的IP
- -u:指明数据库的用户名
- -p:指明数据库的密码
- -d:指明数据库的名字
- -c:指明collection的名字
- -o:指明到要导出的文件名
- -q:指明导出数据的过滤条件
- -j, --numParallelCollections= number of collections to dump in parallel (4 by default)
- --oplog 备份的同时备份oplog
- use oplog for taking a point-in-time snapshot
- 这是replica set或者master/slave模式专用
### 全库备份
mkdir -p /mongodb/backup
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -o /mongodb/backup
### 备份某个库
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d world -o /mongodb/backup/world
### 备份某个集合
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy -c log -o /mongodb/backup/oldboy-log
### 压缩备份
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldguo -o /mongodb/backup/oldguo --gzip
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -o /mongodb/backup/ --gzip
mongodump -uroot -proot123 --port 27017 --authenticationDatabase admin -d app -c vast -o /mongodb/backup/app-vast --gzip
### 恢复某库
mongorestore -uroot -proot123 --port 27017 --authenticationDatabase admin -d world1 /mongodb/backup/world
### 恢复某集合
mongorestore -uroot -proot123 --port 27017 --authenticationDatabase admin -d world -c t1 --gzip /mongodb/backup/oldboy-log/table-name.bson.gz
mongorestore -uroot -proot123 --port 27017 --authenticationDatabase admin -d oldboy --drop /mongodb/backup/oldboy-log/table-name.bson
#### drop表示恢复的时候把之前的集合drop掉(危险)
==mongodump和mongorestore高级企业应用(--oplog)==
oplog的说明
在replica set中oplog是一个定容集合(capped collection),它的默认大小是磁盘空间的5%(可以通过--oplogSizeMB参数修改).
位于local库的db.oplog.rs,有兴趣可以看看里面到底有些什么内容。
其中记录的是整个mongod实例一段时间内数据库的所有变更(插入/更新/删除)操作。
当空间用完时新记录自动覆盖最老的记录。
其覆盖范围被称作oplog时间窗口。需要注意的是,因为oplog是一个定容集合,所以时间窗口能覆盖的范围会因为你单位时间内的更新次数不同而变化。
想要查看当前的oplog时间窗口预计值,可以使用以下命令:
use local
==db.oplog.rs.find().pretty()==
==rs.printReplicationInfo()==
###(1)实现热备,在备份时使用--oplog选项
#### 注:为了演示效果我们在备份过程,模拟数据插入
###(2)准备测试数据
mongo --port 28018
use oldboy
for(var i = 1 ;i < 100; i++) {
db.foo.insert({a:i});
}
db.oplog.rs.find({"op":"i"}).pretty()
#### oplog 配合mongodump实现热备
mongodump --port 28018 --oplog -o /mongodb/backup
##### 作用介绍:--oplog 会记录备份过程中的数据变化。会以oplog.bson保存下来
### 恢复
mongorestore --port 28018 --oplogReplay /mongodb/backup
背景:每天0点全备,oplog恢复窗口为48小时
某天,上午10点world.city 业务表被误删除。
恢复思路:
0、停应用
2、找测试库
3、恢复昨天晚上全备
4、截取全备之后到world.city误删除时间点的oplog,并恢复到测试库
5、将误删除表导出,恢复到生产库恢复步骤:
模拟故障环境:
### 1、全备数据库
#### 模拟原始数据
mongo --port 28017
use wo
for(var i = 1 ;i < 20; i++) {
db.ci.insert({a: i});
}
#### 全备:
rm -rf /mongodb/backup/*
mongodump --port 28017 --oplog -o /mongodb/backup
##### --oplog功能:在备份同时,将备份过程中产生的日志进行备份
##### 文件必须存放在/mongodb/backup下,自动命令为oplog.bson
#### 再次模拟数据
db.ci1.insert({id:1})
db.ci2.insert({id:2})
### 2、上午10点:删除wo库下的ci表
#### 10:00时刻,误删除
db.ci.drop()
show tables;
### 3、备份现有的oplog.rs表
mongodump --port 28017 -d local -c oplog.rs -o /mongodb/backup
### 4、截取oplog并恢复到drop之前的位置
#### 更合理的方法:登陆到原数据库
mongo --port 28017
my_repl:PRIMARY> use local
db.oplog.rs.find({op:"c"}).pretty();
{
"ts" : Timestamp(1553659908, 1),
"t" : NumberLong(2),
"h" : NumberLong("-7439981700218302504"),
"v" : 2,
"op" : "c",
"ns" : "wo.$cmd",
"ui" : UUID("db70fa45-edde-4945-ade3-747224745725"),
"wall" : ISODate("2019-03-27T04:11:48.890Z"),
"o" : {
"drop" : "ci"
}
}
#### 获取到oplog误删除时间点位置:
"ts" : Timestamp(1553659908, 1)
### 5、恢复备份+应用oplog
cd /mongodb/backup/local/
ls
oplog.rs.bson oplog.rs.metadata.json
cp oplog.rs.bson ../oplog.bson
rm -rf /mongodb/backup/local/
mongorestore --port 28017 --oplogReplay --oplogLimit "1553659908:1" --drop /mongodb/backup/
异构平台迁移案列
### mysql -----> mongodb
### world数据库下city表进行导出,导入到mongodb
###(1)mysql开启安全路径
vim /etc/my.cnf --->添加以下配置
secure-file-priv=/tmp
### --重启数据库生效
/etc/init.d/mysqld restart
###(2)导出mysql的city表数据
source /root/world.sql
select * from world.city into outfile '/tmp/city1.csv' fields terminated by ',';
###(3)处理备份文件
desc world.city
ID | int(11) | NO | PRI | NULL | auto_increment |
| Name | char(35) | NO | | | |
| CountryCode | char(3) | NO | MUL | | |
| District | char(20) | NO | | | |
| Population
vim /tmp/city.csv ----> 添加第一行列名信息
ID,Name,CountryCode,District,Population
### (4)在mongodb中导入备份
mongoimport -uroot -proot123 --port 27017 --authenticationDatabase admin -d world -c city --type=csv -f ID,Name,CountryCode,District,Population --file /tmp/city1.csv
use world
db.city.find({CountryCode:"CHN"});
-------------
### world共100张表,全部迁移到mongodb
select table_name ,group_concat(column_name) from columns where table_schema='world' group by table_name;
select * from world.city into outfile '/tmp/world_city.csv' fields terminated by ',';
select concat("select * from ",table_schema,".",table_name ," into outfile '/tmp/",table_schema,"_",table_name,".csv' fields terminated by ',';")
from information_schema.tables where table_schema ='world';
###导入:
####提示,使用infomation_schema.columns + information_schema.tables
### mysql导出csv:
select * from test_info
into outfile '/tmp/test.csv'
fields terminated by ',' ------字段间以,号分隔
optionally enclosed by '"' ------字段用"号括起
escaped by '"' ------字段中使用的转义符为"
lines terminated by '\r\n'; ------行以\r\n结束
### mysql导入csv:
load data infile '/tmp/test.csv'
into table test_info
fields terminated by ','
optionally enclosed by '"'
escaped by '"'
lines terminated by '\r\n';
分片集群的备份思路
1、你要备份什么?
config server
shard 节点
单独进行备份
2、备份有什么困难和问题
(1)chunk迁移的问题
人为控制在备份的时候,避开迁移的时间窗口
(2)shard节点之间的数据不在同一时间点。
选业务量较少的时候
Ops Manager
第十九天
mongodb的问题集---1
为什么副本集(复制集)说是5个9的高可用?
MongoDB复制集在从节点故障时候是不会影响到可用性。
在主节点故障,进行选举的时候需要数秒到十几秒,这期间会影响写入。一年有365天x86640秒 ~= 3000多万秒。假设你每个月发生一次主节点故障或者其他问题导致选举,每次影响15秒,那么可用率就是:就是(3000w-12x15) / 3000w ~= 99.9994%
MongoDB这种复制集模式,在不同的数据中心,这中间的网络延迟比较严重吧,不会影响做复制集的效果吗?
另外一个,数据规模达到多大级别需要使用分片机制?
在多中心部署的时候要考虑网络延迟,所以一般多活中心只是建议能够接受一定数据延迟的情况下才建议。
分片有3个触发条件:数据量,并发量,以及热数据大小(内存需求)。理论上,任意一个都会触发分片需求。如果只看数据量,单分片一般可以到1-2TB。
要是想缓存文件,如pdf或者word应该怎么办,看到mongodb的文件是json
GridFS 了解一下。
mangodb有什么推荐的客户端软件吗
MongoDB Compass(官方)
Studio3T
NoSQL Booster
复杂的聚合查询 一定要尽可能把所有的match放在在project投影之前,如果match出现在project之后 就无法享受索引加速了。
当然 还需要根据match字段 创建对应的联合索引。 我刚刚把一个复杂查询优化了 就是把几个match提前了 然后建索引,查询耗时从5秒降到1秒
重要例子-1
我们业务场景就遇到了聚合查询的情况,很简单,但是对于小白也很费脑筋。作为网关流量统计需求,接口需要给前端返回每日00:00:00 ~ 23:59:59这一天内,字段messageType分别为01 02 03 04 ,四种业务意义的请求数量,请老师明示怎么动态取每天的date,然后返回的结果分条显示当天24小时之内,不同值messageType得请求数量count(messageType)。
- $dateToString
- $year
- $month
- $dayOfMonth
### 测试数据:
db.test.insertMany([
{
messageType: '01',
date: ISODate("2019-01-01T01:15:42Z")
// 其他字段
},
{
messageType: '01',
date: ISODate("2019-01-01T01:17:42Z")
},
{
messageType: '02',
date: ISODate("2019-01-17T01:17:42Z")
},
{
messageType: '02',
date: ISODate("2019-01-17T02:17:42Z")
},
])
### 解决思路
#### 从日期中提取日期和小时。这里如果有必要,需要考虑时区问题。如果考虑时区问题,则需要3.6以上版本支持;
#### 按日期、小时分组聚合;
### 聚合语句
db.test.aggregate([
// 过滤条件(如果有)
{
$match: {
date: {
$gte: ISODate("2019-01-01T00:00:00Z"),
$lt: ISODate("2019-02-01T00:00:00Z"),
}
}
},
// 映射出日期和小时
{
$project: {
date: {
$dateToString: {
date: "$date",
format: "%Y-%m-%d",
timezone: "+08:00"
}
},
hour: {
$hour: "$date"
},
messageType: 1
}
},
{
// 按日期、小时、messageType分组计数
$group: {
_id: {
date: "$date",
hour: "$hour",
messageType: "$messageType"
},
count: {
$sum: 1
}
}
}
]);
如果要在 10 亿数据中查找摘要包含 MongoDB 的记录,{summary:/MongoDB/},要求 1000 毫秒内返回结果,应该怎么玩?一定要单机配置很好的集群吗?单机配置一般,个数可以无限增加可以玩吗?分片是为了解决这类问题吗?
这里面最关键的是内存大小。
考虑为summary建一个covered index,然后保证你的索引能够装在内存里 (所有索引大小加起来要小于物理内存的一半那样)。
如果你用分片的话,比如说4个分片,那每个分片只需要处理2.5亿(在2.5亿里寻找,4个分片同时进行),理论上肯定更快了。但是最终还是要看你总的内存的数量。
三个小问题
第一个问题:关于复制集这块的secondary是怎么从主库来来取oplog的,实现的细节是什么?想了解这块的出发点就是作为dba的话,会经常遇到一些生产问题,需要从原理上解决问题?
第二个问题:在local下,有几个关于副本集的集合,这几个集合是干什么用的,排查问题的时候,这几个表有什么作用?
第三个问题: 我有一个副本集,现在对一个secondary进行了物理的热备,之后,想把这个节点加入现有集群,是只需要配置参数文件,add加入副本集就可以吗?如果可以加入副本集的话,他是通过哪个集合来判断从哪个时间点开始来取oplog的?
第一个问题你可以先看下这篇博客:
https://www.cnblogs.com/Joans/p/7723554.html
如果进一步了解的话,源码也可以尝试阅读下第二个问题也在上面的文章里有提及
第三个问题:可以的,但是要通过对local库下面的一些表做个手脚。如果你用MongoDB的Ops Manager的话,它就提供这种方法来快速恢复一个从节点。
https://docs.opsmanager.mongodb.com/v1.4/tutorial/use-restore-to-seed-secondary/
其中关键的集合是system.replset 以及oplog.rs。 system.replset必须包含对应的配置(和其他节点一致)和时间戳。oplog.rs必须包含至少一条和主节点oplog一样的记录用来匹配同步点。
设计模式集锦
- 列转行
- 场景 多国家 多语言 多属性
- 文档很多类似字段
- 转化为数组,一个索引解决所有问题
- 版本字段
- 场景 任何有多个衍生版本的数据库
- 文档模型格式多 升级的时候需要更新多个文档
- 增加一个版本号字段 快速过滤不需要升级的字段 升级的时候对不同版本的文档做不同的处理
- 近似计算
- 网页计数 各种不需要精确计算的排名
- 写入太频繁 消耗系统资源
- 间隔写入 每次隔10次写入一次 可大量减少写入需求
- 预聚合
- 场景 精确排名 排行榜
- 统计时间消耗 时间长
- 模型中添加统计字段 每次更新同时更新统计值
- 分桶
- 场景 时序数据 物联网 智慧城市 智慧交通
- 数据点采集频繁,数据量太多
- 利用文档内嵌数组,将一个时间段的数据聚合到一个文档里 可大量减少文档数量 大量减少索引占用空间
有个内嵌文档的唯一性问题请教,因为是唯一性的组合索,保证的是内嵌文档跨文档唯一,但我需要的是跨文档可以重复,但当前文档内内嵌文档唯一(比如每个客户有多个联系地址,地址的姓名/电话不能重复),MONGODB支持这种索引吗?还是说需要业务代码来实现?
这个很容易实现。比如说你是这样的文档:
{
_id: ObjectId(),
customer_id: 101,
customer_name: "Nina",
emergency_contacts: [
{ contact_name: 'Nina mom', contact_phone: 12345},
{ contact_name: 'Nina dad', contact_phone:12346}
]
}你可以建一个这样的唯一索引:
{ customer_id: 1, "emergency_contacts.contact_name":1,
"emergency_contacts.contact_phone":1}
mongodb的事务
写事务
- ==writeConcern==
- ==w:"majority"==
### 在复制集测试writeConcern参数
db.test.insert( {count: 1}, {writeConcern: {w: "majority"}})
db.test.insert( {count: 1}, {writeConcern: {w: 3 }})
db.test.insert( {count: 1}, {writeConcern: {w: 4 }})
### 配置延迟节点,模拟网络延迟(复制延迟)
conf=rs.conf()
conf.members[2].slaveDelay = 5
conf.members[2].priority = 0
rs.reconfig(conf)
### 观察复制延迟下的写入,以及timeout参数
db.test.insert( {count: 1}, {writeConcern: {w: 3}})
db.test.insert( {count: 1}, {writeConcern: {w: 3, wtimeout:3000 }})
读事务
- ==readPreference==
- primary
- primaryPreferred
- secondary
- secondaryPreferred
- nearest
- ==Tag==
- ==readConcern==
- available
- local
- majority
- linearizable
- snapshot
### 通过 MongoDB 的连接串参数:
mongodb://host1:27107,host2:27107,host3:27017/?replicaSet=rs&readPre
ference=secondary
### 通过 MongoDB 驱动程序 API: MongoCollection.withReadPreference(ReadPreference readPref)
### Mongo Shell:
db.collection.find({}).readPref( “secondary” )
### 主节点写入 {x:1}, 观察该条数据在各个节点均可见
#### 在两个从节点分别执行 db.fsyncLock() 来锁定写入(同步)
#### 主节点写入 {x:2}
db.test.find({a: 123})
db.test.find({a: 123}).readPref(“secondary”)
### 解除从节点锁定 db.fsyncUnlock()
db.test.find({a: 123}).readPref(“secondary”)
如何实现安全的读写分离
==使用 writeConcern + readConcern majority 来解决==
### 注意配置文件内 replication字段 参数 enableMajorityReadConcern
#### 这个参数一定要true
db.orders.insert({ oid: 101, sku: "kiteboar", q: 1}, {writeConcern:{w: "majority”}})
db.orders.find({oid:101}).readPref(“secondary”).readConcern("majority")
mongodb Change Stream 使用场景
跨集群的变更复制——在源集群中订阅 Change Stream,一旦得到任何变更立即写入目标集群。
微服务联动——当一个微服务变更数据库时,其他微服务得到通知并做出相应的变更。
其他任何需要系统联动的场景
注意事项
- Change Stream 依赖于 oplog,因此中断时间不可超过 oplog 回收的最大时间窗;
- 在执行 update 操作时,如果只更新了部分数据,那么 Change Stream 通知的也是增量部分;
- 同理,删除数据时通知的仅是删除数据的 _id。
mongodb 分片问题集---2
数据量不是很大,如果要支撑百万并发,一般要如何设计,大概要多少个节点,用复制集还是分片集?
官方的建议不管读还是写,都用分片来解决。百万级的并发算是很大了,如果是点查为主的读操作占绝大多数,那么一个节点理论上支撑10万以上并发是可以的,当然必须是那种32/64 核高CPU的物理服务器。 Oppo的同学分享过单集群支撑100万+并发,该集群由14个分片组成,14x3共42台机器
我的分片集群有 2个shard, 集合shard key 使用 _id:"hashed" ,写入数据能正常分布到2个shard 上去,当使用 shard key 使用{user_id:1,time:1} 时,数据始终分布在一个 分片上面,无论我怎么调整插入 user_id 和 time 的值,请教老师,这是为什么?
开始的时候你都是写入到同一个chunk,只有到了一定的量以后,超过chunk的阈值,chunk 会拆分变成2个或者更多,然后chunk会迁移到另一个分片。这个时候你的写入就会分布了。
老师,是整个数据库超过2T还是单个集合超过2T的时候需要分片? 我设想 如果我的项目需要分片的时候,大概率是某一个集合太大了,这时是不是只需要对这个集合设置分片键?其它小集合怎么处理呢 不做任何设置 它们会被mongodb怎么安排?
分片是基于单表的。
不分片的集合默认留在主分片上。你也可以通过movePrimary 命令调整主分片所在位置。
是否可以理解为,userId + time, 同一个userId都在同一个分片,time使其在同一个分片分散在不同的块上。
如果同一个userid下面的行数很多的时候,使用time可以将数据分布到多个块上。
email系统以user_id作为片键,为什么会造成超级快?难道chunk达到一定的大小后,不会分裂吗?不会生成新的chunk?
chunk的是以片键值的upper bound 和 lower bound范围决定的。如果user_id是片键,那么对于user_id:1000,在他的email数量达到一定程度,不断分块后最后的就是:
user_id >= 1000(包含1000)
user_id < 1001( 不包含)到了这一步,这一块里面只有他自己一个人的数据了。如果继续增长,你已经无法只用user_id这一个字段作为范围键来定义多余一个的chunk了。这时候就会早产超级块
哈希分配方式,如果刚开始是4个shard, 如果变成8个shard,是不是还需要再分配。
哈希分配最开始的时候你就不需要做分配,只是指定哈希这种方式。4个到8个,方式没变,就不需要做任何事情。mongo会处理。
我们生产使用的是分片模式,但是有一个集合没有建片键,这个集合现在太大了占了7.4TB,
1:现在怎么建片键,可以建吗?
2:建了片键之后会不会影响之前的数据查询
1)可以,直接用shardCollection就可以
2)不会。数据均衡可能会花很长时间(数天),这是正常的。
没有分片的集合是会随机找个shard 复制集存吗?
mongos在你新建库的时候会为你的库挑一个“primary shard”,所有未分片的集合都会在这个shard里面。挑选的规则就是看哪个分片相对数据量小一点。
有个问题,就是有的库我不分片,目前我有2个分片集(A和B),然后我创建一个新数据库,怎么指定其在分片集B上面
==db.adminCommand( { movePrimary : "your_dbname", to : "shard0001" } )==
### 命中片键的时候会读相应的分片服务器,
### 但是如何确定哪个分片?
就是通过chunk - 分片 的对应表(存储在config server里并且缓存到mongos)就可以得知。
1、如果两个分片服务器性能不一样,访问和存储可以权重类的设置吗?
2、加入新的分片后,集群里的分片数据会自动均衡到各分片吗?比如首先只有第一个分片有30G数据,后面新增两个分片,各分片会迁移10G数据吗?
1)可以使用zone sharding方式来自己控制数据的分布。比如说分片1是16Core SSD,那你可以给它更多的数据。
2)是的,最后3个分片会给有10G左右
分片集群下的副本集只有2台机器,一次意外断电导致其中一台系统启动不了,这个副本集也就坏了,如果强制把它从分片集群理彻底删除,现在一直处理【"draining" : true】。
应该先修复副本集再做删除动作,可以删掉坏掉的那个节点的数据,让它自动恢复数据。
mongodb问题集---2
ticket默认是128,一般调高这个值的时候需要参考哪些指标?比如物理机内存?cpu核数?节点数?
ticket一般只是个标志,如果发现用光,是因为你的物理资源不够,主要是IOPS,少数时候CPU。这个时候不是调高tickt,而是优化硬件资源。
最近做了个前端监控分析的系统,使用了MongoDB作为数据库,现在每天的存储记录达到300W+条,查询返回百万级数据这块不知道怎么优化,老师有什么建议吗?
需要对这些数据做==pre-aggregation==,按照时间颗粒度,获得分钟级平均数,小时级平均数,把他们事先存到库里,给前段展现用。这样的话你就不需要返回大量的原始数据再在应用里做计算了。
听完写入操作的过程,有点好奇,oplog和journal日志能不能合并呢,我的理解的话就是oplog是个定容的集合,存放的记录都是幂等操作,用于mongodb的复制集模式,从主节点复制到其他slave节点保证数据的一致,journal日志是用于mongodb crash之后恢复的一个日志。那crash恢复能不能也用oplog呢?
oplog记录的是对数据库的逻辑操作, 在MongoDB里面用一个固定大小的普通集合来记录,和其他的数据一样,默认增删都是在内存里发生。
journal 和其他数据库类似,采用WriteAheadLog机制,用来提供对数据库写入操作的一致性和持久性保证。它记录的是要对数据物理区块修改的一些动作。
Journal 日志和Oplog理论上都可以用来恢复,但是journal比较底层,直接操作存储区,写入和恢复效率要比oplog 日志高,所以通常数据库都会采用专门的journal来做crash recovery.
有描述到有一个企业从1.x升级到4.X,影片时间落在9:48的位置,透过滚动服务升级,透过replica set升级的升级。未来是否有课程会讨论到这一块,我想尝试不下线的方式,从4.2升级到4.22
4.2 版本的驱动程序会自动在换主节点的时候重试读写操作。假设你有三台复制集群节点 A,B,C, A为主节点。你只需要执行以下操作,你的程序应该可以持续运行
确定客户端应用使用的是最新版驱动程序
- 停止C的mongo服务,升级C的mongodb binary文件
- 重启C服务,等待集群稳定
- 停止B的mongo服务,升级B的mongodb binary文件
- 重启B服务,等待集群稳定
- 停止A,升级A的mongodb binary文件
- 重启A,等待集群稳定https://mongoing.com/docs/release-notes/3.0-upgrade.html
Retrywrite 只会重试一次吗?如果重试第二次时新的primary还没被选举出来,是不是会写失败?
只会重试一次, 会一直等到primary选举成功才会试。如果30秒还没选出来,这个写操作就失败了。
30秒选不出来,这个大概率集群出故障了。
MongoDB 压测有什么好的工具和方法实践吗?
我们一般用 POCDriver / YCSB来做压测。监控的话用Ops Manager,可以整体看压测过程中系统性能表现
例mongo看结构就是用了子文档,那么传统数据库就是用子表,我不理解传统数据库为何会失败。第二个案例,百亿数据量,传统数据库肯定不适合,这无需多说,而业务都是只读的,查询消费记录,那为何不选redis呢?应该跟redis比啊。或者es啊,这几个分片集群都很方便。
选型是重要的决策,这里面考虑的,不仅仅是技术一个因素。人才储备,技术栈简洁性,迁移成本等都会是考量。但是我就技术层面回复一下你
关于第一个案例,你要关注没有注意到的两个点
1) 子文档模型的灵活性。里面不同的保单子文档,可以有不相同的结构。这种异构建模,在关系型库里是比较困难的,你在设计的时候,难以对几十个不同系统的模型进行统一抽象,特别是没有哪个架构师对所有系统都熟悉,都是需要各个系统各自出一个人来参与设计的时候。
2) 假设一个顶级的架构师可能真的可以用子表设计出来,这样复杂的系统,一个保单表少说都有可能是几十上百张表的节奏。想象下开发者的体验?他们能很容易明白这么复杂的数据关系?想象下多表关联下数亿数十亿的性能?第二个案例:
基本定位:
- Redis 的基本定位一个缓存
- MongoDB的基本定位是一个OLTP数据库性能:
- Redis 强在读
- MongoDB 在给够内存的情况下,可以和Redis不相上下,特别是简单缓存读取
- 如果Redis 采取和MongoDB接近的持久化策略,它的性能会一落千丈,都比不过RDMBS(根据它自己的官网)功能
- Mongo的优势: 子文档更新能力,数组更新能力
- Mongo的优势:聚合分析Aggregation能力集群能力
- Redis 在集群模式下无法提供强一致(来自官网)
- MongoDB在无论是在副本集还是分片集都能提供强一致
- Redis 集群模式下不支持Multiple Key 操作、事务操作有人会说如果是只读场景,更新能力可以忽略。但是他们忽略的是,缓存的数据也是需要实时去更新的。如果你只能更新整个文档,以及对更新的一致性保障不好,这些也会是技术选型中需要考虑到的重要因素。
Spark+MongoDB比Spark+HBase的架构有哪些优点
HBase 和 MongoDB在这种和Spark场景下是比较类似的,可以互换。如果数据仅仅是用来做spark 分析计算,HBase可能还有一些性能上的优势。
MongoDB的好处是,你很可能已经用MongoDB在存储业务产生的数据,当你需要再做分析的时候不需要再把数据导到大数据平台(HDFS/Hbase)一份。你可以直接加上spark计算框架就可以完成分析了。节省存储,节省人力。
直观简要的说一下对 spark 和 flink 的感受
flink 是一个专门的流处理计算软件,关注对“现在产生的数据”。
spark 除了能做流处理,更多的是批处理能力,对“现在和以前的数据”做回顾式计算和分析。flink 和 spark 的一个模块,spark stream更有可比性。
flink的实时能力更强一些,颗粒度是一条数据。spark stream是基于微批,所以颗粒度粗一点。
MongoDB 和 Elasticsearch 对比的主要应用场景和对比如何?
ES和mongo相比有一个比较关键的点就是实时更新能力。
ES一般不建议做实时更新,因为索引更新耗时很长。
所以如果你需要实时更新某些数据并且马上查询,ES会有索引不及时更新的情况导致数据不够一致。
第二十天
mysql优化---系统层
cpu
-
top
### 统计类 0.0 us #### 计算 用户程序,在运行过程中,使用的CPU时间片的占比。 我们希望的是越高越好,尽量控制在90% 0.0 sy #### 调度 ##### 锁和并发高 控制: 资源管理, 内核的工作(系统调用) sys高的原因: 1. bug ,中病毒了 2. 锁的问题 3. 并发很高 99.9 id 空闲的CPU时间片占比 0.0 wa #### 等待 ##### 锁、io、大事务、索引 CPU花在等待上的时间片占比 wa高的原因: 1. 锁 2. IO (raid,过度条带化) 3. 索引 4、等待大的处理事件(大事务) ### 多cpu使用情况监控: 主要判断我们cpu多核心有没有被充分利用。 现象:单颗很忙,其他很闲,对于MySQL来讲,有可能是并发参数设定不合理导致的。
mem、swap
-
top
KiB Mem : 4028432 total, 3774464 free, 116240 used, 137728 buff/cache total :总内存大小 free :空闲的 used :在使用的 buff/cache :缓冲区 和 缓存 --- 对于page cache来讲(OS buffer) 1. 内存的可用空间的计算 free +buffer cache 2. 内存回收(buffer)的方式: (1) 写入磁盘 (2) swap --- KiB Swap: 2097148 total, 2097148 free, 0 used. 3701464 avail Mem Linux 6操作系统,默认回收策略(buffer cache),不立即回收策略 cat /proc/sys/vm/swappiness 30 ### mysql尽然使用0 swap echo 0 >/proc/sys/vm/swappiness 的内容改成0(临时) vim /etc/sysctl.conf 添加: vm.swappiness=0 sysctl -p
io
-
iostat
dd if=/dev/zero of=/tmp/bigfile bs=1M count=4096 iostat -dm 1 iostat -dk 2 ### 现象说明 1. IO 高 cpu us 也高,属于正常现象 #### 正比 2. CPU us高 IO很低 ,MySQL 不在做增删改查,有可能是存储过程,函数,排序,分组,多表连接 3. Wait 高 , IO低:IO出问题了(raid ,过度条带化) 4. sys 高,io低:锁等待过多的几率比较大. IOPS:每秒磁盘最多能够发生的IO次数,这是个定值 频繁小事务,IOPS很高,达到阈值,可能IO吞吐量没超过IO最大吞吐量.无法新的IO了。存储规划有问题. --- centos 7 默认是deadline cat /sys/block/sda/queue/scheduler ### 临时修改为deadline(centos6) echo deadline >/sys/block/sda/queue/scheduler vi /boot/grub/grub.conf ### 更改到如下内容: kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet ### IO需要考虑一些方面 : raid no lvm ext4或xfs ssd IO调度策略 提前规划好以上所有问题,减轻MySQL优化的难度。
iotop
vmstat
-
glances
- 一次搞定cpu、mem、io
IO密集型:线上系统,OLTP主要是IO密集型的业务,高并发
CPU密集型:数据分析数据处理,OLAP,cpu密集型的,需要CPU高计算能力(i系列,IBM power系列)
CPU密集型: I 系列的,主频很高,核心少
IO密集型: E系列(至强),主频相对低,核心数量多
### oltp 高并发---cpu核心要多 io密集型---磁盘要好 内存尽然大
### olap cpu密集型---cpu主频要 内存要大
配置合理的RAID级别(raid5、raid10、热备盘)
r0 :条带化 ,性能高
r1 :镜像,安全
r5 :校验+条带化,安全较高+性能较高(读),写性能较低 (适合于读多写少)
r10:安全+性能都很高,最少四块盘,浪费一半的空间(高IO要求)
mysql优化---数据库层
### 优化工具
show status
show variables
show index
show processlist
show slave status
show engine innodb status
desc /explain
slowlog
### 扩展类深度优化
#### pt系列
#### mysqlslap
#### sysbench
#### information_schema
#### performance_schema
#### sys
### 导入测试表
mysql> source t100w.sql
### 压测命令(用户名为root,密码123,可以自己修改)
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query="select * from test.t100w where k2='FGCD'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
mysql实例参数
max_connections
Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,当然这是要建立在机器能够支撑的情况下,因为如果连接数越来越多,mysql会为每个连接提供缓冲区,就会开销的越多的内存,所以需要适当的调整该值,不能随便去提高设值。
### 判断依据
show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
show status like 'Max_used_connections';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 101 |
+----------------------+-------+
### 修改方式举例
vim /etc/my.cnf
Max_connections=1024
mysql> set GLOBAL max_connections=1024;
- 开启数据库时,我们可以临时设置一个比较大的测试值
- 观察show status like 'Max_used_connections';变化
- 如果max_used_connections跟max_connections相同,那么就是max_connections设置过低或者超过服务器的负载上限了,低于10%则设置过大.
back_log
mysql能暂存的连接数量,当主要mysql线程在一个很短时间内得到非常多的连接请求时候它就会起作用,如果mysql的连接数据达到max_connections时候,新来的请求将会被存在堆栈中,等待某一连接释放资源,该推栈的数量及back_log,如果等待连接的数量超过back_log,将不被授予连接资源。
back_log值指出在mysql暂时停止回答新请求之前的短时间内有多少个请求可以被存在推栈中,只有如果期望在一个短时间内有很多连接的时候需要增加它
### 判断依据
show full processlist
####发现大量的待连接进程时,就需要加大back_log或者加大max_connections的值
### 修改方式举例
vim /etc/my.cnf
back_log=1024
wait_timeout、interactive_timeout
wait_timeout:指的是mysql在关闭一个非交互的连接之前所要等待的秒数
interactive_timeout:指的是mysql在关闭一个交互的连接之前所需要等待的秒数,比如我们在终端上进行mysql管理,使用的即使交互的连接,这时候,如果没有操作的时间超过了interactive_time设置的时间就会自动的断开,默认的是28800,可调优为7200。
wait_timeout:如果设置太小,那么连接关闭的就很快,从而使一些持久的连接不起作用
设置建议
如果设置太大,容易造成连接打开时间过长,在show processlist时候,能看到很多的连接 ,一般希望wait_timeout尽可能低长连接的应用,为了不去反复的回收和分配资源,降低额外的开销。
一般我们会将wait_timeout设定比较小,interactive_timeout要和应用开发人员沟通长链接的应用是否很多。如果他需要长链接,那么这个值可以不需要调整。
### 修改方式举例
wait_timeout=60
interactive_timeout=1200
key_buffer_size
key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度
- 此参数与myisam表的索引有关
- 临时表的创建有关(多表链接、子查询中、union)
- 在有以上查询语句出现的时候,需要创建临时表,用完之后会被丢弃
- 临时表有两种创建方式:
- 内存中------->key_buffer_size
- 磁盘上------->ibdata1(5.6) 、ibtmp1 (5.7)
### 设置依据
#### 通过key_read_requests和key_reads可以直到key_buffer_size设置是否合理。
mysql> show variables like "key_buffer_size%";
+-----------------+---------+
| Variable_name | Value |
+-----------------+---------+
| key_buffer_size | 8388608 |
+-----------------+---------+
1 row in set (0.00 sec)
mysql>
mysql> show status like "key_read%";
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Key_read_requests | 10 |
| Key_reads | 2 |
+-------------------+-------+
2 rows in set (0.00 sec)
### 注:key_buffer_size只对myisam表起作用,即使不使用myisam表,但是内部的临时磁盘表是myisam表,也要使用该值。
### 可以使用检查状态值created_tmp_disk_tables得知:
mysql> show status like "created_tmp%";
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
| Created_tmp_files | 6 |
| Created_tmp_tables | 1 |
+-------------------------+-------+
3 rows in set (0.00 sec)
mysql>
#### 通常地,我们习惯以 Created_tmp_tables/(Created_tmp_disk_tables + Created_tmp_tables)
#### Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables)
### 配置方法
key_buffer_size=64M
max_connect_errors
max_connect_errors是一个mysql中与安全有关的计数器值,它负责阻止过多尝试失败的客户端以防止暴力破解密码等情况,当超过指定次数,mysql服务器将禁止host的连接请求,直到mysql服务器重启或通过flush hosts命令清空此host的相关信息 max_connect_errors的值与性能并无太大关系。
### 修改/etc/my.cnf文件,在[mysqld]下面添加如下内容
max_connect_errors=2000
sort_buffer_size、join_buffer_size、read_buffer_size、read_rnd_buffer_size
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速
- ORDER BY
- GROUP BY
- distinct
- union
select a.name,b.name from a join b on a.id=b.id where xxxx
用于表间关联缓存的大小,和sort_buffer_size一样,该参数对应的分配内存也是每个连接独享。
尽量在SQL与方面进行优化,效果较为明显。
优化的方法:在on条件列加索引,至少应当是有MUL索引
MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。和 sort_buffer_size一样,该参数对应的分配内存也是每个连接独享
MySql的随机读(查询操作)缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。
注:顺序读是指根据索引的叶节点数据就能顺序地读取所需要的行数据。随机读是指一般需要根据辅助索引叶节点中的主键寻找实际行数据,而辅助索引和主键所在的数据段不同,因此访问方式是随机的。
### 配置依据
#### Sort_Buffer_Size并不是越大越好,由于是connection级的参数,过大的设置+高并发可能会耗尽系统内存资源。
#### 如:500个连接将会消耗500*sort_buffer_size(2M)=1G内存
#### 修改/etc/my.cnf文件,在[mysqld]下面添加如下:
sort_buffer_size=1M
(1)简介:
mysql根据配置文件会限制,server接受的数据包大小。
(2)配置依据:
有时候大的插入和更新会受max_allowed_packet参数限制,导致写入或者更新失败,更大值是1GB,必须设置1024的倍数
(3)配置方法:
max_allowed_packet=32M
max_allowed_packet
### 有时候大的插入和更新会受max_allowed_packet参数限制,导致写入或者更新失败,更大值是1GB,必须设置1024的倍数
### 配置方法:
max_allowed_packet=32M
#### 必须设置1024的倍数
thread_cache_size
服务器线程缓存,这个值表示可以重新利用保存在缓存中线程的数量,当断开连接时,那么客户端的线程将被放到缓存中以响应下一个客户而不是销毁(前提是缓存数未达上限),如果线程重新被请求,那么请求将从缓存中读取,如果缓存中是空的或者是新的请求,那么这个线程将被重新创建,如果有很多新的线程,增加这个值可以改善系统性能.
===连接池
### 配置依据
#### 通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。
#### 设置规则如下:1GB 内存配置为8,2GB配置为16,3GB配置为32,4GB或更高内存,可配置更大。
#### 服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁(前提是缓存数未达上限)
试图连接到MySQL(不管是否连接成功)的连接数
mysql> show status like 'threads_%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 8 |
| Threads_connected | 2 |
| Threads_created | 4783 |
| Threads_running | 1 |
+-------------------+-------+
4 rows in set (0.00 sec)
Threads_cached :代表当前此时此刻线程缓存中有多少空闲线程。
Threads_connected:代表当前已建立连接的数量,因为一个连接就需要一个线程,所以也可以看成当前被使用的线程数。
Threads_created:代表从最近一次服务启动,已创建线程的数量,如果发现Threads_created值过大的话,表明MySQL服务器一直在创建线程,这也是比较耗cpu SYS资源,可以适当增加配置文件中thread_cache_size值。
Threads_running :代表当前激活的(非睡眠状态)线程数。并不是代表正在使用的线程数,有时候连接已建立,但是连接处于sleep状态。
### 配置方法:
thread_cache_size=32
==innodb_buffer_pool_size==
对于单独的MySQL数据库服务器,最大可以把该值设置成物理内存的80%,一般我们建议不要超过物理内存的==70%==。
#### (1)简介
对于InnoDB表来说,innodb_buffer_pool_size的作用就相当于key_buffer_size对于MyISAM表的作用一样。
### (2)配置依据:
InnoDB使用该参数指定大小的内存来缓冲数据和索引。
###(3)配置方法
innodb_buffer_pool_size=2048M
#### 默认128M
### 查看上面的数值是否设置合理
show engine innodb status \G
#### 回显其中有一行是Buffer pool size,这个是表示内存页的个数,一个内存页16KB
==innodb_flush_log_at_trx_commit==
###(1)简介
主要控制了innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,取值分别为0、1、2三个。
0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入日志文件并flush磁盘一次;
1,每次事务的提交都会引起redo日志文件写入、flush磁盘的操作,确保了事务的ACID;
2,每次事务提交引起写入日志文件的动作,但每秒钟完成一次flush磁盘操作。
###(2)配置依据
实际测试发现,该值对插入数据的速度影响非常大,设置为2时插入10000条记录只需要2秒,设置为0时只需要1秒,而设置为1时则需要229秒。因此,MySQL手册也建议尽量将插入操作合并成一个事务,这样可以大幅提高速度。
####根据MySQL官方文档,在允许丢失最近部分事务的危险的前提下,可以把该值设为0或2。
###(3)配置方法
innodb_flush_log_at_trx_commit=1
#### 双1标准中的一个1
innodb_thread_concurrency
###(1)简介
此参数用来设置innodb线程的并发数量,默认值为0表示不限制。
###(2)配置依据
在官方doc上,对于innodb_thread_concurrency的使用,也给出了一些建议,如下:
如果一个工作负载中,并发用户线程的数量小于64,建议设置innodb_thread_concurrency=0;
如果工作负载一直较为严重甚至偶尔达到顶峰,建议先设置innodb_thread_concurrency=128,
并通过不断的降低这个参数,96, 80, 64等等,直到发现能够提供最佳性能的线程数,
例如,假设系统通常有40到50个用户,但定期的数量增加至60,70,甚至200。你会发现,
性能在80个并发用户设置时表现稳定,如果高于这个数,性能反而下降。在这种情况下,
建议设置innodb_thread_concurrency参数为80,以避免影响性能。
如果你不希望InnoDB使用的虚拟CPU数量比用户线程使用的虚拟CPU更多(比如20个虚拟CPU),
建议通过设置innodb_thread_concurrency 参数为这个值(也可能更低,这取决于性能体现),
如果你的目标是将MySQL与其他应用隔离,你可以l考虑绑定mysqld进程到专有的虚拟CPU。
但是需 要注意的是,这种绑定,在myslqd进程一直不是很忙的情况下,可能会导致非最优的硬件使用率。在这种情况下,
你可能会设置mysqld进程绑定的虚拟 CPU,允许其他应用程序使用虚拟CPU的一部分或全部。
在某些情况下,最佳的innodb_thread_concurrency参数设置可以比虚拟CPU的数量小。
定期检测和分析系统,负载量、用户数或者工作环境的改变可能都需要对innodb_thread_concurrency参数的设置进行调整。
128 -----> top cpu
### 设置标准:
1、当前系统cpu使用情况,均不均匀
top
2、当前的连接数,有没有达到顶峰
show status like 'threads_%';
show processlist;
### (3)配置方法:
innodb_thread_concurrency=8
### 方法:
1. 看top ,观察每个cpu的各自的负载情况
2. 发现不平均,先设置参数为cpu个数,然后不断增加(一倍)这个数值
3. 一直观察top状态,直到达到比较均匀时,说明已经到位了.
innodb_log_buffer_size \innodb_log_files_in_group\bulk_insert_buffer_size
此参数确定些日志文件所用的内存大小,以M为单位。缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小。
innodb_log_buffer_size=128M
设定依据:
1、大事务: 存储过程调用 CALL
2、多事务
为提高性能,MySQL可以以循环方式将日志文件写到多个文件。推荐设置为3
批量插入数据缓存大小,可以有效提高插入效率,默认为8M
tokuDB percona
myrocks
RocksDB
TiDB
MongoDB
binary log
log-bin=/data/mysql-bin
binlog_cache_size = 2M
### 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存, 提高记录bin-log的效率。没有什么大事务,dml也不是很频繁的情况下可以设置小一点,如果事务大而且多,dml操作也频繁,则可以适当的调大一点。前者建议是--1M,后者建议是:即 2--4M
max_binlog_cache_size = 8M
### 表示的是binlog 能够使用的最大cache 内存大小
max_binlog_size= 512M
### 指定binlog日志文件的大小,如果当前的日志大小达到max_binlog_size,还会自动创建新的二进制日志。你不能将该变量设置为大于1GB或小于4096字节。默认值是1GB。在导入大容量的sql文件时,建议关闭sql_log_bin,否则硬盘扛不住,而且建议定期做删除。
expire_logs_days = 7
### 定义了mysql清除过期日志的时间。
####二进制日志自动删除的天数。默认值为0,表示“没有自动删除”。
binlog_format=row
sync_binlog=1
#### 双1标准(基于安全的控制):
#### sync_binlog=1 什么时候刷新binlog到磁盘,每次事务commit
innodb_flush_log_at_trx_commit=1
set sql_log_bin=0;
show status like 'com_%';
安全参数
Innodb_flush_method=(O_DIRECT, fsync)
1、fsync :
(1)在数据页需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘
(2)在redo buffuer需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘
但,如果innodb_flush_log_at_trx_commit=1的话,日志还是直接每次commit直接写入磁盘
2、 Innodb_flush_method=O_DIRECT
(1)在数据页需要持久化时,直接写入磁盘
(2)在redo buffuer需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘
但,如果innodb_flush_log_at_trx_commit=1的话,日志还是直接每次commit直接写入磁盘
### 最安全模式:
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
### 最高性能模式:
innodb_flush_log_at_trx_commit=0
innodb_flush_method=fsync
### 一般情况下,我们更偏向于安全。
####“双一标准”
innodb_flush_log_at_trx_commit=1
sync_binlog=1
innodb_flush_method=O_DIRECT
参数优化结果实例
### /etc/my.cnf
[mysqld]
basedir=/data/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
log-error=/var/log/mysql.log
log_bin=/data/binlog/mysql-bin
binlog_format=row
skip-name-resolve
server-id=52
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
relay_log_purge=0
max_connections=1024
back_log=128
wait_timeout=60
interactive_timeout=7200
key_buffer_size=16M
query_cache_size=64M
query_cache_type=1
query_cache_limit=50M
max_connect_errors=20
sort_buffer_size=2M
max_allowed_packet=32M
join_buffer_size=2M
thread_cache_size=200
innodb_buffer_pool_size=1024M
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=32M
innodb_log_file_size=128M
innodb_log_files_in_group=3
binlog_cache_size=2M
max_binlog_cache_size=8M
max_binlog_size=512M
expire_logs_days=7
read_buffer_size=2M
read_rnd_buffer_size=2M
bulk_insert_buffer_size=8M
[client]
socket=/tmp/mysql.sock
### 再次压力测试 :
mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='oldboy' --query="select * from oldboy.t_100w where k2='FGCD'" engine=innodb --number-of-queries=200000 -uroot -p123 -verbose
锁的监控及处理
监控锁状态
### 1.看有没有锁等待
SHOW STATUS LIKE 'innodb_row_lock%';
#### Innodb_row_lock_current_waits
#### Innodb_row_lock_waits (统计类)
### 2.查看哪个事务在等待(被阻塞了)
USE information_schema
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state='LOCK WAIT';
#### trx_id : 事务ID号
#### trx_state : 当前事务的状态
#### trx_mysql_thread_id:连接层的,连接线程ID(SHOW PROCESSLIST ===>Id)
#### trx_query : 当前被阻塞的操作(一般是要丢给开发的)
### 3.查看锁源,谁锁的(这个回显的内容包含了第2点)
SELECT * FROM sys.innodb_lock_waits; #### ====>被锁的和锁定它的之间关系
#### locked_table : 哪张表出现的等待
#### locked_type:锁的类型(record、gap、next)
#### waiting_trx_id: 等待的事务id(与上个视图trx_id 对应)
#### waiting_pid : 等待事务的连接线程号(与上个视图trx_mysql_thread_id)
#### waiting_query:等待事务的sql语句
#### waiting_lock_mode:等待锁的类型(x,s)
#### blocking_trx_id : 锁源的事务ID
#### blocking_pid : 锁源的事务连接线程号
#### sql_kill_blocking_connection:处理建议
### 4.根据锁源的连接线程id,找到锁源sql thread_id(线程id)
SELECT * FROM performance_schema.threads WHERE processlist_id=15;
#### 结果是41
#### thread_id
#### name
#### processlist_id
### 5.根据锁源的sql线程id,找到锁源的sql语句
#### -- 当前在执行的语句
SELECT * FROM performance_schema.`events_statements_current` WHERE thread_id=41;
#### thread_id
#### event_name
#### SQL_text
##### -- 执行语句的历史
SELECT * FROM performance_schema.`events_statements_history` WHERE thread_id=41;
####得出结果,丢给开发
####表信息
####被阻塞的
####锁源SQL
==项目---锁的监控及处理==
1. 背景:
硬件环境: DELL R720,E系列16核,48G MEM,SAS*900G*6,RAID10
在例行巡检时,发现9-11点时间段的CPU压力非常高(80-90%)
2. 项目的职责
2.1 通过top详细排查,发现mysqld进程占比达到了700-800%
2.2 其中有量的CPU是被用作的SYS和WAIT,us处于正常
2.3 怀疑是MySQL 锁 或者SQL语句出了问题
2.4 经过排查slowlog及锁等待情况,发现有大量锁等待及少量慢语句
(1) pt-query-diagest 查看慢日志
(2) 锁等待有没有?
db03 [(none)]>show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
+-------------------------------+-------+
情况一:
有100多个current_waits,说明当前很多锁等待情况
情况二:
1000多个lock_waits,说明历史上发生过的锁等待很多
2.5 查看那个事务在等待(被阻塞了)
2.6 查看锁源事务信息(谁锁的我)
2.7 找到锁源的thread_id
2.8 找到锁源的SQL语句
3. 找到语句之后,和应用开发人员进行协商
(1)
开发人员描述,此语句是事务挂起导致
我们提出建议是临时kill 会话,最终解决问题
(2)
开发人员查看后,发现是业务逻辑问题导致的死锁,产生了大量锁等待
临时解决方案,将阻塞事务的会话kill掉.
最终解决方案,修改代码中的业务逻辑
项目结果:
经过排查处理,锁等待的个数减少80%.解决了CPU持续峰值的问题.
锁监控设计到的命令:
show status like 'innodb_rows_lock%'
select * from information_schema.innodb_trx;
select * from sys.innodb_lock_waits;
select * from performance_schema.threads;
select * from performance_schema.events_statements_current;
select * from performance_schema.events_statements_history;
==死锁监控==
show engine innodb status\G
show variables like '%deadlock%';
vim /etc/my.cnf
innodb_print_all_deadlocks = 1
主从优化
## 5.7 从库多线程MTS
基本要求:
5.7以上的版本(忘记小版本)
必须开启GTID
binlog必须是row模式
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
5.7 :
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=8
cpu核心数作为标准
CHANGE MASTER TO
MASTER_HOST='10.0.0.128',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3307,
MASTER_AUTO_POSITION=1;
start slave;