命名规范
数据库对象(database、schema、table、column、view、index、sequence、function、trigger 等名称)
- 建议使用小写字母、数字、下划线的组合
- 建议不使用双引号"包围,除非必须包含大写字母或空格等特殊字符
- 长度不能超过63个字符
- 不建议以 pg_ 开头或者 pgxc_(避免与系统 DB object 混淆),不建议以数字开头
- 禁止使用 SQL 关键字,例如 type、order 等
- 临时或备份的 DB object:table、view 等,建议加上日期,如 dba_ops.b2c_product_summay_2014_07_12 (其中 dba_ops 为 DBA 专用 schema)
- index 命名规则:普通索引为表名_列名_idx,唯一索引为表名_列名_uidx,如 student_name_idx,student_id_uidx
总结如下:使用小写字母、数字及下划线命名、不使用pg_或者pgxc开头、不使用数字开头、不使用SQL关键字、index命名规则
Column设计
- 建议使用数值类型,不用字符类型
- 建议使用 varchar(N) ,不用 char(N),以节省存储空间
- 建议使用 varchar(N) ,不用 text、varchar
- 建议使用 default NULL,不用default '',以节省存储空间
- 建议国际业务时,使用 timestamp with time zone(timestamptz),不用 timestamp without time zone,避免时间函数在不同时区的时间点返回值不同,也为业务国际化扫清障碍
- 建议使用 NUMERIC(precision, scale) 来存储货币金额和其它要求精确计算的数值,不建议使用 real、double precision
总结如下:使用数值类型、varchar(N)、numeric类型及default NULL
Constriants设计
- 建议每个 table 都使用 shard key 作为主键或者唯一索引
- 建议建表时一步到位,一起建立主键或者唯一索引
- 非 shard key 不可以建立 primary key 或者 unique index
总结如下:必须建shard key、使用shard key作为主键或者唯一索引、建表一步到位
Index设计
- TDSQL PostgreSQL版 提供的 index 类型:B-tree、Hash、GiST (Generalized Search Tree)、SP-GiST (space-partitioned GiST)、GIN (Generalized Inverted Index)、BRIN (Block Range Index),目前不建议使用 Hash,通常情况下使用 B-tree
- 建议 create 或 drop index 时,加 CONCURRENTLY 参数,可以达到与写入数据并发的效果(CONCURRENTLY参数有什么作用呢?)
- 建议对频繁 update、delete 的包含于 index 定义中的 column 的 table,使用 create index CONCURRENTLY、drop index CONCURRENTLY 的方式进行维护其对应 index
- 建议用 unique index 代替 unique constraints,便于后续维护(unique constraints指的是哪部分?为什么这样就便于维护呢)
- 建议对 where 中带多个字段 and 条件的高频 query,参考数据分布情况,建多个字段的联合 index
- 建议对固定条件的(一般有特定业务含义)且数据占比低的 query,建议带 where 的 Partial Indexes
- 建议对经常使用表达式作为查询条件的 query,可以使用表达式或函数索引加速 query
总结如下:index使用B-tree类型、联合index、Partial Indexes、表达式或函数索引、index个数一般不超过6个(核心表可以适当增加)
关于NULL
- NULL 的判断:IS NULL、IS NOT NULL
- 注意 boolean 类型取值 true、false、NULL
- 注意 NOT IN 集合中带有 NULL 元素
- 建议对字符串型 NULL 值处理后,进行 || 操作
- 建议使用 count(1) 或 count(*) 来统计行数,不建议使用 count(col) 来统计行数,因为 NULL 值不会计入
count(多列列名)时,多列列名必须使用括号,例如 count( (col1,col2,col3) ),注意多列的 count,即使所有列都为 NULL,该行也被计数,所以效果与 count(*) 一致。
- count(distinct col) 计算某列的非 NULL 不重复数量,NULL 不被计数。
count(distinct (col1,col2,...) ) 计算多列的唯一值时,NULL 会被计数,同时 NULL 与 NULL会被认为是相同的。
- 两个 NULL 的对比方法
总结如下:null判断、null处理、count(1)、count(*)、count(col)、count(多列列名)、count(distinct col)、count(distinct(col1,col2,...))
开发规范
- 建议对 DB object 尤其是 COLUMN 加 COMMENT,便于后续新人了解业务及维护
- 建议非必须时避免 select *,只取所需字段,以减少包括不限于网络带宽消耗
- 建议 update 时,尽量做 <> 判断,如 update table_a set column_b = c where column_b <> c
- 建议将单个事务的多条 SQL 操作,分解、拆分,或者不放在一个事务里,让每个事务的粒度尽可能小,尽量 lock 少的资源,避免 lock 、dead lock 的产生
- 建议大批量的数据入库时,使用 copy ,不建议使用 insert,以提高写入速度。如下,性能相差5倍
- 建议对报表类的或生成基础数据的查询,使用物化视图 (MATERIALIZED VIEW) 定期固化数据快照,避免对多表(尤其多写频繁的表)重复跑相同的查询,且物化视图支持 REFRESH MATERIALIZED VIEW CONCURRENTLY,支持并发更新(使用物化视图会有什么风险麻?比如没把刚插入的数据统计进去)
- 建议复杂的统计查询可以尝试窗口函数
- 两表 join 时,尽量的使用分布 key 进行 join
- 分布键用唯一索引代替主键。因为唯一索引后期的维护成本比主键要低很多。
- 分布键无法建立唯一索引,则要建立普通索引,提高查询的效率
- 不对字段建立外键
总结如下:comment、禁止使用select *、事务粒度尽可能小、大批量数据导入使用copy、使用物化视图、使用窗口函数、分布KeyJoin、分布键使用唯一索引代替主键、不建立外键