MySQL模式开发设计规范问题与建议

1.时间类型的数据默认值是否可以为null?

根据反馈,时间类型的数据默认值往往在业务上无法赋予一个确定的值,而”1970-01-01“、”0000-01-01“等等的默认值在开发过程中会增加判断工作,经过新版MYSQL(版本号8.0.19)测试,为null的数据可以走索引,故开放时间类型的数据默认值的限制。
* 补充不建议为Null的技术观点:老版本MYSQL在对null值的判断上无法走索引,不能使用=,<,>这样的运算符,对null做算术运算的结果都是null,count时不会包括null行,null比空字符串都需要更多的存储空间。

2.json数据类型是否建议使用?

关于json数据类型的意义、优缺点和设计思路可以详见附件文档json部分,考虑到目前开发进度,我来针对所有作为检索列的json数据类型字段增加虚拟列以防止性能问题的产生(开发无需更改任何代码),前提需要各位老师提供对应的SQL语句或者KEY。

3.text数据类型是否建议使用?

关于text数据类型的缺点和设计思路详见附件文档text部分(text类型的基本原则是能不用就不用),否则后期优化也会产生一定工作量。

4.字段名称与数据类型不一致

比如新建表 epidemic_inout_dynamics_info 的字段 effective_time 类型为varchar(500),这种工单在我看来单从技术角度肯定是不符合规范,并且也无法从技术角度上去限制,故需要工单发起人说明下此设计的需要和目的,如无特殊需求,工单会考虑做驳回处理。

5.审核消息通知

目前运维框架平台,都采用钉钉对接的方式进行消息提醒,不必微信群里再确认,看到审核通过执行成功,就代表数据库修改完成。


MYSQL优化主要分为以下四大方面:

设计:存储引擎,字段类型,范式与逆范式

功能:索引,缓存,分区分表。

架构:主从复制,读写分离,负载均衡。

合理SQL:测试,经验。

MySQL模式开发设计规范问题与建议

Mysql逻辑架构

MySQL模式开发设计规范问题与建议

MySQL查询过程


优化与建议总结会按照【高危】、【强制】、【建议】三个级别进行标注,遵守优先级从高到低。对于不满足【高危】和【强制】两个级别的设计,DBA会强制要求修改。


名称设计

  1. 【强制】表和列的名称必须控制在32个字符以内,表名只能使用字母、数字和下划线,一律小写。
  2. 【强制】表名、字段名不应该使用数据库内定义的关键字,如:DATE、PASSWORD、VALUE、STATUS 等。

字段类型设计

bigint

【建议】占用8字节,比int类型占用空间大一倍,需要根据实际业务选择是否使用bigint,如果int类型可以满足优先使用int类型,慎用bigint类型。必须有默认值且不能为NULL,作为自增列需设置无符号标志unsigned。

UNSIGNED属性就是将数字类型无符号化,与C、C++这些程序语言中的unsigned含义相同。例如,INT的类型范围是-2 147 483 648 ~ 2 147 483 647, INT UNSIGNED的范围类型就是0 ~ 4 294 967 295。

在MYSQL中整型范围:

类型 大小 范围(有符号) 范围(无符号) 用途

TINYINT 1 字节 (-128,127) (0,255) 小整数值

SMALLINT 2 字节 (-32 768,32 767) (0,65 535) 大整数值

MEDIUMINT 3 字节 (-8 388 608,8 388 607) (0,16 777 215) 大整数值

INT或INTEGER 4 字节 (-2 147 483 648,2 147 483 647) (0,4 294 967 295) 大整数值

int、smallint、tinytint

【建议】尽量使用存储空间小的数据类型,比如状态字段应选择TINYINT数据类型。业务上无负数数据要求的使用无符号的数据类型,例如交易金额字段应优先选择:MEDIUMINT ZEROFILL, 必须有默认值且不能为NULL,作为自增列需设置无符号标志unsigned。

bit

【高危】禁止使用此类型,此类型加上索引会导致查询结果错误,并且SQL审核平台前端不支持显示此类型数据。

date、time

【建议】业务上只存储年份、日期、时间的只能选择YEAR、DATE、TIME,不能使用DATETIME替代。必须 有默认值且不能为NULL。

datetime

【建议】在不能选择TIMESTAMP的业务场景才能选择DATETIME(),因为datetime占用8字节,timestamp仅占用4字节,但是范围为1970-01-01 00:00:01到2038-01-01 00:00:00。根据业务场景可以为NULL,保证有默认值且尽量不能为NULL,例如:
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'。

decimal

【强制】存储金钱的字段,建议用int,程序端乘以100和除以100进行存取。int占用四个字节,大部分情况下更省空间。

金额浮点类型,此处不建议。日期可以转时间:

一、date型转换成int型

UNIX_TIMESTAMP('1997-10-04 22:23:00') =====> 875996580

二、int型转换成date型

FROM_UNIXTIME(875996580) =====> '1997-10-04 22:23:00'

json

【建议】

其实,没有JSON数据类型的支持,我们一样可以通过varchar类型或者text等类型来保存这一格式的数据,但是,为什么还要专门增加这一数据格式的支持呢?其中肯定有较varchar或者text、blob来存储此类型更优越的地方。
保证了JSON数据类型的强校验,JSON数据列会自动校验存入此列的内容是否符合JSON格式,非正常格式则报错,而varchar类型、text类型和blob类型本身是不存在这种机制的。
MySQL同时提供了一组操作JSON类型数据的内置函数。
更优化的存储格式,存储在JSON列中的JSON数据会被转成内部特定的存储格式,允许快速读取。
当服务器再次读取JSON文档时,不需要重新解析文本获取该值。
通过键或数组索引直接查找子对象或嵌套值,而不需要读取文档中的所有值。
通过虚拟列的功能可以对JSON中的部分数据进行索引。
存储的容量大,近似于LongBlob与LongText(4G)。

有了虚拟列,似乎一张表只需包含一个int类型的id,一个json类型的content就行了,需要搜索和排序的通过建立虚拟列的方式,但是如果列比较多会增加mysql本身的维护成本。
json的格式要固定,不能随意更改,因为代码是和格式强耦合的,如果变了那要大改,所以这就是mysql和mongodb这类nosql的一个区别,mysql并不是无模式的,对于一张表的那个json字段,模式其实也是固化在json里面而已。
对单体类修改,mybatis自动生成的类不能用,需要自己写sql,属性名称千万不能弄错,没有语法提示,错了不容易发现。
开发成本(写代码)的成本增加。
维护的成本增加,如果后面需要对json里面的另外一个字段进行模糊查找和排序,那么得增加虚拟列,重建索引,代码也要改,而如果是结构化的数据,只要一个DDL即可。
json看似更灵活,其实非常不灵活,远不如结构的数据灵活,通过update的语句,我们其实可以看出来部分修改的操作,比如JSON_SET,并不是实际只修改部分的数据,而是把全量的数据加载到内存,然后修改部分数据,在把修改后的全量的数据设值到mysql中,只不过mysql提供了函数让我们方便的操作json而已。
ETL过程中会涉及到类型转换,增加转换复杂度。

JSON内容不应该是多表需要的。比如一些信息其实是被多表共享的,这就不可以了,因为存在一个更新批量的问题。JSON内容一旦修改/创建其实是要影响多个表,那么慎用JSON。
JSON内容应该是依附性比较强,比如阶段信息永远都是依附于任务而存在,如果有逻辑要单独针对"阶段"内容进行处理,或者在表的概念上有需要以"阶段表"做主表的情况下,JSON化慎用,最好是单独处理为一张表。
JSON中的字段作为检索条件的慎用,尽管MySql支持对于JSON的使用,但是如果数据库服务器还要对JSON进行解析再处理无疑在效率上是有问题的。
JSON不能有默认值并且禁止设置为NOT NULL。

text、blob

【强制】
建议把 BLOB 或是TEXT列分离到单独的扩展表中
MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型,如果查询中包含这样的数据,在排序等操作时,就不能使用内存临时表,必须使用磁盘临时表进行。
而且对于这种数据,MySQL 还是要进行二次查询,会使 SQL 性能变得很差,但是不是说一定不能使用这样的数据类型。
如果一定要使用,建议把 BLOB 或是 TEXT列分离到单独的扩展表中,查询时一定不要使用 select * 而只需要取出必要的列,不需要 TEXT列的数据时不要对该列进行查询。
TEXT 或 BLOB 类型只能使用前缀索引,因为 MySQL 对索引字段长度是有限制的,所以 TEXT或 BLOB 类型只能使用前缀索引,并且 TEXT或 BLOB列上是 不能有默认值并且不能为NULL。 TINYTEXT和TEXT请使用VARCHAR类型代替, 因为varchar是变长存储,比char更省空间。MySQL server层规定一行所有文本最多存65535字节,因此在utf8mb4字符集下最多存16383个字符,超过会自动转换为mediumtext字段。而text在utf8mb4字符集下最多存16382个字符,mediumtext最多存(2^24-1)/4个字符,longtext最多存(2^32-1)/4个字符。
ETL过程中会涉及到类型转换或者不支持类型,增加转换复杂度或者无法转换。
TEXT、BLOB不能有默认值并且禁止设置为NOT NULL。

varchar

【建议】
经常变化的字段用varchar;
知道固定长度的用char;
超过255字节的只能用varchar;
能够用数字类型的字段尽量选择数字类型而不用字符串类型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了;
必须有默认值且不能为NULL。

视图的应用

【强制】
MySQL在处理视图时有两种算法,分别称为MERGE和TEMPTABLE。
在执行"CREATE VIEW"语句时可以指定使用哪种算法。不显式指定的话,Mysql默认使用Merge算法。
MERGE,将视图sql合并到主查询sql中,重新构成新sql进行查询。
TEMPTABLE,见文知意,就是将视图当作临时表来处理。
所谓MERGE是指在处理涉及到视图的操作时,将对视图的操作根据视图的定义进行展开,有点类似于C语言中的宏展开。
一般来说在能够使用MERGE算法的时候MySQL处理视图上没什么性能问题,因为可以使用索引、mysql查询优化算法,但并非在任何时候都能使用MERGE算法。事实上,只要视图的定义稍稍有点复杂,MySQL就没办法使用MERGE算法了。准确的说,只要视图定义中使用了以下SQL构造块就无法使用MERGE算法:
聚集函数
DISTINCT
GROUP BY
HAVING
集合操作(UNION, UNION ALL)
查询
对于复杂视图定义,MySQL使用了一种以不变应万变的方法,即先执行视图定义,将其结果使用临时表保存起来,这样后续对视图的操作就转化为对临时表的操作。不能不说从单从软件设计的角度看,这样的方法非常的优雅,然而从性能角度,这一方法也是非常的差。
使用注意事项:
1)尽量让视图采用merge算法,视图定义中避免DISTINCT、GROUP BY等集合相关运算;
2)如果视图很复杂采用TEMPTABLE的话,想办法减少TEMPTABLE记录数。

慢查询

【高危】
慢查询被称为压垮数据库的最后一棵稻草,可见优化慢查询有多么地重要,然而优化慢查询的难点不在于如何优化,而是在于优化的贯彻力。比如监测出来的慢查询,如若重构SQL需要其他部门配合,那么需要很强的贯彻力,在规定的时间内由DBA发起重构SQL技术指导意见,其他部门结合业务情况重新生成SQL提交至DBA审核,如若通过予以上线,如若不通过打 回再次修改,直至可以通过审核上线,否则优化慢查询也只是停留在优化慢查询会议上。


缓存参数优化

从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个IO是在毫秒级别,二者相差3个数量级。所以,要优化数据库,首先第一步需要优化的就是IO,尽可能将磁盘IO转化为内存IO。从MySQL 数据库IO相关参数(缓存参数)的角度来看看可以通过以下参数进行IO优化(建议级):

· query_cache_type : 如果全部使用innodb存储引擎,建议为0,如果使用MyISAM 存储引擎,建议为2,同时在SQL语句中显式控制是否使用query cache;

· query_cache_size: 根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB可能已经差不多了,大型的配置型静态数据可适当调大;

· binlog_cache_size: 一般环境2MB~4MB是一个合适的选择,事务较大且写入频繁的数据库环境可以适当调大,但不建议超过32MB;

· key_buffer_size: 如果不使用MyISAM存储引擎,16MB足以,用来缓存一些系统表信息等。如果使用 MyISAM存储引擎,在内存允许的情况下,尽可能将所有索引放入内存,简单来说就是“越大越好”;

· bulk_insert_buffer_size: 如果经常性的需要使用批量插入的特殊语句(上面有说明)来插入数据,可以适当调大该参数至16MB~32MB,不建议继续增大,某人8MB;

· innodb_buffer_pool_size: 如果不使用InnoDB存储引擎,可以不用调整这个参数,如果需要使用,在内存允许的情况下,尽可能将所有的InnoDB数据文件存放如内存中,同样将但来说也是“越大越好”;

· innodb_additional_mem_pool_size: 一般的数据库建议调整到8MB~16MB,如果表特别多,可以调整到32MB,可以根据error log中的信息判断是否需要增大;

· innodb_log_buffer_size: 默认是1MB,系的如频繁的系统可适当增大至4MB~8MB。当然如上面介绍所说,这个参数实际上还和另外的flush参数相关。一般来说不建议超过32MB;

· innodb_max_dirty_pages_pct: 根据以往的经验,重启恢复的数据如果要超过1G的话,启动速度会比较慢,几乎难以接受,所以建议不大于1GB/innodb_buffer_pool_size(GB)*100这个值。当然,如果你能够忍受启动时间比较长,而且希望尽量减少内存至磁盘的flush,可以将这个值调整到90,但不建议超过90。

注:以上取值范围仅仅只是根据以往遇到的数据库场景所得到的一些优化经验值,并不一定适用于所有场景,所以在实际优化过程中还需要大家自己不断的调整分析

mysql设计规范 mysql基础 MongoDB Mybatis mysql优化 sql审核平台 sql查询优化 innodb mysql https error sql优化 负载均衡 静态数据 json http utf8 数据库 慢查询
分享到:

您可能还会对下面的文章感兴趣: