PostgreSQL 5.7. Schemas 笔记

https://www.postgresql.org/docs/9.4/ddl-schemas.html

A database contains one or more named schemas, which in turn contain tables. Schemas also contain other kinds of named objects, including data types, functions, and operators. The same object name can be used in different schemas without conflict; for example, both schema1 and myschema can contain tables named mytable. Unlike databases, schemas are not rigidly separated: a user can access objects in any of the schemas in the database he is connected to, if he has privileges to do so.

  • 一个数据库包含多个schema, schema里包含tables, database 的下一层逻辑结构就是 schema
  • schema 也包括各种 objects, data types, functions, operators
  • 不同schem里的table名可以相同

在创建一个新的 database 时, PostgreSQL 会自动为其创建一个 名为 public 的 schema。 如果未设置 search_path 变量,那 么 PostgreSQL 会将你创建的所有对象默认放入 public schema 中。_

使用schema带来的好处

  • 允许多用户使用一个数据库而不会相互干扰, 数据隔离
  • 将数据库对象组织到逻辑组中以使其更易于管理
  • 第三方应用程序可以放在单独的模式中, 这样它们就不会与其他对象的名称冲突

5.7.1. Creating a Schema

code example:

CREATE SCHEMA myschema;

访问schema的表

schema.table

实际上,更通用的语法

database.schema.table

在schema里创建表

CREATE TABLE myschema.mytable (
 ...
);

删除空schema

DROP SCHEMA myschema;

删除schema 并且也删除其中的对象

DROP SCHEMA myschema CASCADE;

为某个用户创建schema

CREATE SCHEMA schemaname AUTHORIZATION username;

5.7.2. The Public Schema

默认创建的表都在public schema

下面两条语句是等价的

CREATE TABLE products ( ... );
CREATE TABLE public.products ( ... );

5.7.3. The Schema Search Path

当执行类 似 SELECT * FROM dogs 这种语句时, PostgreSQL 是怎么知道要查的是哪个 schema 中的表 呢?

可以加schema前缀解决, 也可以设置 search_path 变量解决

查看

SHOW search_path;
search_path
--------------
 "$user",public

PostgreSQL 有一个少为人知的系统变量叫作 user , 它代表了当前登录用户的名称。 执行 SELECT user 就能看到其名称。
对于search_path 里的$user, 如果当前登录的角色是 doggy, 那么所有的查询都会优先去 doggy schema 中寻找目标表, 如果找不到才会去 public schema 下找

设置新的schema倒search path里

SET search_path TO myschema,public;

这样 默认 创建访问 table 都在 myschema schema里

2019/3/4 posted in  阅读笔记

Create a High-Availability Kubernetes Cluster on AWS with Kops

https://www.poeticoding.com/create-a-high-availability-kubernetes-cluster-on-aws-with-kops/

AWS上轻松运行 Kubernetes 集群 部署 elixir 项目

使用 kops 部署 Kubernetes Operations (kops) - Production Grade K8s Installation, Upgrades, and Management

AWS 对kops有良好支持

部署此项目 https://github.com/poeticoding/phoenix_chat_example

High-Availability Cluster 高可用集群

结构图

要拥有HA集群,我们至少需要三个master服务器(管理整个Kubernetes集群的服务器)和两个worker服务器

AWS account and IAM role

配置账号

需要用到 aws-cli

$ aws configure

AWS Access Key ID: YOUR_ACCESS_KEY
AWS Secret Access Key: YOUR_SECRET_ACCESS_KEY
Default region name [None]:
Default output format [None]:

iam 用户需要 AdministratorAccess 权限

查看用户

$ aws iam list-users
{
    "Users": [
        {
            "Path": "/",
            "UserName": "alvise",
            "UserId": ...,
            "Arn": ...
        }
    ]
}

kops是在AWS上创建Kubernetes集群的工具 kubectl是我们用来管理集群启动运行

 Real domain in Route53

使用kops 需要配置域名

命令的使用等等

Kubernetes API and Security Group

k8s的api默认是公开的, 需要配置访问权限

Deploy an Nginx server

使用 kubectl get nodes 查看node列表

部署nginx需要 创建 kubernetes deployment

配置

# nginx_deploy.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx

  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.15
        ports:
        - containerPort: 80

$ kubectl create -f nginx_deploy.yaml
deployment.apps "nginx" created

$ kubectl get pod
NAME                   READY     STATUS    RESTARTS   AGE
nginx-c9bd9bc4-jqvb5   1/1       Running   0          1m

pod 运行起来了
使用 load balancer 访问

# nginx_svc.yaml
kind: Service
apiVersion: v1

metadata:
  name: nginx-elb
  namespace: default
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: 80

LoadBalancer 服务 转发流量到nginx

Deploy the Phoenix Chat

配置

kind: Deployment
apiVersion: apps/v1
metadata:
  name: chat
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: chat

  template:
    metadata:
      labels:
        app: chat
    spec:
      containers:
      - name: phoenix-chat
        image: alvises/phoenix-chat-example:1_kops_chat
        ports:
        - containerPort: 4000
        env:
        - name: PORT
          value: "4000"
        - name: PHOENIX_CHAT_HOST
          value: "chat.poeticoding.com"

docker 镜像 https://cloud.docker.com/repository/docker/alvises/phoenix-chat-example

LoadBalancer类似

kind: Service
apiVersion: v1

metadata:
  name: chat-elb
  namespace: default
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

spec:
  type: LoadBalancer
  selector:
    app: chat
  ports:
    - name: http
      port: 80
      targetPort: 4000

Multiple Chat replicas

只运行在一个节点 replicas 为 1

多节点需要分布式消息处理

2019/2/4 posted in  阅读笔记

ActiveRecord’s Queries tricks 小记

原文 https://medium.com/rubyinside/active-records-queries-tricks-2546181a98dd

关联表join时使用条件

# User model
scope :activated, ->{
  joins(:profile).where(profiles: { activated: true })
}

更好的做法

# Profile model
scope :activated, ->{ where(activated: true) }
# User model
scope :activated, ->{ joins(:profile).merge(Profile.activated) }

关于 merge
https://apidock.com/rails/ActiveRecord/SpawnMethods/merge
https://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html

嵌套join的差异

  • User has_one Profile
  • Profile has_many Skills
User.joins(:profiles).merge(Profile.joins(:skills))
=> SELECT users.* FROM users 
   INNER JOIN profiles    ON profiles.user_id  = users.id
   LEFT OUTER JOIN skills ON skills.profile_id = profiles.id
# So you'd rather use:
User.joins(profiles: :skills)
=> SELECT users.* FROM users 
   INNER JOIN profiles ON profiles.user_id  = users.id
   INNER JOIN skills   ON skills.profile_id = profiles.id
   

内链接和外连接

Exist query

存在和不存在

# Post
scope :famous, ->{ where("view_count > ?", 1_000) }
# User
scope :without_famous_post, ->{
  where(_not_exists(Post.where("posts.user_id = users.id").famous))
}
def self._not_exists(scope)
  "NOT #{_exists(scope)}"
end
def self._exists(scope)
  "EXISTS(#{scope.to_sql})"
end

Subqueries 子查询

比如查询部分用户(user)的帖子(post)

不好的做法

Post.where(user_id: User.created_last_month.pluck(:id))

这里的缺陷是将运行两个SQL查询:一个用于获取用户的ID,另一个用于从这些user_id获取帖子

这样写一个查询就可以了

Post.where(user_id: User.created_last_month)

基础

.to_sql 生成 SQL 语句字符串
.explain 获取查询分析

Booleans

对于User.where.not(tall: true)在pg下会生成
SELECT users.* FROM users WHERE users.tall <> 't'
这返回 tall 是 false 的 记录,不包括是null 的

包括null应该这么写

User.where("users.tall IS NOT TRUE")

or

User.where(tall: [false, nil])
2018/12/22 posted in  阅读笔记

Distributed Phoenix Chat using Redis PubSub

原文 https://www.poeticoding.com/distributed-phoenix-chat-using-redis-pubsub/

这篇文章里 Create a High-Availability Kubernetes Cluster on AWS with Kops, 我们学到怎么创建Kubernetes集群来部署Phoenix Chat应用, 这在单节点上跑的很好, 当我们试图扩展时,我们看到消息没有发送到所有浏览器。

在下图中,我们看到一个配置,其中两个聊天服务器是隔离的。当负载均衡器将两个WebSocket连接路由到两个不同的服务器时,两个浏览器无法相互发送消息。

此问题通常使用外部组件(如Redis)来解决, 使用redis将消息广播到群集中的所有节点。

使用Elixir可以不依赖redis, 纯粹依赖Elixir不用节点见的通讯

在本文中,我们将看到如何横向扩展Phoenix Chat,利用Redis PubSub运行在多个服务器。

Redis PubSub

Github repo https://github.com/poeticoding/phoenix_chat_example/tree/pubsub_redis

什么是 PubSub messaging (发布/订阅消息收发)?

Publish/subscribe messaging, or pub/sub messaging, is a form of asynchronous service-to-service communication used in serverless and microservices architectures. In a pub/sub model, any message published to a topic is immediately received by all of the subscribers to the topic.

AWS – Pub/Sub Messaging

发布/订阅消息收发是一种异步的服务间通信方式,适用于无服务器和微服务架构。在发布/订阅模式下,发布到主题的任何消息都会立即被主题的所有订阅者接收。发布/订阅消息收发可用于启用事件驱动架构,或分离应用程序,以提高性能、可靠性和可扩展性。

为了更好地理解PubSub的工作原理,让我们在Redis实验下。运行一个redis服务器

$ docker run --name redis -d redis

执行redis-cli

$ docker exec -it redis redis-cli

在图中,运行了4个客户端, 3个客户端订阅了 my_channel

 Phoenix PubSub Redis adapter

在phoenix使用 redis pubsub功能,要用到 The Redis PubSub adapter for the Phoenix framework
这个包 https://github.com/phoenixframework/phoenix_pubsub_redis

集成很容易

#mix.exs
defp deps do
[
  ...
  {:phoenix_pubsub_redis, "~> 2.1"}
]
end
#config/config.exs
config :chat, Chat.Endpoint,
  url: [host: "localhost"],
  root: Path.expand("..", __DIR__),
  secret_key_base: ".......",
  debug_errors: false,
  pubsub: [
    name: Chat.PubSub,
    adapter: Phoenix.PubSub.Redis,
    host: "localhost", port: 6379,
    node_name: System.get_env("NODE")
 ]
 ```
 
 配置 redis host, 每个server要配置不贩node_name

 
启动

Terminal 1

$ NODE=n1 PORT=4000 mix phx.server

# Terminal 2
$ NODE=n2 PORT=4001 mix phx.server


2019/1/31 posted in  阅读笔记 Elixir