重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Model是django项目的基础,如果一开始没有好好设计好,那么在接下来的开发过程中就会遇到的问题.然而,大多数的开发人员都容易在缺少思考的情况下随意的增加或修改model.这样做的后果就是,在接下来的开发过程中,我们不得不做出努力来修正这些错误.因此,在修改model时,一定尽可能的经过充分的考虑再行动!以下列出的是我们经常用到的一些工具和技巧:South,用于数据迁移,我们会在每个django项目中都用到.但到django1.7时,将会有django.db.migrations代替.django-model-utils,用于处理常见的模式,例如TimeStampedModel.django-extensions,主要用到shell_plus命令,该命令会在shell中自动载入所有的app的model1.基本原则第一,将model分布于不同的app中.如果你的django项目中,有一个app拥有超过20个model,那么,你就应当考虑分拆该app了.我们推荐每个app拥有不超过5个model.第二,尽量使用ORM.我们需要的大多数数据库索引都能通过Object-Relational-Model实现,且ORM带给我们许多快捷方式,例如生成SQL语句,读取/更新数据库时的安全验证.因此,如果能使用简单的ORM语句完成的,应当尽量使用ORM.只有当纯SQL语句极大地简化了ORM语句时,才使用纯SQL语句.并且,在写纯SQL语句是,应当优先考虑使用raw(),再是extra().第三,必要时添加index.添加db_index=True到model中非常简单,但难的是理解何时应该添加.在建立model时,我们事先不会添加index,只有当以下情况时,才会考虑添加index:在所有的数据库查询中使用率在10%-25%时或当有真实的数据,或能正确估计出使用index后的效果确实满意时第四,注意model的继承.model的继承在django中需要十分小心,django提供了三种继承方式,1.abstractbaseclass继承(不要和Pyhton标准库的abc模块搞混),2.多表(multi-table)继承,3.proxymodel继承.下表罗列了这三种继承的优劣:model继承类型优势劣势不使用继承,即每个相同的field会重复出现在不同的model中容易明白model和数据表的关系如果有大量相同的field,会较难维护abstractbaseclass继承,即只有继承自该类的model才会有数据表,其自身没有对应的数据表不用在每个model重复编写相同的field.也没有多表继承带来的过多消耗无法单独使用其共同的abstractbaseclass多表(multi-table)继承每个model都有数据表,因此可以既使用母表,又使用子表.也能通过parent.child从母表访问子表增加了消耗,因为每次查询子表,都会自动查询其母表.强烈建议不使用这一方法!proxymodel继承,即只有原始model才会有相应的数据表在不建立新数据表的情况下,使我们拥有不同行为的model无法修改model的fielddjango的创造者和其他许多开发人员都认为,多表继承的方法不是一个良好的方法.因此我们强烈建议大家不要使用该方法.下面列举了一些常见的如何选择model继承的情形:如果只有少数model拥有重复的field时,大可不必使用model继承,只需要在每个model中添加这些相同的field即可.如果有足够的model拥有重复的field时,大多是情况下,可以使用abstractbaseclass继承,将相同的field提取到abstractbaseclass中.Proxymodel继承很少被用到,和其他两种继承也有着许多不一样之处.请不要使用多表(multi-table)继承,因为它既消耗资源又复杂,如果可以,尽量使用OneToOneFields和ForeignKeys代替.django项目中,创建时间和修改时间这两个field是最用到的,下面给出一个abstractbaseclass继承的例子:#models.pyfromdjango.dbimportmodelsclassTimeStampedModel(models.Model):"""abstractbaseclass,提供创建时间和修改时间两个通用的field"""created=models.DateTimeField(auto_now_add=True)modified=models.DateTimeField(auto_now=True)classMeta:abstract=True注意以上代码的最后两行,正式这两行将这一model变味了abstractbaseclass,下面以TimeStampedModel为abstractbaseclass建立model:fromdjango.dbimportmodelsclassArticle(TimeStampedModel):title=models.CharField(max_length=200)以上两个class,在执行syncdb时,只会建立一个数据表,这也正是我们希望得到的.第五,使用south进行数据迁移,可以参见以前的文章:如何在Django中使用django-south,实现数据迁移(datamigrations)2.DjangoModel的设计如何设计出好的djangomodel可能是最难也是最复杂的一个话题了,在此,我们看看一些基本的技巧吧:a.规范化我们首先建议了解数据库规范化(databasenormalization).如果你还不清楚这是什么,那么,我们强烈建议你先阅读一下相关的书籍,或搜索"关系型数据库设计"或"数据库规范化".在创建djangomodel之前,应当首先保证设计的数据库是规范化的.b.cache正确的使用cache能帮助我们提高数据库的性能.详细的信息,我们会在今后的文章中作进一步介绍.c.何时使用null和blank当定义modelfield时,我们可以设置null=True和blank=True(默认都是False),知道何时设置null和blank对于开发人员也是十分重要的,在下面的表格中,我们一一列举了如何使用这两个选项:Field类型设置null=True设置blank=TrueCharField,TextField,SlugField,EmailField,CommaSeparatedIntegerField等不要设置django规定储存空字符串来代表空值,当从数据库中读取NULL或空值时都为空字符串可以设置设置后允许接受widget中为空值(即不填写),储存到数据库时空值变为空字符串FileField,ImageField不要设置django实际储存的是路径的字符串,因此同上可以设置同上BooleanField不要设置因为有NullBooleanField代替不要设置IntegerField,FloatField,DecimalField等可以设置如果你希望在数据库中能储存NULL可以设置设置后允许接受widget中为空值(即不填写),设置为True时必须同时设置null=TrueDateTimeField,DateField,TimeField等可以设置如果你希望在数据库中能储存NULL可以设置设置后允许接受widget中为空值(即不填写),设置为True时必须同时设置null=TrueForeignKey,ManyToManyField,OneToOneField可以设置如果你希望在数据库中能储存NULL可以设置设置后允许接受widget中为空值(即不填写)GenericIPAddressField可以设置如果你希望在数据库中能储存NULL可以设置设置后允许接受widget中为空值(即不填写)IPAddressField不推荐设置用GenericIPAddressField代替不推荐设置用GenericIPAddressField代替d.什么时候使用BinaryField在django1.6中,新增了BinaryField,用于储存二进制数据(binarydata或bytes).对于BinaryField,我们无法使用ORM的filters,excludes或其他SQL操作.但在少数情况下,我们会用到BinaryField,例如MessagePack格式的内容,传感器接受的原始数据和压缩数据等.但需要注意的是,BinaryData一般都十分庞大,因此可能会拖慢数据库的速度.如果发生这一现象,我们可以将binarydata储存在文件中,然后使用FileField储存该文件的路径信息.还有,不要从BinaryField中直接读取文件并呈献给用户.因为,1.从数据库读写总是比从文件系统读写慢;2.数据库备份会变得十分庞大,花费的时间;3.获得文件的过程,增加了从django到数据库的这一环节.3.不要替换默认的ModelManager从ORM获取model,实际上是通过django中的Modelmanager完成的,django为每一个model提供了默认的modelmanager,我们不建议将其替换掉,因为:当使用model继承时,model会继承abstractbaseclassmodel的modelmanager,而不会继承非abstractbaseclass的manager.model的第一个modelmanager通常作为默认的manager,当被替换时,可能会发生不可预测的问题.4.数据库事务(Transaction)在django1.6中,ORM默认会autocommit每一个数据库查询,也就是说,每次使用m.create()或m.update()时,在数据库中马上就会做出相应的修改.这样做的好处就是简化了初学者对ORM的理解.但坏处就是,当一个view中包含两个数据库修改,可能一个成功,但另一个失败,这就可能导致数据库不完整,给我们带来很大的危险.解决这一问题的方法就是使用数据库transaction,即将一系列数据库操作包含在一个transaction中,当其中有一个失败时,其他操作也会自动回退.Django1.6为我们带来了一套崭新的既简单又强大的transaction机制,使我们方便的使用数据库transaction.a.将整个httprequest包裹在transaction中django给我们提供了一个简单地方法,将一个httprequest中的所有数据库操作包裹在transaction中:#setting/base.pyDATABASES={'default':{'ENGINE':'django.db.backends.postgresql_psycopg2','NAME':'','USER':'','PASSWORD':'','HOST':'','PORT':'','ATOMIC_REQUESTS':True,}}只需要在数据库设置中加入'ATOMIC_REQUESTS':True选项,就能将整个httprequest包裹在transaction中.这样做的好处显而易见是是安全,但坏处则是性能可能会下降,因此随着流量的增大,我们必须采取更针对性的transaction.其次,需要注意的是,回退的只是数据库的状态,而不包括其他费数据库项,例如发送email等.所以当涉及这些非数据库项时,我们应当使用transaction.con_atomic_request()修饰(decorate)这些view:#myapp/views.py#此时'ATOMIC_REQUESTS'设置为Truefromdjango.dbimporttransactionfromdjango.httpimportHttpResponsefromdjango.shortcutsimportget_object_or_404fromdjango.utilsimporttimezonefrom.modelsimportArticle@transaction.non_atomic_requests#以下httprequest不被包裹在一个transaction中defdo_something_to_article(request,pk,title):article=get_object_or_404(Article,pk=pk)#以下代码会以django默认的autocommit模式执行article.datetime=timezone.now()article.save()withtransaction.atomic()#以下代码被包裹在另一个transaction中article.title=titlearticle.datetime=timezone.now()article.save()returnHttpResponse("success!")#如果以上transaction失败了,返回错误状态returnHttpResponse("oops!failed",status_code=400)b.更明确地transaction控制更明确地transaction控制意味着提高真题webapp的性能,但也意味着的开发时间.大多数网站下,由于有限的流量,使用ATOMIC_REQUESTS已经足够.在使用手动transaction控制时,应当注意:不做数据修改的操作,应当排除在transaction之外做数据修改的操作,则应在transaction内特殊情况下,可以违反以上两条需要注意的是,当view返回的是django.http.StreamingHttpResponse时,应当设置ATOMIC_REQUESTS为false,或使用transaction.non_atomic_requests将该view修饰.因为对于view本身,是可以使用transaction的,但对于之后生成的responsestream触发的额外SQL查询,会自动变为django默认的autocommit模式.c.MySQL中使用transaction注意,如果你使用的是MySQL,且数据表类型是MyISAM,那么transaction可能无法使用.无论你的设置是怎么样,当transaction无法使用时,django会自动转化为autocommit模式.
10年积累的做网站、成都网站设计经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有金安免费网站建设让你可以放心的选择与我们合作。
楼上回答错了:
修正第三个参数,第三个参数是截取的长度,而不是结束位置。
正确的是:
使用substr函数,该函数有3个参数
第一个参数是要截取的字段
第二个参数是从哪个位置开始截取,类型为int
第三个参数是从截取长度,类型为int,选填,不填表示从开始截取到最后
和其他发行版无异
-----
CentOS是Linux的一个发行版。
特点:
===
1、可以把CentOS理解为Red Hat AS系列!它完全就是对Red Hat AS进行改进后发布的!各种操作、使用和RED HAT没有区别!
2、CentOS完全免费,不存在RED HAT AS4需要序列号的问题。
3、CentOS独有的yum命令支持在线升级,可以即时更新系统,不像RED HAT那样需要花钱购买支持服务!
4、CentOS修正了许多RED HAT AS的BUG!
附:
1、CentOS版本说明:
CentOS3.1 等同于 RED HAT AS3 Update1
CentOS3.4 等同于 RED HAT AS3 Update4
CentOS4.0 等同于 RED HAT AS4
---------
CentOS 2
CentOS 3.8
CentOS 4.4
CentOS 5.0
内 核
2.4.9 2.4.21 2.6.9 2.6.18
GCC
2.96 3.2.3 3.4.6 4.1.1
Web 服务器
apache 1.3.27 httpd 2.0.46 httpd 2.0.52 httpd 2.2.3
PHP 4.1.2 4.3.2 4.3.9 5.1.6
MySQL 3.23.58 3.23.58 4.1.20 5.0.22
PostgreSQL 7.1.3 7.3.15 7.4.13 8.1.4
Python 1.5.2
2.2.3
2.3.4
2.4.3
Perl 5.6.1 5.8.0 5.8.5
5.8.8
Samba 2.2.7 3.0.9
3.0.10 3.0.23c
Sendmail
8.11.6 8.12.11 8.13.1 8.13.8
Postfix
X
2.0.16 2.2.10 2.3.3
OpenSSH
3.1p1 3.6.1p2 3.9p1 4.3p2
OpenSSL
0.9.6b 0.9.7a 0.9.7a 0.9.8b
iptables
1.2.5 1.2.8 1.2.11 1.3.5
Bind
9.2.1 9.2.4 9.2.4 9.3.3
我有啊 你在google上打 postgresql dowmload 第一个连接就是了 不要用百度
第一个链接就是官网的下载地址
最近对离线数仓体系进行了扩容和架构改造,也算是一波三折,出了很多小插曲,有一些改进点对我们来说也是真空地带,通过对比和模拟压测总算是得到了预期的结果,这方面尤其值得一提的是郭运凯同学的敬业,很多前置的工作,优化和应用压测的工作都是他完成的。
整体来说,整个事情的背景是因为服务器硬件过保,刚好借着过保服务器替换的机会来做集群架构的优化和改造。
1.集群架构改造的目标
在之前也总结过目前存在的一些潜在问题,也是本次部署架构改进的目标:
1)之前 的GP segment数量设计过度 ,因为资源限制,过多考虑了功能和性能,对于集群的稳定性和资源平衡性考虑有所欠缺,在每个物理机节点上部署了10个Primary,10个Mirror,一旦1个服务器节点不可用,整个集群几乎无法支撑业务。
2)GP集群 的存储资源和性能的平衡不够 ,GP存储基于RAID-5,如果出现坏盘,磁盘重构的代价比较高,而且重构期间如果再出现坏盘,就会非常被动,而且对于离线数仓的数据质量要求较高,存储容量相对不是很大,所以在存储容量和性能的综合之上,我们选择了RAID-10。
3)集 群的异常场景的恢复需要完善, 集群在异常情况下(如服务器异常宕机,数据节点不可用,服务器后续过保实现节点滚动替换)的故障恢复场景测试不够充分,导致在一些迁移和改造中,相对底气不足,存在一些知识盲区。
4)集群版本过 低 ,功能和性能上存在改进空间。毕竟这个集群是4年前的版本,底层的PG节点的版本也比较旧了,在功能上和性能上都有一定的期望,至少能够与时俱进。
5)操作系统版本升 级 ,之前的操作系统是基于CentOS6,至少需要适配CentOS 7 。
6)集群TPCH 压测验收 ,集群在完成部署之后,需要做一次整体的TPCH压测验收,如果存在明显的问题需要不断调整配置和架构,使得达到预期的性能目标。
此外在应用层面也有一些考虑,总而言之,是希望能够解决绝大多数的痛点问题,无论是在系统层面,还是应用层面,都能上一个台阶。
2.集群规划设计的选型和思考
明确了目标,就是拆分任务来规划设计了,在规划设计方面主要有如下的几个问题:
1)Greenplum的版本选择 ,目前有两个主要的版本类别,一个是开源版(Open Source distribution)和Pivotal官方版,它们的其中一个差异就是官方版需要注册,签署协议,在此基础上还有GPCC等工具可以用,而开源版本可以实现源码编译或者rpm安装,无法配置GPCC。综合来看,我们选择了 开源版本的6.16.2 ,这其中也询问了一些行业朋友,特意选择了几个涉及稳定性bug修复的版本。
2)数据集市的技术选型 ,在数据集市的技术选型方面起初我是比较坚持基于PostgreSQL的模式,而业务侧是希望对于一些较为复杂的逻辑能够通过GP去支撑,一来二去之后,加上我咨询了一些行业朋友的意见,是可以选择基于GP的方案,于是我们就抱着试一试的方式做了压测,所以数据仓库和和数据集市会是两个不同规模体量的GP集群来支撑。
3)GP的容量规划 ,因为之前的节点设计有些过度,所以在数量上我们做了缩减,每台服务器部署12个segment节点,比如一共12台服务器,其中有10台服务器是Segment节点,每台上面部署了6个Primary,6个Mirror,另外2台部署了Master和Standby,就是即(6+6)*10+2,整体的配置情况类似下面的模式。
4)部署架构方案选型 ,部署架构想起来比较容易,但是落实起来有很多的考虑细节,起初考虑GP的Master和Standby节点如果混用还是能够节省一些资源,所以设计的数据仓库和数据集市的部署架构是这样考虑的,但是从走入部署阶段之后,很快就发现这种交叉部署的模式是不可行的,或者说有一些复杂度。
除此之外,在单个GP集群的部署架构层面,还有4类方案考虑。
方案1 :Master,Standby和segment混合部署
方案2 :Master,Standby和segment独立部署,整个集群的节点数会少一些
方案3 :Segment独立部署,Master,Standby虚拟机部署
方案4 :最小化单节点集群部署(这是数据集市最保底的方案)
这方面存在较大的发挥空间,而且总体来说这种验证磨合的成本也相对比较高,实践给我上了一课, 越是想走捷径,越是会让你走一些弯路 ,而且有些时候的优化其实我也不知道改怎么往下走,感觉已经无路可走,所以上面这4种方案其实我们都做了相关的测试和验证。
3.集群架构的详细设计和实践
1)设计详细的部署架构图
在整体规划之上,我设计了如下的部署架构图,每个服务器节点有6个Primary,6个Mirror,服务器两两映射。
2)内核参数优化
按照官方文档的建议和具体的配置情况,我们对内核参数做了如下的配置:
vm.swappiness=10
vm.zone_reclaim_mode = 0
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 100
vm.dirty_background_ratio = 0 # See System Memory
vm.dirty_ratio = 0
vm.dirty_background_bytes = 1610612736
vm.dirty_bytes = 4294967296
vm.min_free_kbytes = 3943084
vm.overcommit_memory=2
kernel.sem = 500 2048000 200 4096
4.集群部署步骤
1)首先是配置/etc/hosts,需要把所有节点的IP和主机名都整理出来。
2)配置用户,很常规的步骤
groupadd gpadmin
useradd gpadmin -g gpadmin
passwd gpadmin
3)配置sysctl.conf和资源配置
4)使用rpm模式安装
# yum install -y apr apr-util bzip2 krb5-devel zip
# rpm -ivh open-source-greenplum-db-6.16.2-rhel7-x86_64.rpm
5)配置两个host文件,也是为了后面进行统一部署方便,在此建议先开启gpadmin的sudo权限,可以通过gpssh处理一些较为复杂的批量操作
6)通过gpssh-exkeys来打通ssh信任关系,这里需要吐槽这个ssh互信,端口还得是22,否则处理起来很麻烦,需要修改/etc/ssh/sshd_config文件
gpssh-exkeys -f hostlist
7)较为复杂的一步是打包master的Greenplum-db-6.16.2软件,然后分发到各个segment机器中,整个过程涉及文件打包,批量传输和配置,可以借助gpscp和gpssh,比如gpscp传输文件,如下的命令会传输到/tmp目录下
gpscp -f /usr/local/greenplum-db/conf/hostlist /tmp/greenplum-db-6.16.2.tar.gz =:/tmp
或者说在每台服务器上面直接rpm -ivh安装也可以。
8)Master节点需要单独配置相关的目录,而Segment节点的目录可以提前规划好,比如我们把Primary和Mirror放在不同的分区。
mkdir -p /data1/gpdata/gpdatap1
mkdir -p /data1/gpdata/gpdatap2
mkdir -p /data2/gpdata/gpdatam1
mkdir -p /data2/gpdata/gpdatam2
9)整个过程里最关键的就是gpinitsystem_config配置了,因为Segment节点的ID配置和命名,端口区间都是根据一定的规则来动态生成的,所以对于目录的配置需要额外注意。
10)部署GP集群最关键的命令是
gpinitsystem -c gpinitsystem_config -s 【standby_hostname】
其中文件gpinitsystem_config的主要内容如下:
MASTER_HOSTNAME=xxxx
declare -a DATA_DIRECTORY=(/data1/gpdata/gpdatap1 /data1/gpdata/gpdatap2 /data1/gpdata/gpdatap3 /data1/gpdata/gpdatap4 /data1/gpdata/gpdatap5 /data1/gpdata/gpdatap6)
TRUSTED_SHELL=ssh
declare -a MIRROR_DATA_DIRECTORY=(/data2/gpdata/gpdatam1 /data2/gpdata/gpdatam2 /data2/gpdata/gpdatam3 /data2/gpdata/gpdatam4 /data2/gpdata/gpdatam5 /data2/gpdata/gpdatam6)
MACHINE_LIST_FILE=/usr/local/greenplum-db/conf/seg_hosts
整个过程大约5分钟~10分钟以内会完成,在部署过程中建议要查看后端的日志查看是否有异常,异常情况下的体验不是很好,可能会白等。
5.集群部署问题梳理
集群部署中还是有很多细节的问题,太基础的就不提了,基本上就是配置,目录权限等问题,我提另外几个:
1) 资源配置问题 ,如果/etc/security/limits.conf的资源配置不足会在安装时有如下的警告:
2) 网络问题 ,集群部署完成后可以正常操作,但是在查询数据的时候会抛出错误,比如SQL是这样的,看起来很简单:select count(*) from customer,但是会抛出如下的错误:
这个问题的主要原因还是和防火墙配置相关,其实不光需要配置INPUT的权限,还需要配置OUTPUT的权限。
对于数据节点可以开放略大的权限,如:
入口的配置:
-A INPUT -p all -s xxxxx -j ACCEPT
出口的配置:
-A OUTPUT -p all -s xxxxx -j ACCEPT
3)网络配置问题 ,这个问题比较诡异的是,报错和上面是一样的,但是在排除了防火墙配置后,select count(*) from customer;这样的语句是可以执行的,但是执行的等待时间较长,比如表lineitem这表比较大,过亿的数据量,,在10个物理节点时,查询响应时间是10秒,但是4个物理节点,查询响应时间是在90秒,总体删感觉说不过去。
为了排查网络问题,使用gpcheckperf等工具也做过测试,4节点和10节点的基础配置也是相同的。
gpcheckperf -f /usr/local/greenplum-db/conf/seg_hosts -r N -d /tmp
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
#127.0.0.1 test-dbs-gp-128-230
xxxxx.128.238 test-dbs-gp-svr-128-238
xxxxx.128.239 test-dbs-gp-svr-128-239
其中127.0.0.1的这个配置在segment和Master,Standby混部的情况是存在问题的,修正后就没问题了,这个关键的问题也是郭运凯同学发现的。
5.集群故障恢复的测试
集群的故障测试是本次架构设计中的重点内容,所以这一块也是跃跃欲试。
整体上我们包含两个场景,服务器宕机修复后的集群恢复和服务器不可用时的恢复方式。
第一种场景相对比较简单,就是让Segment节点重新加入集群,并且在集群层面将Primary和Mirror的角色互换,而第二种场景相对时间较长一些,主要原因是需要重构数据节点,这个代价基本就就是PG层面的数据恢复了,为了整个测试和恢复能够完整模拟,我们采用了类似的恢复方式,比如宕机修复使用了服务器重启来替代,而服务器不可用则使用了清理数据目录,类似于一台新配置机器的模式。
1)服务器宕机修复后集群恢复
select * from gp_segment_configuration where status!='u';
gprecoverseg -o ./recov
gprecoverseg -r
select * from gp_segment_configuration where status='u'
2)服务器不可用时集群恢复
重构数据节点的过程中,总体来看网络带宽还是使用很充分的。
select * from gp_segment_configuration where status='u'
select * from gp_segment_configuration where status='u' and role!=preferred_role;
gprecoverseg -r
select * from gp_segment_configuration where status='u' and role!=preferred_role;
经过测试,重启节点到数据修复,近50G数据耗时3分钟左右
6.集群优化问题梳理
1)部署架构优化和迭代
对于优化问题,是本次测试中尤其关注,而且争议较多的部分。
首先在做完初步选型后,数仓体系的部署相对是比较顺利的,采用的是第一套方案。
数据集市的集群部分因为节点相对较少,所以就选用了第二套方案
实际测试的过程,因为配置问题导致TPCH的结果没有达到预期。
所以这个阶段也产生了一些疑问和怀疑,一种就是折回第一种方案,但是节点数会少很多,要不就是第三种采用虚拟机的模式部署,最保底的方案则是单节点部署,当然这是最牵强的方案。
这个阶段确实很难,而在上面提到的修复了配置之后,集群好像突然开悟了一般,性能表现不错,很快就完成了100G和1T数据量的TPCH测试。
在后续的改造中,我们也尝试了第三套方案,基于虚拟机的模式,通过测试发现,远没有我们预期的那么理想,在同样的数据节点下,Master和Standby采用物理机和虚拟机,性能差异非常大,这个是出乎我们预料的。比如同样的SQL,方案3执行需要2秒,而方案2则需要80秒,这个差异我们对比了很多指标,最后我个人理解差异还是在网卡部分。
所以经过对比后,还是选择了方案2的混合部署模式。
2)SQL性能优化的分析
此外整个过程的TPCH也为集群的性能表现提供了参考。比如方案2的混合部署模式下,有一条SQL需要18秒,但是相比同类型的集群,可能就只需要2秒钟左右,这块显然是存在问题的。
在排除了系统配置,硬件配置的差异之后,经典的解决办法还是查看执行计划。
性能较差的SQL执行计划:
# explain analyze select count(*)from customer;
QUERY PLAN
Aggregate (cost=0.00..431.00 rows=1 width=8) (actual time=24792.916..24792.916 rows=1 loops=1)
- Gather Motion 36:1 (slice1; segments: 36) (cost=0.00..431.00 rows=1 width=1) (actual time=3.255..16489.394 rows=150000000 loops=1)
- Seq Scan on customer (cost=0.00..431.00 rows=1 width=1) (actual time=0.780..1267.878 rows=4172607 loops=1)
Planning time: 4.466 ms
(slice0) Executor memory: 680K bytes.
(slice1) Executor memory: 218K bytes avg x 36 workers, 218K bytes max (seg0).
Memory used: 2457600kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 24832.611 ms
(9 rows)
Time: 24892.500 ms
性能较好的SQL执行计划:
# explain analyze select count(*)from customer;
QUERY PLAN
Aggregate (cost=0.00..842.08 rows=1 width=8) (actual time=1519.311..1519.311 rows=1 loops=1)
- Gather Motion 36:1 (slice1; segments: 36) (cost=0.00..842.08 rows=1 width=8) (actual time=634.787..1519.214 rows=36 loops=1)
- Aggregate (cost=0.00..842.08 rows=1 width=8) (actual time=1473.296..1473.296 rows=1 loops=1)
- Seq Scan on customer (cost=0.00..834.33 rows=4166667 width=1) (actual time=0.758..438.319 rows=4172607 loops=1)
Planning time: 5.033 ms
(slice0) Executor memory: 176K bytes.
(slice1) Executor memory: 234K bytes avg x 36 workers, 234K bytes max (seg0).
Memory used: 2457600kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 1543.611 ms
(10 rows)
Time: 1549.324 ms
很明显执行计划是被误导了,而误导的因素则是基于统计信息,这个问题的修复很简单:
analyze customer;
但是深究原因,则是在压测时,先是使用了100G压测,压测完之后保留了原来的表结构,直接导入了1T的数据量,导致执行计划这块没有更新。
3)集群配置优化
此外也做了一些集群配置层面的优化,比如对缓存做了调整。
gpconfig -c statement_mem -m 2457600 -v 2457600
gpconfig -c gp_vmem_protect_limit -m 32000 -v 32000
7.集群优化数据
最后来感受下集群的性能:
1)10个物理节点,(6+6)*10+2
tpch_1t=# iming on
Timing is on.
tpch_1t=# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1235.801 ms
tpch_1t=# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 10661.756 ms
2)6个物理节点,(6+6)*6
# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1346.833 ms
# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 18145.092 ms
3)4个物理节点,(6+6)*4
# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1531.621 ms
# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 25072.501 ms
4)TPCH在不通架构模式下的性能比对 ,有19个查询模型,有个别SQL逻辑过于复杂暂时忽略,也是郭运凯同学整理的列表。
在1T基准下的基准测试表现: