谈谈DevOps和虚拟化

其实DevOps和虚拟化并没有多大关系,为什么要把这两个东西放在一起呢?如果有人跟我突兀的讲这两个词,我会联想到docker。其实也就是因为docker才有了这篇文章。此文只是简单的介绍,以后会抽出时间专门来分别介绍虚拟化技术、DevOps还有Docker。

背景

因为需要在内网中搭建一个私有云,一方面是提高硬件的利用率,另外提高系统的可用性和伸缩性。如今有很多云平台可用,但是由于某些安全原因,我们不能使用开放的平台。之前是在几台bare metal上安装了Citrix xen server,使用一个Windows机器上安装xen Center来连接Xen server安装了一些CentOS6.4,之后在这些CentOS上使用Anbari部署Hadoop集群,用来处理的大量的GIS数据。然后有一天Hadoop的master节点无故宕机了,导致整个Hadoop集群就不可用了。老大想探索新方法来增强系统的可用性,另外的要求是系统宕了之后,如果可能,能够迅速的切换到其他的环境下,部署并运行,而不需要在部署Hadoop好之后才能重新运行集群。

总结起来就提出了以下几个要求:

  • 硬件高利用率
  • 高可用性
  • 伸缩性
  • 快速部署和运行

使用虚拟化技术可以从一定程度上提高硬件的利用率;在分布式环境中,提高可用性可使用反向代理和热备的方式;系统的伸缩性由Hadoop来控制,增加节点不是难事;至于快速部署运行,我首相想到的是Docker和vagrant,他们都是DevOps的有理工具。本文将简要介绍以上几个方面。

虚拟化技术

使用虚拟化技术能够提高硬件的利用率,因为如果一台物理机只跑一个应用或者服务的话,cpu的利用率的均值不会很高,当采用虚拟化技术,同时运行多个虚拟机OS的时候,可以提高cpu的利用率均值。

当今虚拟化技术可以分为以下三种:

  • Type1 Hypervisor
    • Xen (Citrix)
    • KVM (FOSS)
    • Hyper-v (MicroSoft)
  • Type2 Hypervisor
    • VMWare Fusion
    • Virtual Box (Oracle)
    • Parallels
  • Container
    • Docker
    • LXC (IBM Linux Containers)
    • nspawn

两种Hypervisor的区别如下图所示:

compare0.png

以上两种Hypervisor的最大区别是,Type1 Hypervisor是直接安装在裸机上的,然而Type2 Hypervisor是安装在桌面操作系统上的。

Docker与LXC和Hypervisor的关系如图所示:
compare.png

Hypervisor虚拟化技术添加了一个Hypervisor层,Hypervisor层来模拟虚拟机运行所需要的硬件功能,然后虚拟机跑在这些Hypervisor之上,也就是说Hypervisor是硬件级别的虚拟化。Hypervisor需要对整个硬件设备进行抽象(包括cpu,网络,外部设备等)。
然而Container虚拟化则是操作系统级别的虚拟化。每一个Guest container操作系统与Host主机共享同样的内核,有时候甚至可以共享部分操作系统,正因为如此container可以扔掉hypervisor VM中99%的虚拟机垃圾,保持一个瘦小,简单的容器来保持你的应用。container值需要对操作系统进行抽象。
另外,Google在很早以前就开始投入到容器的研发和使用中了,现在Google几乎所有的应用都是跑在container里的,比如google search, Gmail, Google Docs等等。

container的这种方式有以下优点:

  • 虚拟机与Hypervisor相比更小
  • container之间共享资源效率更高(多个container打开一个文件,只需要在HOST内核中管理一个打开句柄,二Hypervisor则每个VM中需要各自打开)
  • container更节约内存(Hypervisor需要为各个vm的内核分配内存,而且启动VM之前需要预先分配好内存大小)
  • 启动和关闭更快(因为在HOST眼中就是一些进程,秒级别)

由于以上几点,那么:

  • 同样的物理机,可以打开更多的container
  • 由于更轻量级,并且资源利用率更高,如果用container来搭建VPS服务,或者PaaS的时候则更节省资源
  • Hypervisor更适合用来搭建IaaS(个人观点)
  • 随着container的出现,产生了微服务架构的概念。

Docker环境的条件

比较了Hypervisor和container之后,显示出了container的优势,然后安装Docker也需要一些基本的前提条件。

  • 目前只支持64位CPU架构的计算机(目前只能是x86_64和amd64),不支持32位CPU
  • Linux3.8或者更高的内核版本,2.6.x或者之后的或许也能运行,但是可能出现问题
  • 内核必须支持一种合适的存储驱动,例如Device Manager/AUFS/vfs/btrfs等
  • 内核必须支持并开启cgroup和命名空间namespace功能。

DevOps

vs.png

Wikipedia对DevOps的解释如下:

DevOps is a software development method that emphasizes communication, collaboration (information sharing and web service usage),integration, automation, and measurement of cooperation between software developers and other IT professionals. The method acknowledges the interdependence of software development, quality assurance (QA), and IT operations, and aims to help an organization rapidly produce software products and services and to improve operations performance.

devopsguys.jpg

我理解的DevOps是一个团队角色。即懂开发、测试又懂运维。传统的团队,开发团队、测试团队和运维团队往往是分开的,这往往降低了交流与合作的效率,然而DevOps角色的出现能够有效解决这方面的问题。

DevOps这个词和full stack有些像,是颇具争议的。Jeff Knupp就对DevOps持反对意见,可见他的文章《How ‘DevOps’ is killing the Developer》,最近玉伯和耗子在微博中的辩论也与这个有关,耗子就倾向于小而美的团队,一人掌握多技能和职责.

从Developer到DevOps

timeline.jpg

Developer非运维人员,一般只专注于代码部分,职能中不会涉及到代码完了之后的过程,这些过程包括持续集成中的自动构建和测试、服务器配置、部署、发布等。DevOps除了写好代码之外还应该能够掌握从代码完成到上线的整个过程。

作为一名开发人员,需要突破自己的局限,向DevOps方向扩展自己。这个过程中除了需要了解从代码到产品上线的整个流程之外,就只剩下学习一些常用的工具了。我觉得从Developer到DevOps需要学习以下几个方面的知识:

  • 自动化(Automation)
  • 持续集成(Continuous Integration)
  • 持续测试(Continuous Testing)
  • 持续交付(Continuous Delivery)
  • 持续监控(Continuous Monitoring)

总结起来就是学习一堆运维方面的工具。

自动化配置管理

  • Ansible
  • CFEngine
  • Chef
  • Puppet
  • SaltStack
  • RANCID
  • Ubuntu Juju

持续集成

  • 版本控制 SVN/Git
  • simicity
  • Jenkins
  • Travis
  • BuildHive

持续交付

  • Capistrano
  • Fabric
  • Ansible
  • Jenkins

持续监控

  • New Relic APM(code-level,监控性能问题)
  • New Relic Server (infrastructure-level,监控cpu内存之类)

其他

  • Vagrant
  • Docker

Vagrant & Docker

差点跑题了,立马打住,以上只是为了介绍DevOps这个概念,其实主要是为了引出Vagrant和Docker,这两个DevOps工具。这两个工具的核心思想就是,”一次配置,到处运行”,这个正好能够解决背景中提出的第四个需求。

Vagrant是一个虚拟机管理软件,然而并没有什么卵高级的,但是他还是给DevOps提供了不少帮助,它与普通的虚拟机(像VMWare/VritualBox/Parallels)相比,它的虚拟机经过裁剪,更节省内存,所有的操作通过vagrant命令进行,非常适合喜爱命令行的用户。与Docker相比,它的使用逻辑更符合传统的虚拟机的做法。

Docker的使用不像是传统的虚拟机桌面软件,并且默认不支持ssh登陆,所以使用逻辑有点怪异,毕竟Docker较Vagrant的优势在于在生产环境下的高效性能。

关于Vagrant和Docker该用哪个,其实两者并不冲突,或者某些场景两个可以结合起来使用。
StackOverflow上有Vagrant作者Mitchell和Docker作者Solomon关于他们的对比和应用场景介绍。见Should I use Vagrant or Docker.io for creating an isolated environment?

Reference

c++资源管理经验谈

前言

编写中…

Step1: 用对象管理资源

Step2: RAII原则来申请和释放资源

  • 在构造的时候申请资源,在析构的时候释放资源

Step3: 管理好对象的生命周期

管理好对象的生命周期,对于单线程环境来说比较好办,注意几点就行了,但是对于多线程环境就比较难处理了。下面首先讲一些单线程环境下对象生命周期中需要注意的事项:

构造:

  • 不要在构造函数中暴露this指针,典型的不要调用观察者或者访问者接口,因为这个时候对象还没有构造完,不应该被调用

析构:

  • 不要在析构函数中抛出异常,异常会改变程序执行的顺序。
  • 父类析构函数应该定义为virtual,否则子类独有的资源不会被释放.

拷贝构造:

调用拷贝构造的时机:

  • 用一个对象构造另一个对象 Point fei();Point lun(fei);
  • 参数传递使用传值的方式的时候
  • 函数返回类型值的时候
  • Point fei = lun;(不会调用赋值操作符,只会调用拷贝构造)

赋值:

首先回顾一下赋值操作符的四个要点:

  • 检查是否为同一个地址if(this == &rhs) return *this
  • 参数为类型的常引用const Feature& rhs
  • 返回为*this
  • 在分配资源之前先回收清理原有的资源
1
2
3
4
5
Point fei();
Point lun = fei; /// 只会调用拷贝构造函数

Point foo();
foo = fei; /// 只会调用赋值操作符

多线程下对象生命周期的管理

聚合资源与组合资源

结论:

  1. 使用对象来管理资源,使用RAII原则来申请和释放资源
  2. 对于聚合资源对象的类,使用依赖注入和shared_ptr来构造
  • RAII(资源申请和释放更安全)
  • 依赖注入(构造更块,调用安全)

依赖注入和控制反转
依赖注入是一种将对象传入到某个类中的(注入),而不是让这个类自己创建并存储该对象的技巧,可将其作为控制反转概念的一种更为特殊的形式。举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
class RedisDataSource
{

RedisConnection* poDriver_;
Vector<OGRLayer*> layers_;

public:
RedisDataSource():
poDriver_(new RedisConnection("host=192.168.1.23 port=5432 db=2"))
{
/// 其他操作
}
}

上面的代码存在以下问题:

  1. 如果RedisConnection的构造函数发生了变化,或者修改了数据库的端口,那么就必须修改RedisDataSource来解决
  2. 从销量来看,RedisDataSource的构造过程较长,切每个RedisDataSource事例都需要创建一个RedisConnection对象
  3. RedisDataSource强依赖于RedisConnection的定义

然而如果采用依赖注入的方式:

1
2
3
4
5
6
7
8
9
10
11
12
class RedisDataSource
{

RedisConnection* poDriver_;
Vector<OGRLayer*> layers_;

public:
RedisDataSource(RedisConnection* poRedis):
poDriver_(poRedis)
{
/// 其他操作
}
}
  1. RedisDataSource只需要前置生命RedisConnection即可,依赖性相对较低
  2. 可以由RedisDataSource的工厂来创建RedisConnection并注入到RedisDataSource中,工厂负责维护RedisDataSource状态的正确性

对于上面依赖注入的例子,我们将Redis链接当作是资源的话,用对象来管理资源没有问题(RedisConnection类),使用依赖注入的方式还可以让对个RedisDataSource实例公用一个RedisConnection对象。但是这就要保证共享的RedisConnection生命周期的安全,也就是上面的Step3的要求,具体点就是资源对象在正确的时候析构。

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
class RedisDataSource : public DataSource
{
// RedisConnection* poDriver_;
std::shared_ptr<RedisConnection> poDriver_;

Vector<OGRLayer*> layers_;

public:
RedisDataSource(RedisConnection* poRedis)
{
/// 其他操作
if(poRedis)
{
poDriver_ = std::make_shared<RedisConnection>(poRedis);
}
}

// 用于共享本类的连接
RedisConnection* getConnection() const
{
return poDriver_.get();
}
}

class DataSourceFactory
{

public:
static DataSource* CreateDataSource(std::string dsType, std::string connInfo)
{
DataSource *poRet = NULL;
if(dsType == "redis")
{
RedisConnection* poRedisCon = new RedisConnection(connInfo);
if(poRedisCon)
{
poRet = new RedisDataSource(new RedisConnection(connInfo));
}
}
return poRet;
}

static DataSource* CreateDataSource(std::string dsType, Connection* poConn)
{
DataSource *poRet = NULL;
if(dsType == "redis")
{
if(poConn && poConn.getName() == "redis")
{
poRet = new RedisDataSource(new RedisConnection(connInfo));
}
else
{
// ...
}
}
else
{
// ...
}
return poRet;

}
}
1
2
3
4
5
6
RedisConnection* poRedis = 
DataSourceFactory::CreateDataSource("redis", "host=192.168.1.44...");

// 共用链接
RedisConnection* poRedis2 =
DataSOurceFactory::CreateDataSource("redis", poRedis.getConnection);

关于内存管理

C++中可能出现的内存问题:

  • 缓冲区溢出
  • 空悬指针/野指针
  • 重复释放
  • 内存泄漏
  • 不配对的new/delete
  • 内存碎片

关于类型安全

从指向父亲的指针转为指向孩子节点的指针

Reference

  • 《c++ API设计》by Martin Reddy
  • 《企业应用架构》by Martin Fowler
  • 《effective c++》
  • 《Linux多线程服务器编程》 by 陈硕

数据库存储管理

前言

本文首先总结一下大神级论文《Architecture of database system》中关于存储管理的介绍。因为我用PostgreSQL比较多一点,查了一些资料,之后介绍一下PostgreSQL的存储管理方面的内容。

Joseph M 讲存储管理

在“Architecture of Database System”中,Joseph介绍说在如今的商业DBMS中,主要有两种基本类型的DBMS存储管理器:

  • DBMS直接与底层的面向磁盘的块模式设备驱动程序进行交互(又称为原始模式访问)
  • DBMS使用标准的OS文件系统设施

以上两中存储管理的不同会从空间和时间上同时影响DBMS控制存储的能力:

  • 空间:数据在物理磁盘上的位置
  • 时间:什么时候被物理的写到磁盘

空间控制 (磁盘, 外存)

从磁盘中读取和写入数据时,顺序读写带宽要比随机读写带宽快10到100倍,并且这个差距还在增加。因此,对于DBMS存储管理器来说,如何把数据块放置在磁盘上就显得尤其重要,从而使得需要访问大量数据的查询可以顺序地访问磁盘。

原始访问模式

DBMS控制局部性最好的方式,就是将数据直接存储到“原始”磁盘设备中,完全绕过文件系统。这种方法可行,但是有一些缺点:

  • 它需要数据库管理员将整个磁盘分区都分配给数据库管理系统。
  • “原始磁盘”的访问接口往往是与特定操作系统相关,这使得DBMS的可移植性变的很差。
文件系统访问

原始磁盘访问的一种替代方式是,由DBMS在操作系统的文件管理系统中创建一个非常大的文件,然后采用数据在文件中的地址偏移量来定位数据。
在大多数流行的文件系统中,如果你分配一个非常大的文件到一个空磁盘上,文件中的地址将会和存储位置的物理临近性非常吻合。因此,这是一个原始磁盘访问的很好的金丝方法,而不需要直接访问原始设备借口。

性能比较

运行TPC-C测试基准时候,文件系统访问相交于原始访问只有6%性能降低,而对于较少包含密集IO的工作负载而言,几乎没有负面影响。DB2网上的一份测试报告现实,当使用直接IO(DIO)和它的变种并发IO(CIO)时,文件系统开销可以降低至1%。因此,数据库管理系统厂商通常不再推荐原始数据存储,而且很少用户会使用这种配置。

时间控制 (缓冲,内存)

一个DBMS还必须控制数据什么时候被物理地写到磁盘中,DBMS中包含了关键的逻辑程序,它可以判断什么时候把数据写入磁盘。大多数操作系统的文件系统还提供内置的IO缓冲机制,来决定何时读取和写入文件块。
如果DBMS在执行写操作时使用标准的文件系统借口,操作系统缓冲机制将会打乱DBMS逻辑程序的意图,因为,操作系统缓冲机制会悄悄地推迟DBMS写操作或者重新排序写操作。不能保证对磁盘写操作的时间的显示控制,这可能带来一下问题:

  • 数据库ACID事务承诺的正确性 (不能保证WAL已经写入到磁盘)
  • 性能(文件系统基于物理位置局部性的“预读取”和“后写入”不适合DBMS,DBMS使用比如B树叶子节点的索引局部性,不一定是聚集索引,所以不一定物理相邻)
  • 双缓冲和内存拷贝的昂贵CPU开销 (文件系统到RDBMS缓冲的拷贝, 可以使用POSIX mmap或特定平台的DIO和CIO来避免对文件的双缓冲)
缓冲管理

为了提供对数据库页面的有效访问,每个数据库管理系统会在自己的内存空间中实现一个大型共享缓冲池。
缓冲池会被组织成一个帧数组,其中,每一帧是内存中的一段区域,帧的大小是数据库磁盘块的大小。块从磁盘直接复制到缓冲池中,不会发生格式的变化,在内存中也是以这种原生的格式进行修改操作,然后,写回到磁盘。

和缓冲池中的帧数相关联的是一个哈希表,它会对以下内容进行哈希映射:

  • 把内存中当前的页面编号映射到它们在帧表中的位置
  • 页面在备份磁盘存储中的位置
  • 关于该页面的一些元数据(脏标记位:标识从磁盘读出来之后是否发生改变;页面替换策略所需要的信息;引脚计数器)

PostgreSQL 存储管理

用PL/pgSQL对osm数据进行矢量分级

执行总流程介绍

Perform-sql-updates.sql是总执行脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

# subsequent sql depends on functions installed
echo "Creating functions..."
psql $@ -f functions.sql
echo "done."

# apply updates in parallel across tables
echo -e "\nApplying updates in parallel across tables..."
psql $@ -f apply-updates-non-planet-tables.sql &
psql $@ -f apply-planet_osm_polygon.sql &
psql $@ -f apply-planet_osm_line.sql &
psql $@ -f apply-planet_osm_point.sql &
wait
echo "done."

echo -e '\nApplying triggers...'
psql $@ -f triggers.sql
echo 'done.'

echo -e "\nAll updates complete. Exiting."

这是总的执行脚本,干了一下几件事:

  1. 导入了一些函数,这些函数在之后的sql处理中会用到
  2. 对point, line, polygon几个表分别进行了处理,会在下面详细介绍
  3. 导入必要的触发器

对non-planet表进行处理

apply-updates-non-planet-tables.sql

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
DO $$
BEGIN

-- add way_area columns to all tables that use it
PERFORM mz_add_area_column('ne_110m_ocean', 'way_area', 'the_geom');

PERFORM mz_add_area_column('ne_110m_lakes', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_50m_ocean', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_50m_lakes', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_50m_playas', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_10m_ocean', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_10m_lakes', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_10m_playas', 'way_area', 'the_geom');
PERFORM mz_add_area_column('water_polygons', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_10m_urban_areas', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_50m_urban_areas', 'way_area', 'the_geom');
PERFORM mz_add_area_column('ne_10m_parks_and_protected_lands', 'way_area', 'the_geom');

-- way_area indexes
PERFORM mz_create_index_if_not_exists('ne_110m_ocean_wayarea_index', 'ne_110m_ocean', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_110m_lakes_wayarea_index', 'ne_110m_lakes', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_50m_ocean_wayarea_index', 'ne_50m_ocean', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_50m_lakes_wayarea_index', 'ne_50m_lakes', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_50m_playas_wayarea_index', 'ne_50m_playas', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_50m_urban_areas_way_area_index', 'ne_50m_urban_areas', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_10m_ocean_wayarea_index', 'ne_10m_ocean', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_10m_lakes_wayarea_index', 'ne_10m_lakes', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_10m_playas_wayarea_index', 'ne_10m_playas', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_10m_urban_areas_way_area_index', 'ne_10m_urban_areas', 'way_area');
PERFORM mz_create_index_if_not_exists('ne_10m_parks_and_protected_lands_way_area_index', 'ne_10m_parks_and_protected_lands', 'way_area');
PERFORM mz_create_index_if_not_exists('water_polygons_wayarea_index', 'water_polygons', 'way_area');

-- additional updates
PERFORM mz_create_index_if_not_exists('ne_10m_populated_places_scalerank_index', 'ne_10m_populated_places', 'scalerank');

PERFORM AddGeometryColumn('ne_50m_urban_areas', 'mz_centroid', 900913, 'Geometry', 2);
UPDATE ne_50m_urban_areas SET mz_centroid=ST_Centroid(the_geom);
CREATE INDEX ne_50m_urban_areas_centroid_index ON ne_50m_urban_areas USING gist(mz_centroid);

PERFORM AddGeometryColumn('ne_10m_parks_and_protected_lands', 'mz_centroid', 900913, 'Geometry', 2);
UPDATE ne_10m_parks_and_protected_lands SET mz_centroid=ST_Centroid(the_geom);
CREATE INDEX ne_10m_parks_and_protected_lands_centroid_index ON ne_10m_parks_and_protected_lands USING gist(mz_centroid);

PERFORM AddGeometryColumn('ne_10m_urban_areas', 'mz_centroid', 900913, 'Geometry', 2);
UPDATE ne_10m_urban_areas SET mz_centroid=ST_Centroid(the_geom);
CREATE INDEX ne_10m_urban_areas_centroid_index ON ne_10m_urban_areas USING gist(mz_centroid);

END $$;

对planet_osm_polygon表做处理

apply_planet_osm_polygon.sql

对于面的处理向较与point和line来说算是比较复杂,因为需要对面进行抽希处理。抽希处理需要在两个方面进行:

  • 去掉一些不必要的Feature。
  • 对与一个Feature,去掉其Geometry的一些点。
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

DO $$
BEGIN

--------------------------------------------------------------------------------
-- planet_osm_polygon
--------------------------------------------------------------------------------

-- indexes on existing columns
PERFORM mz_create_index_if_not_exists('planet_osm_polygon_wayarea_index', 'planet_osm_polygon', 'way_area');

PERFORM mz_create_partial_index_if_not_exists('planet_osm_polygon_building_index', 'planet_osm_polygon', 'building', 'building IS NOT NULL');
PERFORM mz_create_partial_index_if_not_exists('planet_osm_polygon_admin_level_index', 'planet_osm_polygon', 'admin_level', 'boundary = ''administrative''');

-- indexes on functions
CREATE INDEX planet_osm_polygon_is_building_or_part_index ON planet_osm_polygon(mz_calculate_is_building_or_part(building, "building:part")) WHERE mz_calculate_is_building_or_part(building, "building:part") = TRUE;
CREATE INDEX planet_osm_polygon_is_water_index ON planet_osm_polygon(mz_calculate_is_water("waterway", "natural", "landuse")) WHERE mz_calculate_is_water("waterway", "natural", "landuse") = TRUE;

-- update polygon table to add centroids
ALTER TABLE planet_osm_polygon ADD COLUMN mz_is_landuse BOOLEAN;
ALTER TABLE planet_osm_polygon ADD COLUMN mz_centroid GEOMETRY;

-- at the moment we only add centroids to landuse features
UPDATE planet_osm_polygon SET
mz_is_landuse = TRUE,
mz_centroid = ST_Centroid(way)
WHERE mz_calculate_is_landuse("landuse", "leisure", "natural", "highway", "amenity", "aeroway") = TRUE;


-- indexes for centroid queries
CREATE INDEX planet_osm_polygon_is_landuse_col_index ON planet_osm_polygon(mz_is_landuse) WHERE mz_is_landuse=TRUE;
CREATE INDEX planet_osm_polygon_centroid_landuse_index ON planet_osm_polygon USING gist(mz_centroid) WHERE mz_is_landuse=TRUE;

END $$;

对planet_osm_point表做处理

apply-planet_osm_point.sql

1
2
3
4
5
6
7
8
9
10
11
12
DO $$
BEGIN

--------------------------------------------------------------------------------
-- planet_osm_point
--------------------------------------------------------------------------------

CREATE INDEX planet_osm_point_place_index ON planet_osm_point(place) WHERE name IS NOT NULL AND place IN ('city', 'continent', 'country', 'county', 'district', 'hamlet', 'island', 'isolated_dwelling', 'lake', 'locality', 'neighbourhood', 'ocean', 'province', 'sea', 'state', 'suburb', 'town', 'village');


CREATE INDEX planet_osm_point_level_index ON planet_osm_point(mz_calculate_poi_level("aerialway", "aeroway", "amenity", "barrier", "highway", "historic", "leisure", "lock", "man_made", "natural", "power", "railway", "shop", "tourism", "waterway")) WHERE mz_calculate_poi_level("aerialway", "aeroway", "amenity", "barrier", "highway", "historic", "leisure", "lock", "man_made", "natural", "power", "railway", "shop", "tourism", "waterway") IS NOT NULL;

END $$;

对planet_osm_line表做处理

apply-planet_osm_line.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DO $$
BEGIN

--------------------------------------------------------------------------------
-- planet_osm_line
--------------------------------------------------------------------------------

-- indexes on existing columns
PERFORM mz_create_partial_index_if_not_exists('planet_osm_line_waterway', 'planet_osm_line', 'waterway', 'waterway IS NOT NULL');


-- indexes on functions
CREATE INDEX planet_osm_line_road_level_index ON planet_osm_line(mz_calculate_road_level(highway, railway, aeroway)) WHERE mz_calculate_road_level(highway, railway, aeroway) IS NOT NULL;

END $$;

导入的函数

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
--------------------------------------------------------------------------------
-- mz_does_index_exist()
--------------------------------------------------------------------------------

-- used to check whether an index exists before adding
CREATE OR REPLACE FUNCTION mz_does_index_exist(index_name text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM pg_class AS c
INNER JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE c.relname = index_name
);

END;
$$ LANGUAGE plpgsql STABLE;

--------------------------------------------------------------------------------
-- mz_does_trigger_exist()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_does_trigger_exist(trigger_name text, table_name text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS(
SELECT 1 FROM pg_class AS c
INNER JOIN pg_namespace AS n ON n.oid = c.relnamespace
INNER JOIN pg_trigger AS t ON t.tgrelid = c.oid
WHERE c.relname = table_name
AND t.tgname = trigger_name
);

END;
$$ LANGUAGE plpgsql STABLE;

--------------------------------------------------------------------------------
-- mz_create_index_if_not_exists()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_create_index_if_not_exists(index_name text, table_name text, column_name text)
RETURNS VOID AS $$
BEGIN
IF NOT mz_does_index_exist(index_name) THEN
EXECUTE 'CREATE INDEX ' ||
quote_ident(index_name) || ' ON ' || quote_ident(table_name) ||
'(' || quote_ident(column_name) || ')';

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_create_partial_index_if_not_exists()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_create_partial_index_if_not_exists(index_name text, table_name text, column_name text, where_clause text)
RETURNS VOID AS $$
BEGIN
IF NOT mz_does_index_exist(index_name) THEN
EXECUTE 'CREATE INDEX ' ||
quote_ident(index_name) || ' ON ' || quote_ident(table_name) ||
'(' || quote_ident(column_name) || ') WHERE ' || where_clause;

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_create_trigger_if_not_exists()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_create_trigger_if_not_exists(
trigger_name text, table_name text, function_name text)
RETURNS VOID AS $$
BEGIN
IF NOT mz_does_trigger_exist(trigger_name, table_name) THEN
EXECUTE 'CREATE TRIGGER ' || quote_ident(trigger_name) ||
' BEFORE INSERT OR UPDATE ON ' || quote_ident(table_name) ||
' FOR EACH ROW EXECUTE PROCEDURE ' || quote_ident(function_name) || '()';

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_does_column_exist()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_does_column_exist(input_table_name text, input_column_name text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name=input_table_name AND column_name=input_column_name
);

END;
$$ LANGUAGE plpgsql STABLE;

--------------------------------------------------------------------------------
-- mz_add_simplified_geometry_column()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_add_simplified_geometry_column(
table_name text, column_name text, existing_geom_column_name text, geom_type text, tolerance float, where_clause text default NULL)
RETURNS VOID AS $$
DECLARE v_where_clause TEXT DEFAULT '';

DECLARE v_index_where TEXT DEFAULT '';
BEGIN
IF NOT mz_does_column_exist(table_name, column_name) THEN
PERFORM AddGeometryColumn(table_name, column_name, 900913, geom_type, 2);

IF where_clause IS NOT NULL THEN
v_where_clause := ' AND ' || where_clause;
v_index_where := ' WHERE ' || where_clause;
END IF;
EXECUTE 'UPDATE ' || quote_ident(table_name) ||
' SET ' || quote_ident(column_name) ||
'=ST_SimplifyPreserveTopology(' || quote_ident(existing_geom_column_name) ||
', ' || tolerance || ')' ||
' WHERE ' || quote_ident(existing_geom_column_name) || ' IS NOT NULL' ||
v_where_clause;

EXECUTE 'CREATE INDEX ' || quote_ident(table_name || '_' || column_name) ||
' ON ' || quote_ident(table_name) || ' USING gist(' || quote_ident(column_name) || ')' ||
v_index_where;

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_safe_convert_to_float()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_safe_convert_to_float(v_input text)
RETURNS FLOAT AS $$
DECLARE v_float_value FLOAT DEFAULT NULL;

BEGIN
BEGIN
v_float_value := TO_NUMBER(
REPLACE(REPLACE(v_input, ';', '.'), ',', '.'),
'999999D99S') AS FLOAT;

EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
RETURN v_float_value;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_normalize_id()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_normalize_id(id bigint, geom Geometry)
RETURNS TEXT AS $$
BEGIN
IF id < 0 THEN
RETURN Substr(MD5(ST_AsBinary(geom)), 1, 10);

ELSE
RETURN id::text;
END IF;
END;
$$ LANGUAGE plpgsql STABLE;

--------------------------------------------------------------------------------
-- mz_add_normalized_id()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_add_normalized_id(
table_name text, column_name text, geom_name text, prev_id_column_name text)
RETURNS VOID AS $$
BEGIN
IF NOT mz_does_column_exist(table_name, column_name) THEN
EXECUTE 'ALTER TABLE ' || quote_ident(table_name) ||
' ADD COLUMN ' || quote_ident(column_name) ||
' TEXT';

EXECUTE 'UPDATE ' || quote_ident(table_name) ||
' SET ' || quote_ident(column_name) ||
' = mz_normalize_id(' || quote_ident(prev_id_column_name) ||
', ' || quote_ident(geom_name) || ')' ||
' WHERE ' || quote_ident(prev_id_column_name) || ' IS NOT NULL';

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_add_area_column()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_add_area_column(table_name text, column_name text, geom_name text)
RETURNS VOID AS $$
BEGIN
IF NOT mz_does_column_exist(table_name, column_name) THEN
EXECUTE 'ALTER TABLE ' || quote_ident(table_name) ||
' ADD COLUMN ' || quote_ident(column_name) ||
' REAL';

EXECUTE 'UPDATE ' || quote_ident(table_name) || ' SET ' || quote_ident(column_name) ||
' = ST_Area(' || quote_ident(geom_name) || ') ' ||
'WHERE ' || quote_ident(geom_name) || ' IS NOT NULL';

END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_calculate_is_landuse()
--------------------------------------------------------------------------------
-- functions to encapsulate logic for calculating new columns

CREATE OR REPLACE FUNCTION mz_calculate_is_landuse(
landuse_val text, leisure_val text, natural_val text, highway_val text, amenity_val text, aeroway_val text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN
landuse_val IN ('park', 'forest', 'residential', 'retail', 'commercial',
'industrial', 'railway', 'cemetery', 'grass', 'farmyard',
'farm', 'farmland', 'wood', 'meadow', 'village_green',
'recreation_ground', 'allotments', 'quarry')
OR leisure_val IN ('park', 'garden', 'playground', 'golf_course', 'sports_centre',
'pitch', 'stadium', 'common', 'nature_reserve')
OR natural_val IN ('wood', 'land', 'scrub', 'wetland', 'glacier')
OR highway_val IN ('pedestrian', 'footway')
OR amenity_val IN ('university', 'school', 'college', 'library', 'fuel',
'parking', 'cinema', 'theatre', 'place_of_worship', 'hospital')
OR aeroway_val IN ('runway', 'taxiway', 'apron');

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_calculate_poi_level()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_calculate_poi_level(
aerialway_val text,
aeroway_val text,
amenity_val text,
barrier_val text,
highway_val text,
historic_val text,
leisure_val text,
lock_val text,
man_made_val text,
natural_val text,
power_val text,
railway_val text,
shop_val text,
tourism_val text,
waterway_val text
)
RETURNS SMALLINT AS $$
BEGIN
RETURN (
CASE WHEN aeroway_val IN ('aerodrome', 'airport') THEN 9
WHEN natural_val IN ('peak', 'volcano') THEN 11
WHEN railway_val IN ('station') THEN 12
WHEN (aerialway_val IN ('station')
OR railway_val IN ('halt', 'tram_stop')
OR tourism_val IN ('alpine_hut')) THEN 13
WHEN (natural_val IN ('spring')
OR railway_val IN ('level_crossing')) THEN 14
WHEN (amenity_val IN ('hospital', 'parking')
OR barrier_val IN ('gate')
OR highway_val IN ('gate', 'mini_roundabout')
OR lock_val IN ('yes')
OR man_made_val IN ('lighthouse', 'power_wind')
OR natural_val IN ('cave_entrance')
OR power_val IN ('generator')
OR waterway_val IN ('lock')) THEN 15
WHEN (aeroway_val IN ('helipad')
OR amenity_val IN ('biergarten', 'bus_station', 'bus_stop', 'car_sharing',
'picnic_site', 'place_of_worship',
'prison', 'pub', 'recycling', 'shelter')
OR barrier_val IN ('block', 'bollard', 'lift_gate')
OR highway_val IN ('bus_stop', 'ford')
OR historic_val IN ('archaeological_site')
OR man_made_val IN ('windmill')
OR natural_val IN ('tree')
OR shop_val IN ('department_store', 'supermarket')
OR tourism_val IN ('camp_site', 'caravan_site', 'information', 'viewpoint')) THEN 16
WHEN (aeroway_val IN ('gate')
OR amenity_val IN (
'atm', 'bank', 'bar', 'bicycle_rental',
'cafe', 'cinema', 'courthouse', 'drinking_water', 'embassy', 'emergency_phone',
'fast_food', 'fire_station', 'fuel', 'library', 'pharmacy',
'police', 'post_box', 'post_office', 'restaurant', 'telephone', 'theatre',
'toilets', 'veterinary')
OR highway_val IN ('traffic_signals')
OR historic_val IN ('memorial')
OR leisure_val IN ('playground', 'slipway')
OR man_made_val IN ('mast', 'water_tower')
OR shop_val IN ('bakery', 'bicycle', 'books', 'butcher', 'car', 'car_repair',
'clothes', 'computer', 'convenience',
'doityourself', 'dry_cleaning', 'fashion', 'florist', 'gift',
'greengrocer', 'hairdresser', 'jewelry', 'mobile_phone',
'optician', 'pet')
OR tourism_val IN ('bed_and_breakfast', 'chalet', 'guest_house',
'hostel', 'hotel', 'motel', 'museum')) THEN 17
WHEN (amenity_val IN ('bench', 'waste_basket')
OR railway_val IN ('subway_entrance')) THEN 18
ELSE NULL END
);

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_calculate_road_level()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_calculate_road_level(highway_val text, railway_val text, aeroway_val text)
RETURNS SMALLINT AS $$
BEGIN
RETURN (
CASE WHEN highway_val IN ('motorway') THEN 7
WHEN highway_val IN ('trunk', 'primary', 'secondary') THEN 10
WHEN (highway_val IN ('tertiary')
OR aeroway_val IN ('runway', 'taxiway')) THEN 11
WHEN highway_val IN ('motorway_link', 'trunk_link', 'residential', 'unclassified', 'road') THEN 12
WHEN highway_val IN ('primary_link', 'secondary_link') THEN 13
WHEN (highway_val IN ('tertiary_link', 'minor')
OR railway_val IN ('rail', 'subway')) THEN 14
WHEN (highway_val IN ('service', 'footpath', 'track', 'footway', 'steps', 'pedestrian', 'path', 'cycleway', 'living_street')
OR railway_val IN ('tram', 'light_rail', 'narrow_gauge', 'monorail')) THEN 15
ELSE NULL END
);

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_calculate_road_sort_key()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_calculate_road_sort_key(
layer_val text, bridge_val text, tunnel_val text, highway_val text, railway_val text, aeroway_val text)
RETURNS FLOAT AS $$
DECLARE v_layer_as_float FLOAT DEFAULT NULL;

BEGIN
v_layer_as_float := mz_safe_convert_to_float(layer_val);

RETURN (
(CASE WHEN v_layer_as_float IS NOT NULL THEN 1000 * v_layer_as_float
ELSE 0
END)

+

--
-- Bridges and tunnels have an implicit physical layering.
--
(CASE WHEN bridge_val IN ('yes', 'true') THEN 100
WHEN tunnel_val IN ('yes', 'true') THEN -100
ELSE 0
END)

+

--
-- Large roads are drawn on top of smaller roads.
--
(CASE WHEN highway_val IN ('motorway') THEN 0
WHEN railway_val IN ('rail', 'tram', 'light_rail', 'narrow_guage', 'monorail') THEN -.5
WHEN highway_val IN ('trunk') THEN -1
WHEN highway_val IN ('primary') THEN -2
WHEN highway_val IN ('secondary') THEN -3
WHEN aeroway_val IN ('runway') THEN -3
WHEN aeroway_val IN ('taxiway') THEN -3.5
WHEN highway_val IN ('tertiary') THEN -4
WHEN highway_val LIKE '%_link' THEN -5
WHEN highway_val IN ('residential', 'unclassified', 'road') THEN -6
WHEN highway_val IN ('unclassified', 'service', 'minor') THEN -7
WHEN railway_val IN ('subway') THEN -8
ELSE -9 END)
);

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_calculate_is_water()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_calculate_is_water(
waterway_val text, natural_val text, landuse_val text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN (
waterway_val IN ('riverbank', 'dock')
OR natural_val IN ('water')
OR landuse_val IN ('basin', 'reservoir')
);

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_calculate_is_building_or_part()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_calculate_is_building_or_part(
building_val text, buildingpart_val text)
RETURNS BOOLEAN AS $$
BEGIN
RETURN (building_val IS NOT NULL OR buildingpart_val IS NOT NULL);

END;
$$ LANGUAGE plpgsql IMMUTABLE;


-- functions to temporarily enable and disable triggers
-- prevents them from firing while executing mass updates

--------------------------------------------------------------------------------
-- mz_tables_with_triggers()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_tables_with_triggers()
RETURNS TEXT[] AS $$
BEGIN
RETURN ARRAY['planet_osm_polygon', 'planet_osm_line', 'planet_osm_point', 'water_polygons'];

END;
$$ LANGUAGE plpgsql IMMUTABLE;

--------------------------------------------------------------------------------
-- mz_disable_triggers()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_disable_triggers()
RETURNS VOID AS $$
DECLARE table_name TEXT;

BEGIN
FOREACH table_name IN ARRAY mz_tables_with_triggers()
LOOP
EXECUTE 'ALTER TABLE ' || quote_ident(table_name) ||
' DISABLE TRIGGER USER';

END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

--------------------------------------------------------------------------------
-- mz_enable_triggers()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_enable_triggers()
RETURNS VOID AS $$
DECLARE table_name TEXT;

BEGIN
FOREACH table_name IN ARRAY mz_tables_with_triggers()
LOOP
EXECUTE 'ALTER TABLE ' || quote_ident(table_name) ||
' ENABLE TRIGGER USER';

END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

导入的触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--------------------------------------------------------------------------------
-- mz_trigger_function_landuse()
--------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION mz_trigger_function_landuse()
RETURNS TRIGGER AS $$
BEGIN
IF mz_calculate_is_landuse(NEW."landuse", NEW."leisure", NEW."natural", NEW."highway", NEW."amenity", NEW."aeroway") then
NEW.mz_is_landuse := TRUE;

NEW.mz_centroid := ST_Centroid(NEW.way);
ELSE
NEW.mz_is_landuse := NULL;
NEW.mz_centroid := NULL;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE;

DO $$
BEGIN

PERFORM mz_create_trigger_if_not_exists('mz_trigger_landuse', 'planet_osm_polygon', 'mz_trigger_function_landuse');


END $$;

拆分瓦片处理

split_to_tiles.sql

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
-- This file contains functions (see `mz_SplitIntoTiles()`) for splitting a
-- table of polygons into uniform tiles. It was useful in integrating the
-- ne_10m_land Natural Earth dataset, which, downloaded straight from the
-- source, stores all seven continents in one monolithic multipolygon. That
-- makes `st_intersects()`/`st_intersection()` take absurdly long, since they
-- can't take advantage of any spatial indexes (which *would* help if it were
-- split up into many smaller polygons), and thus clogs up tile queries, which
-- depend on both of those functions. Hence, we split it into 10km x 10km tiles
-- using `mz_SplitIntoTiles()`.

--------------------------------------------------------------------------------
-- mz_CreateGrid()
--------------------------------------------------------------------------------
-- A function that creates a table containing a grid of cells, taken from here:
-- http://gis.stackexchange.com/questions/16374/how-to-create-a-regular-polygon-grid-in-postgis

create or replace function mz_CreateGrid(
numberX integer,
numberY integer,
xsize float8,
ysize float8,
x0 float8 default 0,
y0 float8 default 0,
out "row" integer,
out col integer,
out the_geom geometry
)
returns setof record as
$$
select
rowInd + 1 as row,
colInd + 1 as col,
st_Translate(cell, colInd * xsize + x0, rowInd * ysize + y0) as the_geom
from
generate_series(0, numberY - 1) as rowInd,
generate_series(0, numberX - 1) as colInd,
(select (format('POLYGON((0 0, 0 %s, %s %s, %s 0,0 0))', ysize, xsize, ysize, xsize))::geometry as cell) as foo;

$$ language sql immutable strict;

--------------------------------------------------------------------------------
-- mz_SplitIntoTiles()
--------------------------------------------------------------------------------
-- Split the polygons in a table called `table_name` into uniformly sized tiles
-- in a table called `${table_name}_tiles`.

create or replace function mz_SplitIntoTiles(
table_name text,
tile_size_meters integer,
geom_column_name text default 'the_geom'
)
returns void as
$$
declare
grid_table_name text := table_name || '_grid';

table_bbox box2d;
num_tiles_x integer;
num_tiles_y integer;
begin
execute format('select st_extent(%s) from %s', geom_column_name, table_name) into table_bbox;

num_tiles_x = ceiling(
(st_xmax(table_bbox) - st_xmin(table_bbox)) / (tile_size_meters :: float)
);
num_tiles_y = ceiling(
(st_ymax(table_bbox) - st_ymin(table_bbox)) / (tile_size_meters :: float)
);

-- Create a table containing a grid with cells of length/width
-- `tile_size_meters`, covering the entire extent of `table_name`.
execute format(
'create table %s as
select *
from MZ_CreateGrid(%s, %s, %s, %s, %s, %s);',

grid_table_name, num_tiles_x, num_tiles_y,
tile_size_meters, tile_size_meters,
st_xmin(table_bbox), st_ymin(table_bbox)
);

perform UpdateGeometrySRID(grid_table_name, 'the_geom', 900913);
execute format('create index %s_index on %1$s using gist(the_geom)', grid_table_name);

execute format('create sequence %1$s_ids;', table_name);

-- Intersect the gridded cells with the polygons in `table_name`,
-- storing the now-tiled polygons in `${table_name}_tiles`. Assign each
-- a unique `gid`.
execute format(
'create table %1$s_tiles as
select
nextval(''%1$s_ids'')::int as gid,
st_intersection(%1$s.%3$s, %2$s.the_geom) as the_geom
from %1$s
join %2$s
on (
st_isvalid(%1$s.%3$s) and
st_intersects(%1$s.%3$s, %2$s.the_geom)
);',

table_name, grid_table_name, geom_column_name
);


execute 'drop table ' || grid_table_name;
end
$$ language plpgsql;

用python写一个osm2rdbms导入工具

代码

首先贴出代码:

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
from osgeo import ogr
import pyodbc


def main():
src_conn_info = "../shape_eg_data/pline.shp"
dst_conn_info = "DSN=svde;SERVER=127.0.0.1;TCP_PORT=5236;UID=SYSDBA;PWD=SYSDBA;"

ogr_dm_type_mapping = {
'0' : 'integer',
'1' : 'double',
'2' : 'varchar',
'3' : 'varchar',
'4' : 'varchar',
'5' : 'varchar',
}

ogr_geom_type_mapping = {
'0' : 'Point',
'1' : 'LineString',
'2' : 'Polygon',
'3' : 'MultiPoint',
'4' : 'MultiLineString',
'5' : 'MultiPolygon',
}

dm_conn = pyodbc.connect(dst_conn_info)
cursor = dm_conn.cursor()

# Ensure metadata talbes exsits
try:
cursor.execute("""select 1 from svde_metadata""")
except pyodbc.ProgrammingError:
cursor.execute("""create table svde_metadata(
table_name varchar primary key,
geom_column_name varchar not null,
geom_type varchar,
geom_dimension varchar,
geom_srid varchar)""")

cursor.execute("""create table svde_srs(
srid varchar primary key,
auth_name varchar,
auth_srid varchar,
srtext varchar not null,
proj4text varchar)""")

cursor.execute("""create table svde_column_defn(
cid varchar,
table_name varchar not null,
column_name varchar not null,
column_type varchar not null,
primary key (table_name, column_name))""")

cursor.commit()
else:
print "metadata tables already exists in database!"
finally:
pass


# Create table for shapefile layer

ds = ogr.Open(src_conn_info)
layer = ds.GetLayer()
layer_defn = layer.GetLayerDefn()
field_count = layer_defn.GetFieldCount()

fields_type_dict = {}
fields_defn_serial = ""
fields_comma_serial = ""
for i in range(0, field_count-1):
field_name = layer_defn.GetFieldDefn(i).GetName()
field_type = layer_defn.GetFieldDefn(i).GetType()
fields_type_dict[field_name] = ogr_dm_type_mapping[str(field_type)]
fields_defn_serial += field_name + ' ' + ogr_dm_type_mapping[str(field_type)] + ','
fields_comma_serial += field_name + ','

fields_defn_serial = fields_defn_serial.rstrip(', ')
fields_comma_serial = fields_comma_serial.rstrip(', ')

table_name = layer_defn.GetName()
print "fields_comma_serial" + fields_comma_serial
print """create table %s (
%s,
fid varchar primary key,
geom varchar)""" % (table_name, fields_defn_serial)


try:
cursor.execute("""select 1 from %s""" % (table_name) )
except pyodbc.ProgrammingError:
# 1] create table for layer
# 2] register geom colum in svde_metadata
# 3] register column in svde_column_defn
cursor.execute("""create table %s (
%s,
fid varchar primary key,
geom varchar)""" % (table_name, fields_defn_serial) )

cursor.commit()

geom_type = ogr_geom_type_mapping[str(layer_defn.GetGeomType())]
geom_dimension = str(2)
geom_srid = str(900913)

print """insert into svde_metadata(table_name, geom_column_name, geom_type,
geom_dimension, geom_srid) values (%s, 'geom', %s, %s, %s)
""" % (repr(table_name), repr(geom_type), repr(geom_dimension), repr(geom_srid) )


cursor.execute("""insert into svde_metadata(table_name, geom_column_name, geom_type,
geom_dimension, geom_srid) values (%s, 'geom', %s, %s, %s)
""" % (repr(table_name), repr(geom_type), repr(geom_dimension), repr(geom_srid) ) )


print fields_type_dict
for cname, cvalue in fields_type_dict.items():
cursor.execute("""insert into svde_column_defn( table_name, column_name, column_type)
values ('%s', '%s', '%s')""" % (table_name, cname, str(cvalue)) )

cursor.commit()

else:
print "table " + str(table_name) + "already exsit"
finally:
pass


# Read shapefile layer into OGR objects and Persist ogr objects into dameng

fid = 0
for feat in layer:
fid = fid + 1
values_comma_serial = ""
for i in range(0, field_count-1):
value = feat.GetFieldAsString(i)
values_comma_serial += repr(value) + ','

values_comma_serial = values_comma_serial.rstrip(',')
geom_wkt = feat.GetGeometryRef().ExportToWkt()

print """insert into %s(%s, fid, geom) values (%s, '%s' , '%s')""" % (
table_name, fields_comma_serial, values_comma_serial, str(fid), geom_wkt)

cursor.execute("""insert into %s(%s, fid, geom) values (%s, '%s' , '%s')""" % (
table_name, fields_comma_serial, values_comma_serial, str(fid), geom_wkt) )
# allowed most 100 sql statement, so should not commit after execute all
cursor.commit()


if __name__ == '__main__':
main()

C++_API设计总结

GIS空间索引技术总结

GIS索引技术总结

  • 网格索引
  • 四叉树索引
  • R树系列索引

网格索引

四叉树索引

R树系列索引

设计模式总结

服务器IO模型总结

阻塞与非阻塞、同步与异步

首先说说一下几个大家常说的IO模型:

  • Blocking
  • Non-blocking
  • Synchronize
  • Asynchronize

这几个概念还是很容易区分的,举个例子说明一切:
代码编写上要做a->b->c三件事,行a的过程中要进行IO,比如说从磁盘读文件,IO速度相对于cpu的内存操作来说慢很多,一般都是由DMA来讲数据从磁盘搬运到内存的内核空间,所以按理来说cpu需要等DMA把数据搬完了才能做接下来的对这些数据的操作。

Blocking 的做法:
执行a,发现需要进行IO,内核将该进程挂起,等DMA把数据搬完了,内核把该进程唤醒,放在就绪队列,等待分配cpu并进行执行

Non-blocking的做法
执行a, 发现要进行IO,然后不同的循环检查DMA有没有搬完,有点像自旋锁,进程不挂起,cpu一直进行检查运算,等DMA搬完之后,cpu继续执行b操作。

Synchronize的做法
同步IO只是说明,在a完成之前不能进行后面的b操作,所以以上说的阻塞和非阻塞情况都是属于同步IO,后面介绍的IO复用也是同步的做法,但是IO复用不一定是阻塞的,根据IO复用API参数的配置可以配置成阻塞的,也可以配置称非阻塞的。

Asynchronize的做法
不必非要等a执行完了才能执行b操作,执行a不是要等DMA搬运数据啊,在DMA搬运数据的时候cpu可以执行后面的b操作,有可能在执行完b操作之后,DMA数据搬完了,再去处理a。

除了异步IO、还有很多使用异步思想的技术,一下列出我能想到的

  • web前端中的各种事件,JS代码处理当鼠标悬浮,右击等时间之后的处理。
  • MFC/Cocoa/IOS/Android中的基于UI的用户交互操作都是基于这种异步事件思想,用户的交互触发某个事件,这个事件事先绑定了事件处理函数,也就是回调。
  • NodeJS中的Event Loop

IO复用

IO复用属于同步IO,可能是阻塞,也可能是非阻塞。

  • select
  • poll
  • epoll (Linux)
  • kqueue (FreeBSD)

select模型

  1. 最大并发数限制,因为一个进程所打开的FD(文件描述符)是有限制的,由FD_SETSIZE设置,默认值是1024/2048,因此Select模型的最大并发数就被相应限制了。自己改改这个FD_SETSIZE?想法虽好,可是先看看下面吧…
  2. 效率问题,select每次调用都会线性扫描全部的FD集合,这样效率就会呈现线性下降,把FD_SETSIZE改大的后果就是,大家都慢慢来,什么?都超时了??!!
  3. 内核/用户空间 内存拷贝问题,如何让内核把FD消息通知给用户空间呢?在这个问题上select采取了内存拷贝方法。

poll模型

基本上效率和select是相同的,select缺点的2和3它都没有改掉。

epoll的提升

把其他模型逐个批判了一下,再来看看Epoll的改进之处吧,其实把select的缺点反过来那就是Epoll的优点了。

  1. Epoll没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。
  2. 效率提升,Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
  3. 内存拷贝,Epoll在这点上使用了“共享内存”,这个内存拷贝也省略了。

总结

  1. select 是采用内核轮询方式,每次调用都需要轮询 FD_SET,默认最多可以接受 1024 个fd,可更改为更大,但是随着数量的增多,轮询周期的变长,性能会急剧下降;
  2. poll 是 select 的改进版,将 FD_SET 改造成由( fd,监听事件类型,实际事件类型 )为节点组成的链,解除了1024 的限制,其他并无大的区别,当 fd 多时,同样会造成效率下降;
  3. epoll 将 轮询机制 改造为 事件触发机制,给每一个 fd 附上一个 callback,当监听事件发生时,就将 fd 链接到 就绪链表,调用 epoll_wait 时,只用检查就绪链表就可以了,而不需要像 select 和 poll 一样进行轮询。
  4. 另外,select 和 poll 是将存有 fd 的结构或者数组再每次调用的时候都复制到内核态,然后调用完再复制回用户态,而无所谓是否有意义。epoll 使用内存映射,减去了这部分的data-copy操作。
  5. 再者,从触发方式上来看,select 和 poll 都只有 条件触发(也可以叫水平触发),epoll 则有条件触发 和 事件触发(也可以叫边缘触发)两种。
  6. 在选择使用哪种方式的时候,需要根据 fd 的多少和活跃程度来判断。当fd 数量较少,且都比较活跃的时候,使用 select 或者 poll 反而有可能效率更高,因为毕竟 epoll 要有多次的回调函数。

Reactor和Preactor

Socket与IO

文件IO

服务器并发策略总结

串行服务

多进程

多线程

IO复用

基于事件的并发