沉舟侧畔千帆过
接入新客户的数据,处理的数据量比之前上了一个量级。以前的索引数据库用的 PostgreSQL ,客户端代码暂时没空修改,为了支持这批数据,打算迁移到 Greenplum。
Greenplum 是先进的基于 PostgreSQL 的开源分布式数据库之一。因为兼容 PostgreSQL 的接口,客户端无需做太多的修改就可以使用。本身是基于MPP架构设计的一套数据分析系统,方便扩展,系统稳定,很适合我们的场景。Greenplum是Pivotal旗下的产品,有商业公司开发和维护,会有更好的技术支持和服务。
Greenplum基于MPP架构,如上。 Master 节点作为系统的入口,处理SQL请求。 Segment 节点存储和处理数据。 Master 和 Segment 之间通过 Interconnect 这个网络层连接起来。
一个SQL查询到 Master 节点, Master 会分发到 Segment 节点进行计算,计算结果最后再汇总到 Master 节点,处理好之后返回数据。 Master 和 Segment 分别通过 StandBy Master 和 Mirror 节点提供高可用。 Segment 节点之间并没有太多的数据交互。这种架构好处是简单,好扩展,但是感觉 Master 节点容易成为系统的瓶颈。整个系统的入口和数据最后的汇总都在 Master 节点上,负载可不轻松。
Greenplum提供了详细的安装文档, 6.0 新版本刚发布,我直接使用的新版本。
主要安装步骤如下:
提供了 ansible 脚本:https://gpdb.docs.pivotal.io/6-0/install_guide/ansible-example.html
主要三个:
/etc/selinux/config
中设置 SELINUX=disabled
,重启即可也是三个,首先创建 gpadmin 用户和用户组
1 | sudo groupadd gpadmin |
再生成密钥对
1 | sudo su gpadmin |
最后将管理用户设置为 sudo 免密,方便以后自己操作
1 | echo 'pgadmin ALL=(ALL) NOPASSWD: ALL' | EDITOR='tee -a' visudo |
Ubuntu 系统有现成的PPA源,Pivotal也提供编译好的二进制包。我选择的是最新版本的 greenplum-db。
1 | sudo add-apt-repository ppa:greenplum/db |
文档在此,需要在 master 节点上操作。
创建 hostfile_gpinitsystem
包括所有 segment 节点的地址,不包括 master 和 standby master。更改配置文件,确保 master 到各个 segment 节点的连接正确。
1 | ssh-keyscan -f hostfile_gpinitsystem || sudo tee -a /etc/ssh/ssh_known_hosts |
1 | source /opt/greenplum-db-6.0.1/greenplum_path.sh |
主要修改的几个参数按照文档要求的填对就行。
需要在各个类型的节点上创建 Greenplum 用于存储数据的路径,并配置好权限。数据存储路径需要和配置文件的保持一致。可以利用 Greenplum 提供的工具在 master 节点操作整个网络。
Master
1 | mkdir -p /data/master |
Standby master
1 | source /opt/greenplum-db-6.0.1/greenplum_path.sh |
Segment
1 | source /opt/greenplum-db-6.0.1/greenplum_path.sh |
Mirror
1 | source /opt/greenplum-db-6.0.1/greenplum_path.sh |
上面都整好了,就可以执行命令初始化数据库了。
1 | gpinitsystem -c gpinitsystem_config -h hostfile_gpinitsystem |
为了方便以后的操作,可以增加下面内容到 .bashrc
,不用每次 source
引入环境变量。
1 | source /opt/greenplum-db-6.0.1/greenplum_path.sh |
生产环境使用的 PostgreSQL 11,使用了很多新特性,包括BRIN等。Greenplum 6 还是基于 PG 9.4 版本,一些东西老版本的不支持,需要特别注意,处理一下。
迁移使用PG自带的工具 pg_dump 和 psql ,很快可以灌入数据。
小问题就不提了,看 log 和文档定位到修改就好了,说一个奇葩的问题。
查询一个SQL语句,只要带 Group By 就无法返回结果,而 sum 却可以拿到结果。查看日志没有报错。查询就是等待返回数据,非常诡异。master 和 segment 节点是有超时设置的,如果是 segment 查询超时,应该会有错误日志,但是很诡异,就是没有详细的异常日志。根据Greenplum的架构图,猜测应该是 master 节点处理的问题,但是继续检查配置文件、检查Log,依旧找不到问题。
搜索结果中看到了三罐可乐带你读懂Greenplum的interconnect,可能是 Greenplum 的 interconnect 导致 master 对 segment 返回的结果处理有奇怪的逻辑。整个网络状态是正常的,端口监听也正常。反思整个部署过程,打开了 /etc/hosts
文件。
1 | 127.0.0.1``````hostname1 |
云服务器的系统会在 hosts 文件将自己的 hostname 配置成 127.0.0.1
,搭建 Greenplum 集群的时候设置的 hosts 添加在这一行下面。删除本机 127.0.0.1
的配置,重启系统,恢复正常。
深度操作系统V15.11已经发布,发行标记见:深度操作系统 V15.11——心随意动 畅享云端
这个版本一个大的变化是早先的 unstable 版本已正式停止技术支持,所有 unstable 用户需要重新下载安装 stable 版本。个人觉得这是个非常不友好的变动,无论如何,重新安装系统成本都是非常巨大的。早先 deepin 切换到基于 debian 时的一个目标就是学习 Arch 的滚动升级策略,复杂升级不再需要重装系统,现在看未实现这个功能。
桌面环境的一个变动是从之前的 dde-wm 升级到了 dde-kwin,目的主要是通过使用新的 kwin 来支持 Wayland。但是新开发一套桌面的窗口管理系统是很麻烦的,需要时间来完善。目前的 dde-kwin 很多功能还是缺失的,右键菜单刚支持极少的功能。Firefox 浏览器上因为 dde-kwin 带来的窗口条无法隐藏,严重影响美观。
其他主要是一些内置软件的功能更新和BUG修复。这类内置软件,我的态度是够用就行,美观最好。这里就不再赘述了。
deepin 是国内最用心的在做 Linux 操作系统的厂商,这一点指的表扬和尊敬。但是人是要恰饭的,公司是要盈利的。现在王勇离开,企业被360收购,前途多了一些不确定。希望未来 deepin 能在进一步商业化探索的同时,保持自己的初心。
今晚打开 readfree 的网站,不再是熟悉的界面,而是一封告别辞。
随着国内版权意识或主动、或被动的提高,我迎来了 readfree 的告别。在以前 readfree 可以正常访问的时候,我就不止一次的想过这个问题:虽然我方便的找到了书籍,但是很这明显侵权了书籍的版权,这个网站真的可以这么正常的存活在中国的互联网上么?现实给予了答案。
但是无论如何,我从 readfree 获益良多,感谢网站管理员以前的辛苦付出。尊重知识产权是读书之人应有之意,我相信管理员顺应大势,做出了一个正确的选择。感谢 readfree 多年以来的陪伴,它实现了他最初的愿景,希望未来的某一天,会以一个更加合适的方式归来。
结尾附上 readfree 告别辞。
最近整理博客,发现很大一部人都是新手教程性质的“How to”类,内容主要是记录怎么手把手解决事情。没什么难度,非常简单,都是查询官方文档或者Google搜索出的解决方案。考虑到国内很多人其实只会用百度,看CSDN来工作的现实,我这类“How to”类的小笔记是有意义和有价值的。比那些不知道经过多少手的“资料”,自己的一手小笔记更加准确。我是个喜欢整理的人,这类小笔记,以后打算做成一个系列,名称会以how-to
开头,也都会带上how-to
的标签。以前有一个换灯泡作为“伪”第一篇,这个作为“真”第一篇。
最近重装了 deepin 系统,版本号重装前和重装后系统版本都是15.10
,但是系统依赖却有了很大的不同。之前的版本是滚动升级上来的,基于 Debian 的测试源,deepin 自己的代号panda
,发行版名称deepin unstable
。重新安装之后,变成了 Debian 的稳定版,deepin 代号 lion
,发行版名称deepin stable
。panda
下可以直接从源安装Python3.6
,升级之后因为基于 Debian 稳定版,源里移除了。好在编译安装也不麻烦,下面就是正常的从源代码编译安装的步骤。
下面的方法在 deepin 上测试通过,应该同样适用于 Debian、Ubuntu。
打算编译安装以前,首先升级系统,安装必要的依赖。这样可以避免之后安装 Python 时缺少第三方库的问题。
1 | sudo apt update |
这次计划安装 Python 3.6.8,相关页面:https://www.python.org/downloads/release/python-368/
1 | wget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tgz |
从压缩包解压源代码:
1 | tar xvf Python-3.6.8.tgz |
进入目录,执行 ./configure
配置构建文件:
1 | cd Python-3.6.8 |
编译并安装:
1 | make -j8 && sudo make altinstall |
1 | sudo make clean |
至此 Python3.6 已经安装完成,可以在命令行执行python3.6
查看效果,pipenv
创建当前版本的环境也会自动调用。
题目地址:Vijos P1005 超长数字串
最直接的做法,是维护一个字符串,枚举这个无限长的S字符串。然后直接在S中寻找第一次出现位置。空间是有限的,所以我们不可能无限制枚举。可以根据内存限制和测试点范围,估计一个大概的长度,然后填满这个有限长度的S。依照这个想法快速写出代码,可以:
上面这样当然没法得到全部的正确答案。这个题目很明显,A的长度不超过200,这个长度为 200 的数,假设为 N,一定会出现在 S 中。知道某个 N,我们是可以根据规律直接计算出他出现的位置。所以这个问题就转化成求最小的 N,最小的 N 是可以通过枚举 N 的位数来直接尝试出的。
按照这个思路,可以得到求解方式为:
不考虑数据大小,这个方式是正确的思路,稍微注意边界条件,可以得到正确的结果。边界条件包括:
用一个例子来验证思路,假如输入数据是 0000
。
10000
是5位数,L=5。计算1-4位的值,再加上第5位的值,减去 N 在 A 的偏移量 -1,就是位置。每个长度为 L 的数在 S 中消耗的位数为 L * 9 * 10^(L-1)
,可以得出
位数 | 计算方式 | 占的长度 |
---|---|---|
1 | 1 * 9 * 10^(1-1) | 9 |
2 | 2 * 9 * 10^(2-1) | 180 |
3 | 3 * 9 * 10^(3-1) | 2700 |
4 | 4 * 9 * 10^(4-1) | 36000 |
第五位的偏移量为 5*(10000-10000)=0,位置为 9+180+2700+36000+0-(-1)=38890,+1 得到答案 38891。
按照思路二,写成了新的代码。但是还不能通过全部的点。A 长度最多为 200,这个范围肯定是超过最大的整型了,需要高精度。
如果是以前竞赛的时候,时间不够了,估计用 Int64 整完想法二的代码,差不多这个题就这么提交了。但是,现在,是为了学习 Go 来做题的,Go 有处理大数据的 math/big!
将想法二的代码,涉及到计算的地方都使用现成的big.Int
相关的方法替换,出来的结果就是能支持 200 位 N 的结果计算的正确代码。
A | 结果 |
---|---|
21 | 15 |
00 | 191 |
99999 | 438886 |
9999999999 | 88888888881 |
0000000000 | 98888888891 |
1 | /* |
懒癌患者学习Go语言,断断续续都看了几遍,可惜工作中一直没机会用上。偶然看到vijos,现在已经支持Go语言。So,做题使我快乐!做题使我RP++
现在是为了学习Go来做题,和十年前信息竞赛刷vijos是完全不同的心态。当时是为了短时间内尽可能多拿分,搞不定正确的算法,也起码得按照输入数据规模把简单的三四十分拿到手。借着那本人手必读的骗分导论,暴力打表之道也偶尔发生。光阴似箭,日月如梭。扯回来,咱们来做这道题P1003 等价表达式。
一个字符串处理问题。按照做题的思路,应该是读取进来一个表达式,再将a
带入求值。若两个表达式a
带入同一个值得到的结果相同,则认为表达式一致。可以使用多组数据,避免偶然发生的不同表达式计算结果一样的情况。整理出来,步骤如下:
逻辑想明白,接下来就是编码实现。出于学习Go的目的,为了熟悉相关函数和语法,有些地方不一定用最简单的方式实现逻辑。
尝试了fmt
,bufio.NewReader
和bufio.NewScanner
,因为输入数据有空格,fmt
会把空格作为终止符,单行读取不完整。bufio.NewReader
一次性读取了全部的。bufio.NewScanner
比较像熟悉的readline
。
表达式头部尾部中间会有多余的空格,最简单的是字符串替换,过滤掉全部的空格。我用了另一种方式,使用正则表达式,留下符合要求的符号、数字和字母。多用用Go的正则表达式。多习惯一下基本数据结构string
、rune
、byte
。
1 | func filterExpression(expression string) string { |
构造并维护一个数字栈和一个操作符栈,从左至右处理表达式。遇见数字压栈;遇见a
替换成数字压栈;遇见操作符,和栈顶操作符比较,优先级高压栈,否则弹出栈顶操作符和两个数字进行计算。
操作符优先级 (
>^
>*
>+
=-
>)
,我用一个 Maps 维护,代码如下:
1 | operatorOrder["+"] = 1 |
因为有幂的存在,数值可能会变得很大。好在我们并不需要计算表达式的精确值,所以可以对所有涉及*
和^
的计算取一个大质数的模,这样可以有效避免数值过大越界。幂计算多用位运算加速。
1 | const prime = 16769023 |
操作符栈只有两个括号的时候,应该弹出。测试数据应该有脏数据,有些括号不是成对的。不能直接的弹出两个了事,得做一下判断,是我们需要弹出的括号则弹出,否则不做处理。
下面是AC代码,还有改进的点:
a
前后增加空格,然后按照空格做 split,过滤掉空元素,直接将字符串换成一个操作符、数字和a
的 List,会比现在这样一次一次拿更优雅。rune
的地方,还是用的 string
,对这些转换很明确的话,可以更精确点。全程脑袋都在想,用 Python 的,替换完字符,稍微做点处理,大概就能 eval 直接计算了吧……
1 | /* |
随着项目进度推进,业务的流程更加完善,我的理解也更加深入。逐渐发现早期一些数据库表和APP的关系,不是那么的合理和有序。相关联的一些业务逻辑的表,不同业务层面的表,应该合理的、有序的、科学的分属于不同的APP。为了实现这份合理,就需要对现有的表进行数据迁移。
Django的表关系,不能直接通过数据库一行命令修改表的名称来更新,本身Django内部的数据库表State同样也得跟着一起维护。加上现在涉及到的一些外键,变得更加的麻烦。全网资料不多,参考了Django的相关Ticket和这份资料,成功的在生产环境实现了Model在不同App之间的迁移。
迁移思路是先生成新的Model,然后在 Django State 层面将逻辑指向新的Model,不改动数据库表结构。再修改数据库结构,将旧表重命名成新表。最后Django State层面删除旧表,数据库表结构不改动。完成了Model迁移并且不损失数据。单个Model迁移的步骤如下。多个Model需要迁移,请按顺序逐个操作。数据库表变动是高风险操作,务必小心谨慎。
0. 创建数据库备份,这是翻车之后的救命稻草
1. 将旧的Model代码复制到新的App下的models.py
,将全部的使用旧Model的外键更新成新的Model
2. 执行
python manage.py makemigrations
3. 编辑 migration 文件,将自动生成的 operations
部分按照如下修改:
1 | operations=[ |
4. 删除旧Model的代码,执行
python manage.py makemigrations
5. 编辑 migration 文件,两个操作:
1 | dependencies = [ |
operations
部分按照如下修改1 | operations = [ |
6. 检查上面的流程,知道自己每一步干的什么,没有问题的话,执行
python manage.py migrate
检查 and 测试。没有问题,Model迁移成功。
转眼之间已经3月份了,记录一下2019年以来这个小站的变化。
WebP是Google在2010年发布的一种新型图片格式,支持无损和有损压缩。在无损压缩方面,同质量的WebP图片比PNG的体积小26%,而在有损压缩方面,同质量的WebP图片比JPEG小25-34%。WebP在不降低图片质量的同时,减少了约三分之一的体积。
Chrome 系列的浏览器很早就支持 WebP 的格式。但是 Firefox 不支持,这个让我之前一直不太想变图片的格式。最近发现 Firefox 65 的更新内容恰好包括了 WebP 支持,感觉可以尝试一波更换。
目前这个小站的全部图片资源从 png/jpeg 等都转换成了 webp 格式。现有的图使用了 Python 的 Pillow 库写了个脚本进行批量的处理,以后新发布的图打算用腾讯家的智图平台处理。
实际测试下来,图片文件体积都大幅度减少,新格式还是挺棒的。兼容性方面,Safari 还不支持,Firefox 64 以下版本的也不支持。商业/生产环境慎用 WebP 格式,我这个小站就无所谓了。比较好点的解决方案是根据浏览器版本,返回不同的格式 or 请求不同的资源。不支持 webp 格式的返回 png 或者 jpg。知乎的图片现在有这个效果,不知道具体是用的那种方案,以后有空了研究下,在这里立一个 Flag。
去年 Fork 了 yilia 主题,自己改成 illya 维护,当时主要更新了 webpack 的配置和明显的错误。之后忙起来,这件事情就拉下了(咕咕咕)。最近又进行了一些更新,主要有:
文章都是用 Markdown 写的,但是以前没有什么规范,能渲染出内容就OK。最近在 Visual Studio Code 里面安装了 markdownlint,用来检查语法错误和文件格式。以后新写的文件会遵循统一的标准,这样文章渲染出内容的样式、层级关系会更加统一,有更好的一致性。
不久以前,是直接修改
/etc/resolv.conf
文件来修改DNS服务器配置的。
接到云服务器商的电话推销,在HK区新释放了一批自建路线的IP资源,更好更快更强,可以申请用来替换现有IP资源。鄙人应和之后,丢在一边。
隔两日,又接到电话通知,这次直言旧IP需要升级维护,吧啦吧啦一通之后,我明白了一个事实:旧的IP在15号之前需要更换,不然可能就无法使用了。
在两通负责任的电话通知(实际上是谈心)之后。我明白了一定得进行操作,不能偷懒了。
旧的服务器使用的IP已平平安安用了两年,账户余额已经不多,还是Ubuntu上个LTS的xenial
系统。
既然操作无可避免,那么不如去新的区域整一个新的系统,体验新的bionic
(其实这把申请一个新的动态IP,绑定资源,释放旧的IP,这个简单的操作变复杂了)。
于是乎,我开始了新的系统体验。
感谢平时用ansible
脚本化部署的好习惯。简单的点击加上命令行,新机器很快就创建出来。
服务器在香港的主要原因是为了必须的科学上网,SS服务是必不可少的。
新环境,特意用了Go版本的,替换之前一直使用的Python原版的server端。
部署好了该有的服务,连接成功。一番测试,延迟低,网速快,一切是那么的美好。
时不时的,某G开头的搜索引擎会无法使用,但是过一会儿又会变好。
家里网络环境最近也是不太稳定,没有在意。
在又一次连接失败之后,我看了下浏览器提示的错误信息,发现问题了。
“SSL证书不匹配“
访问某G网站,返回的某F网站的SSL证书。根据当前的IP地址反查,的确此IP对应的是某F网站。
所以去G家的请求,为什么会拿到F家的证书?为什么时好时坏?脑中闪过了600
这个数字,DNS服务器有污染。
主机位于祖国香港,理论上应该不会走国内的DNS服务器。但是现在事实上DNS有概率被污染,事实胜于雄辩,强制使用不受污染的DNS Server应该能解决问题。
Python版的SS服务支持设置dns_server
参数指定服务器,很遗憾我现在用了Go版本的。查看了一下项目代码,好像并不支持在参数里面设置DNS服务器。转而考虑设置系统的DNS服务。
不久以前,是直接修改
/etc/resolv.conf
文件来修改DNS服务器配置的。
打开/etc/resolv.conf
,按照经验,两行nameserver
就能解决问题。但迎接我的是
1 | # This file is managed by man:systemd-resolved(8). Do not edit. |
/etc/resolv.conf
实际上软链到了/run/systemd/resolve/stub-resolv.conf
。
这意味着直接修改的话,重启估计就没了。systemd-resolved(8)
又是什么东西?
Google 一下,按顺序追踪:
archlinux
的资料 https://wiki.archlinux.org/index.php/Systemd-resolvedsystemd-resolved
该怎么配置。vim /etc/systemd/resolved.conf
,在[Resolve]
块,设置DNS=8.8.8.8 8.8.4.4
。sudo service systemd-resolved restart
执行systemd-resolve --status
,可以看到现在的配置,已经更新成8.8.8.8
这个全球稳定的服务。
1 | Global |
意外看到了以前的DNS Server 列表。其中包含有国内的114.114.114.114
,这个对于某G的IP肯定是被污染的。
以前看到某些网络攻击,是污染DNS指向攻击目标,形成DDOS。现在和平年代,干起了G和F互换的骚操作。佩服佩服。
Cloudflare 旗下的1.1.1.1
号称比8.8.8.8
更快,以后可以尝试。多一个可用的选择,厂商存在着竞争,总归是一件好事情。
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i
hexo-generator-json-content
--save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true
一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的行程。
很惭愧,就做了一点微小的工作,谢谢大家。