PostgreSQL 怎么解决 division by zero 问题

问题 最近在使用 sql 进行数值计算时,发现 sql 语句运行报错,报错信息如下:division by zero,综合分析 sql 语句得出是在进行除法运算时,除数为 0 导致的。 解决 接下来我来介绍几种解决这种问题的方法: NULLIF 和 COALESCE 函数 我们可以使用 NULLIF 函数检查变量是否是 0 值,如果是 0 则为 null。使用 COALESCE 函数检查分子和分母是否有 NULL 值,然后返回默认值。 例子: SELECT COALESCE(dividend / NULLIF(divisor, 0), default_value) FROM xx CASE 表达式 我们可以使用 case when 来检查除数是否为 0,如果是 0 则使用默认值。 例子: SELECT COALESCE(dividend / NULLIF(divisor, 0), default_value) FROM xx 最终我采用了第一种方法,顺利的解决了问题,还需要多说的是在 postgresql sql 语句使用过程中,要注意所使用函数需要的参数的类型,遇到需要转换参数类型的情况,可以使用 case 函数进行类型转换。 小结 本文讲述了我遇到的 PostgreSQL 进行数值计算时遇到的 division by zero 问题的情况及解决方法。 参考 https://www.postgresql.org/docs/current/functions-conditional.html#FUNCTIONS-NULLIF https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-EXPRESS-EVAL https://pganalyze.com/docs/log-insights/app-errors/U128 https://stackoverflow.com/questions/17681375/avoid-division-by-zero-in-postgresql

七月 1, 2023 · overstarry

Kubernetes Health check

本文我来讲解 Kubernetes 中的一个重要概念:容器的健康检查。 介绍 在 Kubernetes 中,你可以为 Pod 里的容器定义一个健康检查“探针”(Probe)。 这样,kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器镜像是否运行(来自 Docker 返回的信息)作为依据。 这种机制,是生产环境中保证应用健康存活的重要手段。 k8s 主要有三种健康检查的探针:1) LivenessProbe 存活探针 2) ReadinessProbe 就绪探针 3) StartupProbe 启动探针 kubelet 使用存活探针来确定什么时候要重启容器。例如,存活探针可以探测到应用死锁(应用程序在运行,但是无法继续执行后面的步骤)情况。重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。 存活探针的常见模式是为就绪探针使用相同的低成本 HTTP 端点,但具有更高的 failureThreshold。这样可以确保在硬性终止 Pod 之前,将观察到 Pod 在一段时间内处于非就绪状态。 kubelet 使用就绪探针可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。 kubelet 使用启动探针来了解应用容器何时启动。如果配置了这类探针,你就可以控制容器在启动成功后再进行存活性和就绪态检查,确保这些存活、就绪探针不会影响应用的启动。启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。 probe 介绍 接下来我来讲解用的较多的 2 个探针:1) LivenessProbe 存活探针 2) ReadinessProbe 就绪探针 LivenessProbe 许多应用由于长时间运行导致程序异常,需要重启服务才能继续正常使用,Kubernetes 提供了存活探针 (LivenessProbe) 来发现并处理这种情况。 我们先创建一个 pod, pod 的文件如下: ...

六月 23, 2023 · overstarry

Rancher Cattle Cluster Agent Could not Resolve Host

起因 最近在使用 rancher 导入外部 k8s 集群时,遇到了一个问题:在要导入的集群上执行命令后,创建的 pod 运行错误,错误日志大概如下: INFO: Environment: CATTLE_ADDRESS=100.66.209.198 CATTLE_CA_CHECKSUM= CATTLE_CLUSTER=true CATTLE_CLUSTER_AGENT_PORT=tcp://11.11.10.11:80 CATTLE_CLUSTER_AGENT_PORT_443_TCP=tcp://11.11.10.11:443 CATTLE_CLUSTER_AGENT_PORT_443_TCP_ADDR=10.96.0.125 CATTLE_CLUSTER_AGENT_PORT_443_TCP_PORT=443 CATTLE_CLUSTER_AGENT_PORT_443_TCP_PROTO=tcp CATTLE_CLUSTER_AGENT_PORT_80_TCP=tcp://10.96.0.125:80 CATTLE_CLUSTER_AGENT_PORT_80_TCP_ADDR=10.96.0.125 CATTLE_CLUSTER_AGENT_PORT_80_TCP_PORT=80 CATTLE_CLUSTER_AGENT_PORT_80_TCP_PROTO=tcp CATTLE_CLUSTER_AGENT_SERVICE_HOST=10.96.0.125 CATTLE_CLUSTER_AGENT_SERVICE_PORT=80 CATTLE_CLUSTER_AGENT_SERVICE_PORT_HTTP=80 CATTLE_CLUSTER_AGENT_SERVICE_PORT_HTTPS_INTERNAL=443 CATTLE_CLUSTER_REGISTRY= CATTLE_INGRESS_IP_DOMAIN=sslip.io CATTLE_INSTALL_UUID=333850e4-f500-43a2-a359-e1dfd94e4f35 CATTLE_INTERNAL_ADDRESS= CATTLE_IS_RKE=false CATTLE_K8S_MANAGED=true CATTLE_NODE_NAME=cattle-cluster-agent-55b9954958-5679q CATTLE_SERVER=https://xx.xx.vip CATTLE_SERVER_VERSION=v2.6.6 INFO: Using resolv.conf: search cattle-system.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 ERROR: https://xx.xx.vip/ping is not accessible (Could not resolve host: xx.xx.vip) 可以很好的看出错误的原因是域名 dns 解析错误。 解决 在发现问题后,可以根据错误日志进行相应的解决,我们先查看 coredns 的日志 kubectl logs deployment/coredns -n kube-system [ERROR] plugin/errors: 2 XX.XX.vip. A: read udp 100.108.11.198:32988->100.100.2.136:53: i/o timeout [ERROR] plugin/errors: 2 XX.XX.vip. A: read udp 100.108.11.198:53477->100.100.2.136:53: i/o timeout [ERROR] plugin/errors: 2 XX.XX.vip. AAAA: read udp 100.108.11.198:40436->100.100.2.136:53: i/o timeout 可以看到相应域名解析错误,根据以前的经验,先重启 coredns 容器尝试是否能解决问题,重启后发现问题未能解决 (可能需要重启相应节点的机器。 ...

六月 16, 2023 · overstarry

sealos version compare error

前言 最近在使用 sealos 安装更新 k8s 的过程中发现了一个问题:nfo new cluster version v1.25.10 behind the current version v1.25.8。 现在我来简单描述下过程:我想要将 k8s 版本从 v1.25.8 升级到 v1.25.10,运行更新命令:sealos run labring/kubernetes:v1.25.10-4.2.0 -f 后,出现了以下错误信息: 2023-06-09T11:30:06 info new cluster version v1.25.10 behind the current version v1.25.8 ,通过对错误信息的理解,发现提示 v1.25.10 版本比 v1.25.8 小,通过我对版本号的理解,这应该是个错误。 发现及解决问题 发现了问题,接下来要寻找出现问题的代码和解决问题。通过 Github 的代码搜索功能搜索关键字 behind the current version 顺利找到问题代码所在。 对找到的代码进行分析,发现是 versionutil.Compare(curversion, version) 方法出现了错误,我们来看看这个函数的代码: func Compare(v1, v2 string) bool { v1 = strings.Replace(v1, "v", "", -1) v2 = strings.Replace(v2, "v", "", -1) v1 = strings.Split(v1, "-")[0] v2 = strings.Split(v2, "-")[0] v1List := strings.Split(v1, ".") v2List := strings.Split(v2, ".") if len(v1List) != 3 || len(v2List) != 3 { logger.Error("error version format %s %s", v1, v2) return false } if v1List[0] > v2List[0] { return true } else if v1List[0] < v2List[0] { return false } if v1List[1] > v2List[1] { return true } else if v1List[1] < v2List[1] { return false } if v1List[2] >= v2List[2] { return true } return false } 通过对代码的分析,发现是对主次修订版本号对比时出现了问题,代码中是简单的使用了字符串比较,字符串数字比较会以字典序进行比较,所以会出现 “8” 大于 “10"的情况。正常的数字比较应该是将字符串转为数字进行相应比较。 ...

六月 10, 2023 · overstarry

Kubernetes pod 修改 hosts 文件

前言 最近看了 k8s 的书,学习了一些新的知识,将会分几篇来介绍学习到的知识,本文来先介绍 k8s 中如何修改 pod 的 hosts 文件。 我们知道当 DNS 出现问题时,可以向 Pod 的/etc/hosts 文件添加条目来提供主机名解析 Pod 级别覆盖。该如何向 hosts 文件中添加条目呢?可以使用 PodSpec 中的 HostAliases 字段添加自定义条目。 虽然我们也可以直接进入 pod 修改 host 文件来实现,但这样 pod 重建时会被覆盖,所以我们应该使用 HostAliases 来进行修改,因为该文件会由 Kubelet 管理,并且 可以在 Pod 创建/重启过程中被重写。 使用 我们该如何操作呢,接下来由我来介绍使用步骤: 1 先创建 Deployment YAML 文件来创建后台运行的 busybox pod apiVersion: apps/v1 kind: Deployment metadata: name: busybox-deployment spec: replicas: 1 selector: matchLabels: app: busybox template: metadata: labels: app: busybox spec: containers: - name: busybox image: busybox args: [ "sleep", "3600" ] resources: limits: memory: "128Mi" cpu: "500m" requests: memory: "64Mi" cpu: "250m" volumeMounts: - name: busybox-volume mountPath: /data volumes: - name: busybox-volume emptyDir: {} 查看 pod ip ...

六月 3, 2023 · overstarry

Apisix 忽略 uri 大小写

前言 最近需要忽略请求 uri 大小写,即不管 uri 的大小写都返回小写 uri 所请求的响应内容,例如请求 xx.vip/A 实际请求 xx.vip/a, 本文主要以 apisix 来讲解如何实现这个功能。 解决 遇到这个需求,首先肯定采用 apisix 的插件来实现,根据前面多次的经验,这个功能应该可以使用 serverless 插件中的 serverless-pre-function 插件来实现,具体的流程如下: 1 获取请求的 uri 2 将 uri 转为小写 3 修改请求的 uri 具体的插件内容如下: "serverless-pre-function": { "_meta": { "disable": false }, "functions": [ "return function(conf, ctx) local uri = ctx.var.uri;ctx.var.uri= string.lower(uri);ngx.log(ngx.ERR, \"match uri \", ctx.var.uri ); end" ], "phase": "rewrite" } 添加插件后,再次请求路径,发现请求结果还是没有变,查看日志发现 uri 修改成功了,不知为何还是没有修改成功。 后面又仔细研究了 proxy-rewrite 插件中修改 uri 的代码,发现它是使用了 nginx 标准的函数 ngx.req.set_uri 来赋值新的 uri。 ...

五月 26, 2023 · overstarry

sqlc 初体验

简介 sqlc 可以生成从 SQL 生成类型安全代码。它的工作原理是: 使用 SQL 书写查询语句。 运行 sqlc 生成 Go 代码,该代码为这些查询提供类型安全的接口。 在应用程序代码中调用生成的代码与数据库进行交互。 安装 sqlc 有多种安装方式: macOS: brew install sqlc Ubuntu: sudo snap install sqlc go 安装: # Go >= 1.17: go install github.com/kyleconroy/sqlc/cmd/sqlc@latest # Go < 1.17: go get github.com/kyleconroy/sqlc/cmd/sqlc docker 安装: docker pull kjconroy/sqlc 其它: https://github.com/kyleconroy/sqlc/releases 入门使用 插件 sqlc.yaml 首先创建 sqlc.yaml 文件,sqlc 会在当前目录下查找 sqlc.yaml 或 sqlc.json 文件。 # sqlc.yaml version: 1 packages: - path: "tutorial" name: "tutorial" engine: "mysql" schema: "schema.sql" queries: "query.sql" 创建 sql 文件 sqlc 需要得知数据库表结构和相应的 sql 语句,我们创建 schema.sql 定义表结构: ...

五月 20, 2023 · overstarry

Apisix Docker 部署网站重定向端口错误问题

前言 前段时间在使用 apisix 添加路由时,需要将 http 转为 https, 在配置 http_to_https 后,访问相应网址时发现 https 的端口不是我们所配置的默认端口 443,而是 9443 端口。 可以看到访问 http://localhost 会跳转至错误的地址:https://localhost:9443/ ,正确的地址应该是 https://localhost,这是怎么回事呢? 分析 接下来我来简单对问题进行简单分析。 我是采用 docker 的方式部署的 apisix,这是我们的配置文件: version: "3" services: apisix-dashboard: image: apache/apisix-dashboard:3.0.0-alpine restart: always volumes: - ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml ports: - "9000:9000" networks: apisix: apisix: image: apache/apisix:${APISIX_IMAGE_TAG:-3.2.0-debian} restart: always volumes: - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro depends_on: - etcd ##network_mode: host ports: - "9180:9180/tcp" - "80:9080/tcp" - "9091:9091/tcp" - "443:9443/tcp" - "9092:9092/tcp" networks: apisix: etcd: image: bitnami/etcd:3.4.15 restart: always volumes: - etcd_data:/bitnami/etcd environment: ETCD_ENABLE_V2: "true" ALLOW_NONE_AUTHENTICATION: "yes" ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379" ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" ports: - "2379:2379/tcp" networks: apisix: web1: image: nginx:1.19.0-alpine restart: always volumes: - ./upstream/web1.conf:/etc/nginx/nginx.conf ports: - "9081:80/tcp" environment: - NGINX_PORT=80 networks: apisix: networks: apisix: driver: bridge volumes: etcd_data: driver: local 可以看到我们将 apisix 容器的 http 端口和 https 端口映射为 80 和 443,按照常理来说,http_to_https 后的端口应该也是 443 端口才对。我猜测重定向时,apisix 还是采用配置文件中设定的 https 端口才导致跳转的 url 端口错误。 ...

五月 13, 2023 · overstarry

Go wasi

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C / C ++等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。 Go 从 1.11 版本就开始支持将 Go 源码编译为 wasm 二进制文件,并在支持 wasm 的浏览器环境中运行。 不过 WebAssembly 绝不仅仅被设计为仅限于在 Web 浏览器中运行,核心的 WebAssembly 语言是独立于其周围环境的,WebAssembly 完全可以通过 API 与外部世界互动。在 Web 上,它自然使用浏览器提供的现有 Web API。然而,在浏览器之外,之前还没有一套标准的 API 可以让 WebAssembly 程序使用。这使得创建真正可移植的非 Web WebAssembly 程序变得困难。WebAssembly System Interface(WASI) 是一个填补这一空白的倡议,它有一套干净的 API,可以由多个引擎在多个平台上实现,并且不依赖于浏览器的功能(尽管它们仍然可以在浏览器中运行)。 Go 1.21 将增加对 WASI 的支持,初期先支持 WASI Preview1 版本,之后会支持 WASI Preview2 版本,直至最终 WASI API 版本发布! go 编译支持 wasi 的程序 怎么样才能编译支持 wasi 的程序呢?我们可以使用 GOOS=wasip1 GOARCH=wasm 将 Go 源码编译为支持 WASI 的 wasm 程序。下面是一个例子: ...

五月 2, 2023 · overstarry

Go 实现简单反向代理

前言 说起反向代理,大家应该都不陌生,是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。常见的反向代理有 Nginx,HAProxy,Apisix 等。 接下来介绍如何使用 go 实现一个反向代理服务器。 golang 实现 使用 golang 实现反向代理非常简单,标准库 net/http/httputil 提供了反向代理的方法可以让我们方便的实现反向代理,使我们可以很快的实现一个简单的反向代理服务器。 package main import ( "log" "net/http" "net/http/httputil" "net/url" ) func NewProxy(targetHost string) (*httputil.ReverseProxy, error) { url, err := url.Parse(targetHost) if err != nil { return nil, err } return httputil.NewSingleHostReverseProxy(url), nil } func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) } } func main() { proxy, err := NewProxy("https://overstarry.vip") if err != nil { panic(err) } http.HandleFunc("/", ProxyRequestHandler(proxy)) log.Fatal(http.ListenAndServe(":8080", nil)) } 这段代码将到达我们代理服务器的任何请求都会被代理到 https://overstarry.vip。我们运行代码,访问网站,发现 403 Forbidden 好像请求被拦截了,应该是源网站进行了请求校验,这该怎么处理呢?通过查阅资料得知,我们需要将 host 传递过去,修改后的代码如下: package main import ( "log" "net/http" "net/http/httputil" "net/url" ) func NewProxy(targetHost string) (*httputil.ReverseProxy, error) { url, err := url.Parse(targetHost) if err != nil { return nil, err } return httputil.NewSingleHostReverseProxy(url), nil } func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { r.Host = "overstarry.vip" proxy.ServeHTTP(w, r) } } func main() { proxy, err := NewProxy("https://overstarry.vip") if err != nil { panic(err) } http.HandleFunc("/", ProxyRequestHandler(proxy)) log.Fatal(http.ListenAndServe(":8080", nil)) } 现在运行代码即可正常访问了。 ...

四月 8, 2023 · overstarry