mysql数据库

一、数据库基本设计规范

  1. 数据库设计时,应该要对以后扩展进行考虑
  2. 所有表必须使用 InnoDB 存储引擎
    没有特殊要求(即 InnoDB 无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用 InnoDB 存储引擎(MySQL 5.5 之前默认使用 Myisam,5.6 以后默认的为 InnoDB)InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。(==我们程序中禁止使用事务,因为事务有锁表风险==)
  3. 数据库和表的字符集统一使用 UTF8
    兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效。
  4. 所有表和字段都需要添加注释
    使用 comment 从句添加表和列的备注 从一开始就进行数据字典的维护。
  5. 尽量控制单表数据量的大小,建议控制在 500 万以内
    500 万并不是 MySQL 数据库的限制,过大会造成修改表结构、备份、恢复都会有很大的问题,可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小。
  6. 谨慎使用 MySQL 分区表
    分区表在物理上表现为多个文件,在逻辑上表现为一个表 谨慎选择分区键,跨分区查询效率可能更低 建议采用物理分表的方式管理大数据。
  7. 禁止在表中建立预留字段
    预留字段的命名很难做到见名识义 预留字段无法确认存储的数据类型,所以无法选择合适的类型 对预留字段类型的修改,会对表进行锁定
  8. 禁止在数据库中存储图片,文件等大的二进制数据
    通常文件很大,会短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机 IO 操作,文件很大时,IO 操作很耗时 通常存储于文件服务器,数据库只存储文件地址信息。
  9. 禁止在线上做数据库压力测试
  10. 禁止从开发环境,测试环境直接连接生成环境数据库

二、数据库表设计规范

  1. 设计表时,应该要对以后扩展进行考虑
  2. 所有表必须使用 InnoDB 存储引擎
  3. 每张表必须有一个主键
  4. 禁止使用外键约束
  5. 表的字符集统一使用 utf8mb4;排序使用utf8mb4_general_ci
  6. 设计表时要加注释
  7. 表前缀用项目名称字母缩写;所有表名都小写,单词之间用下划线分开,单词都用单数形式,命名要能做到见名识意,并且最好不要超过3 2 个字符
  1. 关联表以_index 结尾
  1. 临时库表必须以 tmp_ 为前缀并以日期为后缀
  1. 备份表必须以 bak_ 为前缀并以日期 为后缀
[^备份表]: bak_kyy_user_2021-11-01
  1. 分表以_00~99结尾
[^分表]: kyy_user_00、kyy_user_01


三、数据库字段设计规范

  1. 命名要能做到见名识意,单词之间用下划线分开
  2. 字段要有明确的注释,描述该字段的用途及可能存储的内容
  3. 禁止在表中建立预留字段。预留字段的命名很难做到见名识义 预留字段无法确认存储的数据类型,所以无法选择合适的类型 对预留字段类型的修改,会对表进行锁定
  4. 所有字段,均为非空(NOT NULL),最好显示指定默认值
  5. 数值类型的字段请使用UNSIGNED属性
  6. 是别的表的外键均使用xxx_id的方式来表明;
  7. 所有的布尔值字段,以is_ 前缀,如is_hot、is_deleted,都必须设置一个默认值,并设为0;使用tinyint 类型
  8. varchar类型字段的程序处理,要验证用户输入,不要超出其预设的长度;
  9. 不同表中表达同一意思字段要统一,例如创建时间统一 用created_at;更新时间用 updated_at;备注统一用 remark 排序统一用order_id
  10. 避免使用ENUM 类型的 可以用tinyint 类型代替
  11. 同财务相关的金额类型使用 decimal 类型
  12. text字段尽量少用,或是拆到冗余表中
  13. 禁止给表中的每一列都建立单独的索引,合理使用联合索引,建议索引数量不超过 5 个
  14. 避免建立冗余索引和重复索引
[^重复索引示例:]: primary key(id)、index(id)、unique index(id)   
[^冗余索引示例]: index(a,b,c)、index(a,b)、index(a)


四、数据库操作规范

  1. 禁止使用事务,有锁表风险
  2. 不做特殊说明,不做物理删除,统一做软删除
  3. 避免数据类型的隐式转换(隐式转换会导致索引失效)
  1. 尽量不使用 select * 建议使用 select <字段列表> 查询
  2. 禁止使用不包含字段列表的insert 语句
  1. 禁止使用联表查询
  2. 建议使用 in 代替 or,in的值不要超过500个
  3. 禁止使用 order by rand() 进行随机排序
  4. WHERE从句中禁止对列进行函数转换和计算
  1. 什么时候使用组合索引,什么时候使用单独索引 以下是索引使用示例:

    一.前期数据准备
    1.建表
    CREATE TABLE `user` (
      `uid` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(50) DEFAULT NULL,
      `pwd` varchar(50) DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      `modify_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      `rids` varchar(15) DEFAULT NULL,
      `nickname` varchar(45) DEFAULT NULL,
      `company` varchar(15) DEFAULT NULL,
      PRIMARY KEY (`uid`),
      UNIQUE KEY `name_UNIQUE` (`name`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
    2.插入数据
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (1, 'rocker', 'rocker', NULL, '2019-10-08 11:05:02', '1', 'rocker', 'rocker');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (2, 'danny', 'danny', NULL, '2019-10-08 11:31:36', '2', 'rocker', 'danny');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (3, 'tom', 'tom', NULL, '2019-10-08 11:31:39', '1', 'tom', 'rocker');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (4, 'messi', 'messi', NULL, '2019-10-08 11:31:21', '2', 'messi', 'messi');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (5, 'wenger', 'wenger', NULL, '2019-10-08 11:29:38', '1', 'wenger', 'rocker');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (6, 'henry', 'henry', NULL, '2019-10-08 11:30:46', '2', 'henry', 'henry');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (7, 'ronaldo', 'ronaldo', NULL, '2019-10-08 11:30:49', '1', 'ronaldo', 'ronaldo');
    INSERT INTO `monitor`.`user`(`uid`, `name`, `pwd`, `create_time`, `modify_time`, `rids`, `nickname`, `company`) VALUES (8, 'kaka', 'kaka', NULL, '2019-10-08 11:29:45', '2', 'kaka', 'rocker');
    二.分析
    1.不加索引

    首先在'nickname'和‘company’这俩字段不加索引的情况下执行一个查询语句,并分析

    mysql> explain select * from user where nickname = 'rocker' and company = 'rocker';
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    8 |    12.50 | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)

    可以看到,没有走索引,总共查询了8条数据,而表中总共也是8条数据,相当于全表扫描了。

    mysql> explain select * from user where company = 'rocker' or nickname = 'rocker';
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    8 |    23.44 | Using where |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)

    可以看到:不加任何索引的情况下,不管是and还是or,都是全表扫描,没有索引。

    2.单独索引

    给nickname和company分别加上索引,再执行and和or的sql查询

    alter table user add index `idx_nickname` (`nickname`);
    alter table user add index `idx_company` (`company`);

    执行查询语句and

    mysql> explain select * from user where nickname = 'rocker' and company = 'rocker';
    +----+-------------+-------+------+--------------------------+--------------+---------+-------+------+-------------+
    | id | select_type | table | type | possible_keys            | key          | key_len | ref   | rows | Extra       |
    +----+-------------+-------+------+--------------------------+--------------+---------+-------+------+-------------+
    |  1 | SIMPLE      | user  | ref  | idx_nickname,idx_company | idx_nickname | 138     | const |    2 | Using where |
    +----+-------------+-------+------+--------------------------+--------------+---------+-------+------+-------------+
    1 row in set (0.05 sec)

    执行查询语句or

    mysql> explain select * from user where company = 'rocker' or nickname = 'rocker';
    +----+-------------+-------+------+--------------------------+------+---------+------+------+-------------+
    | id | select_type | table | type | possible_keys            | key  | key_len | ref  | rows | Extra       |
    +----+-------------+-------+------+--------------------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | user  | ALL  | idx_nickname,idx_company | NULL | NULL    | NULL |    8 | Using where |
    +----+-------------+-------+------+--------------------------+------+---------+------+------+-------------+
    1 row in set (0.00 sec)

    可以看到:加上索引后and查询是可以走索引的,但是只有一个索引起作用,对于另一个索引字段还是要进行遍历,而且and查询会根据关联性高(符合该条件的行数少)选择具体走哪个索引

    or查询不走索引

    3.组合索引

    删除原先的单独索引,新增组合索引

    alter table user drop index `idx_nickname`
    alter table user drop index `idx_company`
    
    alter table user add index `idx_composition` (`nickname`,`company`);

    执行查询语句and

    mysql> explain select * from user where nickname = 'rocker' and company = 'rocker';+----+-------------+-------+------+-----------------+-----------------+---------+-------------+------+-------------+| id | select_type | table | type | possible_keys   | key             | key_len | ref         | rows | Extra       |+----+-------------+-------+------+-----------------+-----------------+---------+-------------+------+-------------+|  1 | SIMPLE      | user  | ref  | idx_composition | idx_composition | 186     | const,const |    1 | Using where |+----+-------------+-------+------+-----------------+-----------------+---------+-------------+------+-------------+1 row in set (0.00 sec)

    执行查询语句or

    mysql> explain select * from user where company = 'rocker' or nickname = 'rocker';+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys   | key  | key_len | ref  | rows | Extra       |+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+|  1 | SIMPLE      | user  | ALL  | idx_composition | NULL | NULL    | NULL |    8 | Using where |+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+1 row in set (0.00 sec)

    可以看到:加上组合索引后,组合索引起作用,只需查询一条符合结果的数据,效率要比单独索引高,

    但是复合索引对于or查询不起作用

    4.组合索引查询单个索引列

    对于组合索引为(nickname,company)这个顺序的情况

    alter table user drop index `idx_composition`;alter table user add index `idx_composition` (`nickname`,`company`);mysql> explain select * from user where nickname = 'rocker';+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys   | key             | key_len | ref   | rows | Extra       |+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+|  1 | SIMPLE      | user  | ref  | idx_composition | idx_composition | 138     | const |    2 | Using where |+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+1 row in set (0.00 sec)mysql> explain select * from user where company = 'rocker';+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL |    8 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)

    可以看到:组合索引中nickname在前时,单独查询nickname会走索引,单独查询compamy不会走索引

    对于组合索引为(company,nickname)这个顺序的情况

    alter table user drop index `idx_composition`;alter table user add index `idx_composition` (`company`,`nickname`);mysql> explain select * from user where nickname = 'rocker';+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL |    8 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)mysql> explain select * from user where company = 'rocker';+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+| id | select_type | table | type | possible_keys   | key             | key_len | ref   | rows | Extra       |+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+|  1 | SIMPLE      | user  | ref  | idx_composition | idx_composition | 48      | const |    2 | Using where |+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+1 row in set (0.00 sec)

    可以看到:组合索引中compamy在前时,单独查询compamy会走索引,单独查询nickname不会走索引

    如果组合索引是(A,B),则对于条件A=a,是可以用上这个组合索引的,因为组合索引是先按照第一列进行排序的,所以没必要对A单独建立一个索引,但是对于B=b就用不上了,因为只有在第一列相同的情况下,才比较第二列,因而第二列相同的,可以分布在不同的节点上,没办法快速定位

你可能感兴趣的:(mysql)