大西洋暖流

一切都在不完美中前行


  • 首页

  • 关于

  • 标签

  • 归档

  • 搜索

使用Git命令更改文件夹名称

发表于 2018-04-12 | 分类于 编程 | 阅读次数:
字数统计: 634 | 阅读时长 ≈ 2

最近遇到一个问题,在我的博客上点击“DataFrame”标签,程序跳转到“腾讯公益失踪儿童”页面,而不是预期的文章列表页面。

Tencent lost child page

起初以为这是一个个例,但一番检查之后,发现一共有8个标签(下图红色方框标出)存在这个问题。

阅读全文 »

Mac MongoDB启动关闭命令

发表于 2018-04-09 | 分类于 编程 | 阅读次数:
字数统计: 2,269 | 阅读时长 ≈ 10

半年前写过一个爬虫程序,用MongoDB作为持久化工具。今天把之前的爬虫程序整理一番,发现对MongoDB的操作有点生疏,在此做一个整理。

本文只讲解macOS系统下MongoDB的使用方式,版本号是v4.2.8,不涉及Windows或Linux系统,如果存在偏差,请自行查阅相关文档。

Standalone模式

我们知道,mongodb启动的命令是mongod,它有两种方式启动,一是命令行带参数,二是配置文件方式。用命令行带参数启动方式比较简单省事,甚至你直接在终端里输入mongod就可以启动MongoDB服务(注意,这种方式启动成功的前提是已经创建好/data/db目录,并且设置成功相应的权限,/data/db目录是MongoDB默认存放数据的地方,就和MySQL一样数据库里每一条记录最终会存储在硬盘的某一个文件夹下面一样(在MySQL shell里输入show variables like '%datadir%'即可查看数据库对应文件夹的位置);只不过MongoDB需要自己先定义好)。

一般情况下,都应该使用指定配置文件方式启动。

  • 命令行方式启动
    这种方式就是启动的时候不用配置文件,配置文件里的配置项都直接写在命令行上。

    mongod --dbpath=/usr/local/mongodb/data/ --logpath=/usr/local/mongodb/log/mongodb.log

    命令行方式比较繁琐,而且需要手动敲一长串参数文件,不常用。

  • 配置文件方式启动

    将默认启动参数以yaml文件格式写到一个配置文件里。使用mongod -f /XXX/mongod.conf方式启动。

下面便是我使用的配置文件,路径我选择使用/usr/local/mongodb/etc/mongod.conf。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /usr/local/mongodb/log/mongodb.log
# Where and how to store data.
storage:
dbPath: /usr/local/mongodb/data/
journal:
enabled: true
# how the process runs
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
security:
authorization: enabled

启动命令:

1
mongod -f /usr/local/mongodb/etc/mongodb.conf

如果启动成功,在浏览器打开http://localhost:27017 ,能看到以下提示信息。

1
It looks like you are trying to access MongoDB over HTTP on the native driver port.

ReplicaSet模式

上面使用单机(Standalone)模式启动MongoDB,实际生产环境中,单机模式面临着很大的风险,一旦数据库服务出现问题,就会导致线上服务出现异常甚至崩溃,我们需要对MongoDB做相应的主备处理,提高数据库服务的可用性。这就是复制集(Replica Set)模式。

Replica Set是一堆mongod实例集合,它们有着同样的数据内容,包含三类角色:

  1. 主节点(Primary):接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。默认读请求也是发到Primary节点处理的,如需要转发到Secondary需要客户端修改一下连接配置。
  2. 副本节点(Secondary):与主节点保持同样的数据集。当主节点挂掉的时候,参与选举。
  3. 仲裁者(Arbiter):不保存数据,不参与竞选,只参与投票。使用Arbiter可以减轻数据存储的硬件需求,运行Arbiter的机器不需要过高的资源需求,它的作用是当两个Secondary节点投票结果一致时,可以决定哪一个节点胜出升级为Primay节点;很重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。

注意,一个自动failover的Replica Set节点数必须为奇数,目的是选举投票的时候要有一个大多数才能进行选主决策。

贴两张图展示一下复制集模式,详细介绍请自行搜索查阅。

复制集模式,需要分别启动3个独立的mongod服务,我创建了一个新文件夹replicaset用于存放3个节点的数据,如下所示。

在/var/run目录下创建replicaset文件夹,在里面存放3个节点各自对应的PID文件。

下面列出其中一个节点rs0的配置文件信息,rs1和rs2参照rs0配置即可,端口号依次用27017、27018、27019。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /usr/local/mongodb/replicaset/rs0/log/mongodb.log
# Where and how to store data.
storage:
dbPath: /usr/local/mongodb/replicaset/rs0/data/
journal:
enabled: true
# how the process runs
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/replicaset/rs0/mongod.pid # location of pidfile
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
security:
authorization: disabled
replication:
oplogSizeMB: 64
replSetName: rstest

打开终端,依次执行下面3条命令,即启动了3个mongod后台服务。

mongod -f /usr/local/mongodb/replicaset/rs0/etc/mongod.conf

mongod -f /usr/local/mongodb/replicaset/rs1/etc/mongod.conf

mongod -f /usr/local/mongodb/replicaset/rs2/etc/mongod.conf

1. ReplicaSet初始化

在终端输入mongo(不指定节点IP地址和端口号,默认等同于输入mongo 127.0.0.1:27017),输入rs.status()命令,可以看到replset当前没有初始化。

1
2
3
4
5
6
7
> rs.status()
{
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94,
"codeName" : "NotYetInitialized"
}

整理如下replset配置信息,执行rs.initiate(conf)命令,返回{ "ok" : 1 }表示节点部署成功。

1
2
3
4
5
6
7
8
conf = {
"_id" : "rstest",
"members" : [
{ "_id" : 0, "host" : "127.0.0.1:27017" },
{ "_id" : 1, "host" : "127.0.0.1:27018" },
{ "_id" : 2, "host" : "127.0.0.1:27019" }
]
}

部署成功后,会自动选举一个Primay节点。

输入rs.status()查看状态(我用......省略掉无效内容),可以看到members数组里有三个成员,stateStr代表节点的类型;health代表节点的状态,1表示健康;注意看27018和27019两个节点里有syncingTo这个参数,表示它们与27017保持同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
"set" : "rstest",
"date" : ISODate("2020-07-08T14:37:09.527Z"),
"myState" : 1,
......
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 100,
"optime" : {
"ts" : Timestamp(1594259842, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-07-09T01:57:22Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1594259802, 1),
"electionDate" : ISODate("2020-07-09T01:56:42Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "127.0.0.1:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 55,
"optime" : {
"ts" : Timestamp(1594259842, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1594259842, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-07-09T01:57:22Z"),
"optimeDurableDate" : ISODate("2020-07-09T01:57:22Z"),
"lastHeartbeat" : ISODate("2020-07-09T01:57:24.583Z"),
"lastHeartbeatRecv" : ISODate("2020-07-09T01:57:25.341Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:27017",
"syncSourceHost" : "127.0.0.1:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "127.0.0.1:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 55,
"optime" : {
"ts" : Timestamp(1594259842, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1594259842, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-07-09T01:57:22Z"),
"optimeDurableDate" : ISODate("2020-07-09T01:57:22Z"),
"lastHeartbeat" : ISODate("2020-07-09T01:57:24.583Z"),
"lastHeartbeatRecv" : ISODate("2020-07-09T01:57:25.340Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "127.0.0.1:27017",
"syncSourceHost" : "127.0.0.1:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1
}
],
......
}

注意:

  1. 如果以上部署不成功,将3个节点dbPath文件夹下面的内容清空重新部署即可。

  2. 上述操作都在27017这个节点的mongo这个shell里完成。

如果第一次只部署一个节点,第一个节点会默认设置为Primary节点;后续再添加的节点,会自动成为Secondary节点。

1
2
3
4
5
6
conf = {
"_id" : "rstest",
"members" : [
{ "_id" : 0, "host" : "127.0.0.1:27017" }
]
}

登录一个mongo终端(默认使用27017),执行rs.initiate(conf)成功后。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"set" : "rstest",
"date" : ISODate("2020-07-09T02:13:29.964Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
......
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 327,
"optime" : {
"ts" : Timestamp(1594260802, 7),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-07-09T02:13:22Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1594260802, 2),
"electionDate" : ISODate("2020-07-09T02:13:22Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
......
}

再依次执行rs.add("127.0.0.1:27018")和rs.add("127.0.0.1:27019")将另外两个节点加入rstest这个复制集,也可以得到和上面相同的结果。

2. 验证ReplicaSet部署结果

进入Primary节点,写入数据:

1
2
3
4
5
6
7
8
9
rstest:PRIMARY> use test
switched to db test
rstest:PRIMARY> show collections
rstest:PRIMARY> db.test.insert({'name': 'jackson'})
WriteResult({ "nInserted" : 1 })
rstest:PRIMARY> show collections
test
rstest:PRIMARY> db.test.find()
{ "_id" : ObjectId("5f06e0a7051a1b4f12f407bb"), "name" : "jackson" }

进入Secondary节点,查看数据:

1
2
3
4
5
6
7
rstest:SECONDARY> rs.slaveOk()
rstest:SECONDARY> use test
switched to db test
rstest:SECONDARY> show collections
test
rstest:SECONDARY> db.test.find()
{ "_id" : ObjectId("5f06e0a7051a1b4f12f407bb"), "name" : "jackson" }

3. ReplicaSet pymongo连接测试

MongoDB连接字符串:

对于单机模式下的数据库连接,使用mongo://127.0.0.1:27017/test进行连接;

对于复制集模式,除了上面的方式外,也可以使用

mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/test?replicaSet=rstest进行连接。

1
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
"""
测试读取replicaset数据
"""
import pymongo
# client = pymongo.MongoClient('localhost',27019)
client = pymongo.MongoClient('mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/test?replicaSet=rstest')
db = client['test']
table = db['test']
content = [item for item in table.find()]
print('content:', content)

4. 关闭方法

关闭方法:

  1. 使用./mongo进入shell控制台,输入use admin,然后输入db.shutdownServer()关闭服务。

  2. 直接kill进程号

参考资料:

  • MongoDB安装、配置、启动(MAC)
  • Mac环境下安装和配置MongoDB
  • MongoDB Tutorial

将Numpy datetime64类型日期进行格式转换

发表于 2018-04-06 | 分类于 编程 | 阅读次数:
字数统计: 1,418 | 阅读时长 ≈ 6

(Photo by Goran Ivos on Unsplash)

今天使用Tushare获取个股的bar数据时,遇到这样一个问题。获取数据写入MySQL后,表里面date字段显示带有“00:00:00”格式,而且字段类型为datetime,如下图所示。

这或许没有什么问题,但和我预期的结果有差异,我预期date字段的数据类型是date,不希望它是datetime。并且,我不想date字段的值里面带有“00:00:00”这些内容。

排查原因,最后发现是Tushare包里面bar函数,获取到数据后,通过Pandas.to_datetime函数,将日期字段转换为带“00:00:00”格式的日期值。

见trading.py程序里面第861行,如下所示。

1
data['datetime'] = pd.to_datetime(data['datetime'])
阅读全文 »

成功解决pandas.DataFrame.to_sql写入MySQL报错1170问题!

发表于 2018-04-02 | 分类于 编程 | 阅读次数:
字数统计: 745 | 阅读时长 ≈ 3

(Photo by Nghia Le on Unsplash)

上周五遇到pandas.DataFrame.to_sql写入Mysql数据库报错1170问题(详见:pandas.DataFrame.to_sql写入MySQL报错1170),卡了我一整天;周六联系Tushare作者米哥,在用户交流群里,发帖求助,米哥给了一些建议,我尝试了一下,报错依然存在;周日查SQLAlchemy Reference,在想是不是哪个参数没用对,看得要吐血了还是没有眉目;晚上和一个朋友聊天,他说他现在只用PostgreSQL,完全不用MySQL……

这句话给了我灵感,我把数据库换成PostgreSQL,结果会怎样?

上午来公司,安装PostgreSQL,熟悉语法,配置完后,拿原来代码直接一跑,我靠,直接成功。PostgreSQL没有像MySQL那样,创建索引时要求TEXT类型必须指定长度,我心里暗爽,即使MySQL报错1170问题无法解决,我可以用PostgreSQL继续完成我的股票分析大业啊。

阅读全文 »

pandas.DataFrame.to_sql写入MySQL报错1170

发表于 2018-03-30 | 分类于 编程 | 阅读次数:
字数统计: 1,489 | 阅读时长 ≈ 7

(Photo by Fredrick Kearney Jr on Unsplash)

今天在使用pandas.DataFrame.to_sql这个接口,将Tushare获取的一个DataFrame写入MySQL时,出现如下报错。

PGM:writedb:write_records_into_mysql:error: (_mysql_exceptions.OperationalError) (1170, “BLOB/TEXT column ‘code’ used in key specification without a key length”) [SQL: u’CREATE INDEX ix_k_data_code ON k_data (code)’] (Background on this error at: http://sqlalche.me/e/e3q8)

这个错误在创建MySQL表时很常见,当键值字段是变长的BLOB或TEXT类型,MySQL引擎无法生成索引。

很遗憾,尽管知道原因,也尝试过几乎所有可行的办法,但就是没法解决。很无奈,编程工作就是如此,半年前的代码,核心程序不变,搁现在重构一下,就跑不通了。

心情灰暗了一整个上午,下面是报错详细内容。

最近在将半年前写的Python股票分析程序重构,打算写一个标准的Python包出来。write_records_into_mysql是所有程序写入MySQL的接口,它有两个必输参数,分别是df和table_name;三个默认参数,分别是conn、if_exists、dtype。

其它细节不贴,一眼就能看明白,这个函数主要作用是调用pandas.DataFrame.to_sql接口,将df写入数据库。to_sql接收的参数和write_records_into_mysql接收的参数其实是一样的,乍一看这里好像存在重复封装,为什么不直接调用to_sql写数据库,而要采用调用write_records_into_mysql的方式?这是一个好问题。

阅读全文 »
1…91011…15
大西洋暖流

大西洋暖流

74 日志
7 分类
150 标签
常用链接
  • 廖雪峰
  • 阮一峰
  • 王垠
  • 酷壳
  • Python之禅
  • Python入坑指南
  • Echo李金
  • Flask资源
  • Python面试题
  • HelloGitHub
© 2016 — 2021 大西洋暖流 | Site words total count: 97.2k

粤公网安备 44030602003362号