开发可读写,可修改表结构。开发人员可以修改表结构,可以随意修改其中的数据但是需要保证不影响其他开发同事。
开发可读写,开发人员可以通过工具修改表结构。
开发人员不允许直接在线上环境进行数据库操作,如果需要操作必须找 DBA 进行操作并进行相应记录,禁止进行压力测试。
重点的问题,各个环境的 mysql 服务器对应的用户权限,一定要做到权限划分明确,有辨识度,能具体区分业务场景等。
在 MySQL 中,数据库和表对就于那些目录下的目录和文件。因而,操作系统的敏感性决定数据库和表命名的大小写敏感。
同一个模块的表尽可能使用相同的前缀,表名称尽可能表达含义。所有日志表均以 log_ 开头
表引擎取决于实际应用场景;日志及报表类表建议用 myisam,与交易,审核,金额相关的表建议用 innodb 引擎。如无说明,建表时一律采用 innodb 引擎
默认使用 utf8mb4 字符集,数据库排序规则使用 utf8mb4_general_ci,(由于数据库定义使用了默认,数据表可以不再定义,但为保险起见,建议都写上
采用 utf8 编码的 MySQL 无法保存占位是 4 个字节的 Emoji 表情。为了使后端的项目,全面支持客户端输入的 Emoji 表情,升级编码为 utf8mb4 是最佳解决方案。对于 JDBC 连接串设置了 characterEncoding 为 utf8 或者做了上述配置仍旧无法正常插入 emoji 数据的情况,需要在代码中指定连接的字符集为 utf8mb4。
所有表、字段均应用 comment 列属性来描述此表、字段所代表的真正含义,如枚举值则建议将该字段中使用的内容都定义出来。
如无说明,表中的第一个 id 字段一定是主键且为自动增长,禁止在非事务内作为上下文作为条件进行数据传递。禁止使用 varchar 类型作为主键语句设计。
如无说明,表必须包含 create_time 和 modify_time 字段,即表必须包含记录创建时间和修改时间的字段
如无说明,表必须包含 is_del,用来标示数据是否被删除,原则上数据库数据不允许物理删除。
相同点:TIMESTAMP 列的显示格式与 DATETIME 列相同。显示宽度固定在 19 字符,并且格式为 YYYY-MM-DD HH:MM:SS。
不同点:TIMESTAMP
将当前时间作为 ts 的默认值:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP。当行更新时,更新 ts 的值:ts TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP。
可以将 1 和 2 结合起来:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。
使用 INT UNSIGNED 而不是 char(15) 来存储 ipv4 地址,通过 MySQL 函数 inet_ntoa 和 inet_aton 来进行转化。Ipv6 地址目前没有转化函数,需要使用 DECIMAL 或者两个 bigINT 来存储。
INT[M],M 值代表什么含义?
注意数值类型括号后面的数字只是表示宽度而跟存储范围没有关系。很多人他们认为 INT(4) 和 INT(10) 其取值范围分别是 (-9999 到 9999) 和(-9999999999 到 9999999999),这种理解是错误的。其实对整型中的 M 值与 ZEROFILL 属性结合使用时可以实现列值等宽。不管 INT[M]中 M 值是多少,其取值范围还是 (-2147483648 到 2147483647 有符号时),(0 到 4294967295 无符号时)。
显示宽度并不限制可以在列内保存的值的范围,也不限制超过列的指定宽度的值的显示。当结合可选扩展属性 ZEROFILL 使用时默认补充的空格用零代替。例如:对于声明为 INT(5) ZEROFILL 的列,值 4 检索为 00004。请注意如果在整数列保存超过显示宽度的一个值,当 MySQL 为复杂联接生成临时表时会遇到问题,因为在这些情况下 MySQL 相信数据适合原列宽度,如果为一个数值列指定 ZEROFILL, MySQL 自动为该列添加 UNSIGNED 属性。
什么时候用 CHAR,什么时候用 VARCHAR?
CHAR 和 VARCHAR 类型类似,但它们保存和检索的方式不同。它们的最大长度和是否尾部空格被保留等方面也不同。CHAR 和 VARCHAR 类型声明的长度表示你想要保存的最大字符数。例如,CHAR(30) 可以占用 30 个字符。
CHAR 列的长度固定为创建表时声明的长度。长度可以为从 0 到 255 的任何值。当保存 CHAR 值时,在它们的右边填充空格以达到指定的长度。当检索到 CHAR 值时,尾部的空格被删除掉。在存储或检索过程中不进行大小写转换。
VARCHAR 列中的值为可变长字符串。长度可以指定为 0 到 65,535 之间的值。(VARCHAR 的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是 65,532 字节)。同 CHAR 对比,VARCHAR 值保存时只保存需要的字符数,另加一个字节来记录长度(如果列声明的长度超过 255,则使用两个字节)。VARCHAR 值保存时不进行填充。当值保存和检索时尾部的空格仍保留,符合标准 SQL。
char 适合存储用户密码的 MD5 哈希值,它的长度总是一样的。对于经常改变的值,char 也好于 varchar,因为固定长度的行不容易产生碎片,对于很短的列,char 的效率也高于 varchar。char(1) 字符串对于单字节字符集只会占用一个字节,但是 varchar(1) 则会占用 2 个字节,因为 1 个字节用来存储长度信息。
MySQL 的查询速度依赖良好的索引设计,因此索引对于高性能至关重要。合理的索引会加快查询速度(包括 UPDATE 和 DELETE 的速度,MySQL 会将包含该行的 page 加载到内存中,然后进行 UPDATE 或者 DELETE 操作),不合理的索引会降低速度。MySQL 索引查找类似于新华字典的拼音和部首查找,当拼音和部首索引不存在时,只能通过一页一页的翻页来查找。当 MySQL 查询不能使用索引时,MySQL 会进行全表扫描,会消耗大量的 IO。索引的用途:去重、加速定位、避免排序、覆盖索引。
InnoDB 存储引擎中,secondary index(非主键索引)中没有直接存储行地址,存储主键值。如果用户需要查询 secondary index 中所不包含的数据列时,需要先通过 secondary index 查找到主键值,然后再通过主键查询到其他数据列,因此需要查询两次。覆盖索引的概念就是查询可以通过在一个索引中完成,覆盖索引效率会比较高,主键查询是天然的覆盖索引。合理的创建索引以及合理的使用查询语句,当使用到覆盖索引时可以获得性能提升。比如 SELECT email,uid FROM user_email WHERE uid=xx,如果 uid 不是主键,适当时候可以将索引添加为 index(uid,email),以获得性能提升。
InnoDB 的 secondary index 使用 b+tree 来存储,因此在 UPDATE、DELETE、INSERT 的时候需要对 b+tree 进行调整,过多的索引会减慢更新的速度。
对字符串使用前缀索引,前缀索引长度不超过 8 个字符,建议优先考虑前缀索引,必要时可添加伪列并建立索引。
不要索引 blob/text 等字段,不要索引大型字段,这样做会让索引占用太多的存储空间
前缀索引说白了就是对文本的前几个字符(具体是几个字符在建立索引时指定)建立索引,这样建立起来的索引更小,所以查询更快。前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)。
建立前缀索引的语法:ALTER TABLE table_name ADD KEY(column_name(prefix_length));
限制分页展示的页数 只能点击上一页、下一页 采用延迟关联
假如有类似下面分页语句:SELECT * FROM table ORDER BY id LIMIT 10000, 10 由于 MySQL 里对 LIMIT OFFSET 的处理方式是取出 OFFSET+LIMIT 的所有数据,然后去掉 OFFSET,返回底部的 LIMIT。所以,在 OFFSET 数值较大时,MySQL 的查询性能会非常低。可以使用 id > n 的方式进行解决:
使用 id > n 的方式有局限性,对于 id 不连续的问题,可以通过翻页的时候同时传入最后一个 id 方式来解决。
http://example.com/page.php?last=100select * from table where id<100 order by id desc limit 10
//上一页 http://example.com/page.php?first=110select * from table where id>110 order by id desc limit 10
这种方式比较大的缺点是,如果在浏览中有插入/删除操作,翻页不会更新,而总页数可能仍然是根据新的 count(*) 来计算,最终可能会产生某些记录访问不到。为了修补这个问题,可以继续引入当前页码以及在上次翻页以后是否有插入/删除等影响总记录数的操作并进行缓存
select * from table where id >= (select id from table order by id limit #offset#, 1)
EXPLAIN 语句(在 MySQL 客户端中执行)可以获得 MySQL 如何执行 SELECT 语句的信息。通过对 SELECT 语句执行 EXPLAIN,可以知晓 MySQL 执行该 SELECT 语句时是否使用了索引、全表扫描、临时表、排序等信息。尽量避免 MySQL 进行全表扫描、使用临时表、排序等。详见官方文档。
union all 与 union 有什么区别?
union 和 union all 关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。
union 在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。如:
select * from test_union1union select * from test_union2
这个 SQL 在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
而 union all 只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
从效率上说,union all 要比 union 快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用 union all,如下:
select * from test_union1 union all select * from test_union2
单表一到两年内数据量超过 500w 或数据容量超过 10G 考虑分表,需提前考虑历史数据迁移或应用自行删除历史数据,采用等量均衡分表或根据业务规则分表均可。要分表的数据表必须与 DBA 商量分表策略
日志类数据不建议存储在 MySQL 上,优先考虑 Hbase 或 OceanBase,如需要存储请找 DBA 评估使用压缩表存储。

