MySQL表设计的那些事

表设计


表结构

  • 引擎、字符集

  • 1NF:字段不可再分

  • 2NF:一个表必须有主键,即每行数据都能被唯一的区分

  • 3NF:表中的列都和主键直接相关,拆分间接关系

  • 反范式:通过增加冗余或重复的数据来提高数据库的读性能,减少关联查询时间,join表的次数

  • 需求>性能>表结构

表字段

  • 单表1G体积500W⾏

  • 单⾏不超过200Byte

  • 单表不超过50个INT字段

  • 单表不超过20个CHAR(10)字段

  • 建议单表字段数控制在20个以内

主/外键

  • 要有主键

  • 唯一、非空、有序

  • 主键不建议有业务含义(任何有业务含义的列都有改变的可能性,主键一旦带上了业务含义,那么主键就有可能发生变更;带有业务含义的主键,不一定是顺序自增的)

  • 不使用varchar类型作主键,如UUID,可读性差、无序、性能差。多主,ID生成中间件,主从,自增无符号Bigint(20)(innodb 中的主键是聚簇索引。如果主键是自增的,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。如果不是自增主键,那么可能会在中间插入,就会引发页的分裂,产生很多表碎片,插入时间更长,而且占用空间更大)

  • 主外键数据类型一致,防止隐式类型转换不走索引(需要多表 join的字段,数据类型保持绝对一致)

  • 不在数据表中追加外键约束,在应用层维持外键关系

选择合适的类型

  • 尽量选择小,简单的数据类型。 如boolean类型使用tinyint,邮政编码用char(6),用整型来存IP

  • 字段长度不要比实际业务过大,字段长度越大,索引在B-Tree树上遍历会越耗费时间,查询的时间会越久,VARCHAR的长度只分配真正需要的空间

  • 尽量避免Null而是not null default ‘’,索引性能不好,查询会出现一些不可预料的结果,如count(col)

  • 尽量使用timestamp(4字节带时区)而非datetime(8字节时间绝对值)

  • 尽量避免使用text或blob数据类型,非有不可,建议垂直拆分,查询效率太慢

  • 整型定义中不添加长度, 比如使用int, 而不不是int(4)。(在添加了zerofill描述符后的显示宽度,即预存储宽度,所以在不添加zerofill描述符的时候,int(1) 和int(10) int(100) 都没什么区别)

  • 存储精度数值使用decimal,float和double是以二进制存储的,有一定的误差,容易失精度

  • 不存储任何资源文件,比如照片/视频/网站等,可以用文件路径/外链来代替,这样可以通过路径,链接等来进行索引

  • 频繁修改的字段(一般是指状态类字段)最好用独立的数字或者单个字母去表示,不用使用汉字或者英文

优化设计

  • 做好静态表和动态表的分离

  • 2张表的多对多的表关系,最好设计成3张表,即增加一张中间表,之前的两张表和中间表的关系是一对多的关系

  • 计算列允许冗余,提高查询统计的速度

添加逻辑删除&创建时间&修改时间

  • 物理删除一旦删除,即不可恢复,逻辑删除数据可用数据分析

  • 为每个表中设计create_time和update_time,保存系统当前时间

添加注释

  • 表注释,字段注释

索引


不加索引

  • 慢查询

  • 服务超时

  • 数据库服务器CPU 100%

  • 一条SQL语句可以干爆一台DB

区分度

  • 字段去重后的总数与全表总记录数的商 select count(distinct col))/count(*) from table;

  • 区分度越大,索引效果越明显

  • 在区分度较小的字段上新建索引,基本无效,还会增加大量的索引文件

添加合适的索引

  • 在经常需要搜索的列上加索引,可以加快搜索的速度

  • 在创建时间上加索引,加快范围搜索的速度

  • 在外键字段上加索引,加快多表JOIN的关联查询的速度

  • 在SELECT、UPDATE、DELETE语句的WHERE条件中的列上加索引,加快条件的判断速度

  • 在包含ORDER BY、GROUP BY、DISTINCT中的字段上加索引,利用索引的排序,加快排序查询时间

  • 值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段

  • 对较长的字符数据类型的字段,可以考虑创建前缀索引,加快索引速度:ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);

对于联合索引,

  • 区分度较高的字段放联合索引的最左侧

  • 字段长度小的列放在联合索引的最左侧

  • 使用频繁的列放在联合索引的最左侧

  • 联合索引的顺序根据最左前缀原则区分

索引越多越好?

  • 要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描

  • 索引可以增加查询效率,同样会降低插入和更新的效率

  • 建议单表5个索引

  • 考虑联合索引,删除不必要的单列索引,避免冗余索引和重复索引

  • 扩展优先,不要新建索引,尽量在已有索引中修改

不走索引

  • 应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用该字段索引而进行全表扫描

  • 对于联合索引,不是使用的第一部分,则不会使用索引(即不符合最左前缀原则)

  • 最左前缀匹配,向右匹配直到遇到范围查询(>、>=、<=、<、between、like)就停止匹配

  • 隐式类型转换,不走索引(如列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引)

  • 在索引列上进行函数或表达式运算,该列索引会失效,在建有索引的字段上尽量不要使用函数进行操作

  • 查询条件中有or,即使其中有条件带索引也不会使用。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

  • like查询以%开头

SQL


EXPLAIN

  • 在写完SQL后,一定要查看执行计划,尽量避免全表扫描

  • 开启慢查询日志,设置阈值分析慢SQL并进行优化

隐式类型转换

  • 在相同类型的字段间进行比较的操作,检查参数类型与数据库字段类型一致,如果参数不一致,要使用CAST等函数显示转换成一致,否则可能会将索引字段类型转化成=号右边的类型,造成隐式类型转换,不走索引

如:explain select * from t_base_user where telephone=12345678901

图片

SQL规约

  • SQL语句尽量简单,不在数据库做复杂运算,尽量不用存储过程、函数(避免将业务逻辑和DB耦合在一起,数据库存储数据为主,业务逻辑尽量通过应用层实现)

  • 少用子查询和复杂查询

  • 少用复杂JOIN,并注意JOIN中的驱动表是否最优

  • 使用同类型进行比较,比如用"123"和"123"比,123和123比,防止隐式类型转换

  • 不要使用COUNT(列名)或COUNT(常量)来替代COUNT(*),COUNT(*)就是SQL92定义的标准统计行数的语法

  • 使用ISNULL()或IS NULL来判断是否为NULL值,不要用=、<>。NULL与任何值的直接比较都为NULL

  • 通常情况下,OR的查询效率要低于IN,所以对于影响性能的OR,考虑IN或UNION替代

  • IN操作能避免则避免,若实在避免不了,需要仔细评估IN后边的集合元素数量,控制在1000个之内

  • 对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5

  • LIKE只能使用前缀索引,因此避免%xxx式前缀模糊查询,例如LIKE"%abc%",允许"abc%"

  • 尽量避免在WHERE子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描

  • 不用SELECT *,只获取需要的字段 ,减少网络带宽消耗,还有可能利用到覆盖索引

  • 不使用INSERT INTO table_name VALUES(),要显式的指定插入列

  • 不做列运算,如SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,包括数据库函数、计算表达式等等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值