MySQL数据库开发的三十六条军规
石展@赶集 http://weibo.com/wushizhan
来自一线的实战经验 每一条军规背后都是血淋林的教训
不要华丽,只要实用 若有一条让你有所受益,慰矣
主要针对数据库开发人员
总是在灾难发生后,才想起容灾的重要性; 总是在吃亏后,才记得曾经有人提醒过。
目录
- 核心军规 5 条
- 字段类军规 6 条
- 索引类军规 5 条
- SQL 类军规 15 条
- 约定类军规 5 条
核心
1. 不在数据库做运算
别让脚指头想事情,那是脑瓜子的职责
让数据库做擅长的事情:
- 尽量不在数据库做运算
- 复杂运算移到程序端 CPU
- 尽可能简单应用 MySQL
举例:
md5() / order by rand()
2. 控制单表数据量
一年内单表数据量预估:
- 纯 INT 不超过 1000W
- 含 CHAR 不超 500W
合理分表不超载:
- USERID
- DATE
- AREA
- ……
建议单库不超过 300~400 表
3. 保持表身段苗条
表字段数少而精
单表多少字段合适?
单表 1 G 体积 500W 行评估
- 顺序读 1G 文件需要 N 秒
- 单行不超过 200 Byte
- 单表不超过 50 个纯 int 字段
- 单表不超过 20 个 char(10) 字段
单表字段数上限控制在 20~50 个
4. 平衡范式与冗余
- 严格遵循三大范式?
- 效率优先、提升性能
- 没有绝对的对与错
- 适当时牺牲范式、加入冗余
- 但会增加代码复杂度
5. 拒绝 3B
数据库并发像城市交通:非线性增长
拒绝 3B
- Big SQL,大 SQL
- Big Transaction,大事务
- Big Batch,大批量
字段
- 用好数值字段类型
三类数值类型:
- TINYINT, 1 Byte
- SMALLINT, 2B
- MEDIUMINT, 3B
- INT, 4B; BIGINT 8B
- FLOAT, 4B; DOUBLE, 8B
- DECIMAL(M, D)
- 将字符串转化为数字
数字型 vs. 字符串型索引
- 更高效
- 查询更快
- 占用空间更小 举例:用无符号 INT 存储 IP,而非 CHAR(15)
- INT UNSIGNED
- INET_ATON()
- INET_NTOA()
- 优先使用 ENUM 或 SET
优先使用 ENUM 或 SET
- 字符串
- 可能值已知且有限 存储
- ENUM 占用 1 字节,转为数值运算
- SET 视节点定,最多占用 8 字节
- 比较时需要加单引号 ‘ (即使是数值)
举例:
sexenum(‘F’, ‘M’) COMMENT ‘性别’c1enum(‘1’, ‘2’, ‘3’) COMMENT ‘职介审核’
- 避免使用 NULL
避免使用 NULL 字段
- 很难进行查询优化
- NULL 列加索引,需要额外空间
- 含 NULL 复合索引无效
举例:
✘
achar(32) DEFAULT NULL ✘bint(10) NOT NULL √cint(10) NOT NULL DEFAULT 0
- 少用并拆分 TEXT/BLOB
TEXT 类型处理性能远低于 VARCHAR
- 强制生成硬盘临时表
- 浪费更多空间
- VARCHAR(65535) ==> 64K (注意 UTF-8) 尽量不用 TEXT/BLOB 数据类型 若必须使用则拆分到单独的表 举例: CREATE TABLE t1( id INT NOT NULL AUTO_INCREMENT, data text NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB;
- 不在数据库存图片
索引
- 谨慎合理添加索引
谨慎合理添加索引
- 改善查询
- 减慢更新
- 索引不是越多越好 能不加的索引尽量不加
- 综合评估数据库密度和数据分布
- 最好不超过字段数 20% 结合核心 SQL 优先考虑覆盖索引 举例:不要给“性别”列建索引
- 字符字段必须建立前缀索引
区分度:
- 单字母区分度:26
- 4 字母区分度:26 × 26 × 26 × 26 = 456,976
- 5 字母区分度:26 × 26 × 26 × 26 × 26 = 11,881,376
- 6 字母区分度:26 × 26 × 26 × 26 × 26 × 26 = 308,915,776
字符字段必须建前缀索引
pinyinvarchar(100) DEFAULT NULL COMMENT ‘小区拼音’, KEYidx_pinyin(pinyin(8)), ) ENGINE=InnoDB
- 不在索引列做运算
不在索引列进行数学运算或函数运算
- 无法使用索引
- 导致全表扫描 举例:
✘ select * from table where to_days(current_date) - to_days(date_col) <= 10 √ select * from table where date_col >= DATE_SUB(‘2011-19-22’, INTERVAL 10 DAY); where id+1 = 100 和 id = 100 - 1,效率差很远
- 自增列或全局 ID 做 InnoDB 主键
- 对主键建立聚簇索引
- 二级索引存储主键值
- 主键不应更新修改
- 按自增顺序插入值
- 忌用字符串做主键
- 聚簇索引分裂
- 推荐用独立于业务的 AUTO_INCREMENT 列或全局 ID 生成器做代理主键
- 若不指定主键,InnoDB 会用唯一且费控值索引替代
- 尽量不用外键 线上 OLTP 系统(线下系统另论)
- 外键可节省开发量
- 有额外开销
- 逐行操作
- 可“到达”其他表,意味着锁
- 高并发时容易死锁
由程序保证约束
SQL
- SQL语句尽可能简单 大 SQL vs. 多个简单 SQL
- 传统设计思想
- BUT MySQL NOT
- 一条 SQL 只能在一个 CPU 运算
- 5000+ QPS 的高并发中,1 秒大 SQL 意味着?
- 可能一条大 SQL 就把整个数据库堵死 拒绝大 SQL,拆解成多条简单 SQL
- 简单 SQL 缓存命中率更高
- 减少锁表时间,特别是 MyISAM
- 用上多 CPU
- 保持事务(连接)短小 保持事务 / DB 链接短小精悍
- 事务/连接使用原则:即开即用,用完即关
- 与事务无关操作放到事务外面,减少锁资源的占用
- 不破坏一致性前提下,使用多个短事务代替长事务 举例:
- 发帖时的图片等待上传
- 大量的 sleep 连接
- 尽可能避免使用 SP / TRIG / FUNC 线上 OLTP 系统(线下库另论)
- 尽可能少用存储过程
- 尽可能少用触发器
- 减少使用 MySQL 函数对结果进行处理
由客户端程序负责。
- 尽量不用 SELECT * 用 SELECT * 时
- 更多消耗 CPU、内存、IO、网络带宽
- 先向数据库请求所有列,然后丢掉不需要的列? 尽量不用 SELECT *,只取需要的数据列
- 更安全的设计:减少表变化带来的影响
- 未使用 covering index 提供可能性
- Select / Join 减少硬盘临时表生成,特别是有 TEXT/BLOB 时
✘ SELECT * FROM tag WHERE id = 999184
√ SELECT keyword FROM tag WHERE id = 999184
- 改写 OR 为 IN() 同一字段,将 OR 改写为 IN( )
- OR 效率:O(n)
- IN 效率:O(log n)
- 当 n 很大时,OR 会慢很多
注意控制 IN 的个数,建议 n 小于 200
举例:
✘ SELECT * FROM opp WHERE phone = ‘12345678’ or phone = ‘42242233’;
√ SELECT * FROM opp WHERE phone IN (‘12345678’, ‘42242233’);
- 改写 OR 为 UNION 不同字段,将 OR 改为 UNION
- 减少对不同字段进行 OR 查询
- Merge Index 往往很弱智
- 如果有足够信心:set global optimizer_switch = ‘index_merge=off’;
举例:
✘ SELECT * FROM opp WHERE phone = ‘010-88886666’ OR cellPhone = ‘13800138000’;
√ SELECT * FROM opp WHERE phone = ‘010-88886666’
UNION
SELECT * FROM opp WHERE cellPhone = ‘13800138000’;
- 避免负向查询和 % 前缀模糊查询 避免负向查询:NOT, !=, <>, !<, !>, NOT EXISTS, NOT IN, NOT LIKE … 避免 % 前缀模糊查询
- B+ Tree
- 使用不了索引
- 导致全表扫描 举例:
- 减少使用 COUNT(*)
几个有趣的例子
- COUNT(COL) vs. COUNT(*)
- COUNT(*) vs. COUNT(1)
- COUNT(1) vs. COUNT(0) vs. COUNT(100)
示例:
idint(10) NOT NULL AUTO_INCREMENT COMMENT ‘公司的 id’,sale_idint(10) unsigned DEFAULT NULL,
结论:
- COUNT(*) = COUNT(1)
- COUNT(0) = COUNT(1)
- COUNT(1) = COUNT(100)
- COUNT(*) != COUNT(col) WHY? MyISAM vs. InnoDB
- 不带 WHERE COUNT()
- 带 WHERE COUNT() COUNT(*) 的资源开销大,尽量不用/少用 计数统计
- 实时统计:用 memchache,双向更新,凌晨跑基准
- 非实时统计:计量用单独统计表,定期重算
- LIMIT 高效分页 传统分页: SELECT * FROM table LIMIT 10000,10; LIMIT 原理:
- LIMIT 10000, 10
- 偏移量越大则越慢
推荐分页:
SELECT * FROM table WHERE id >= 23423 LIMIT 11;
10+1 (每页 10 条)
SELECT * FROM table WHERE id >= 23434 LIMIT 11; 分页方式二: SELECT * FROM table WHERE id >= (SELECT id FROM table LIMIT 10000,1) LIMIT 10; 分页方式三: SELECT * FROM table INNER JOIN (SELECT id FROM table LIMIT 10000, 10) USING (id); 分页方式四:
程序取 ID
SELECT id FROM table LIMIT 10000, 10; SELECT * FROM table WHERE id IN (123, 456 … ); 可能需按场景分析并重组索引 示例:
- 用 UNION ALL 而非 UNION 若无需对结果进行去重,则用 UNION ALL;UNION 有去重开销 举例: SELECT * FROM detail20091128 UNION ALL SELECT * FROM detail20110427 UNION ALL SELECT * FROM detail20110426 UNION ALL SELECT * FROM detail20110425 UNION ALL SELECT * FROM detail20110424 UNION ALL SELECT * FROM detail20110423;
- 分解连接保证高并发
高并发 DB 不建议进行两个表以上的 JOIN
适当分解连接保证高并发
- 可缓存大量早期数据
- 使用了多个 MyISAM 表
- 对大表的小 ID 使用 IN()
- 连接引用同一个表多次 举例: SELECT * FROM tag JOIN tag_post ON tag_post.tag.id = tag.id JOIN post ON tag_post.post_id = WHERE tag.tag = ‘二手玩具’; → SELECT * FROM tag WHERE tag = ‘二手玩具’; SELECT * FROM tag_post WHERE tag_id = 1321; SELECT * FROM post WHERE post.id IN (123, 456, 314, 141);
- GROUP BY 去除排序
GROUP BY 实现
- 分组
- 自动排序 无需排序:ORDER BY NULL 特定排序:GROUP BY DESC/ASC 举例
- 同数据类型的列值比较
原则:数字对数字,字符对字符
数值列与字符类型比较
- 同时转换为双精度
- 进行比对 字符列与数值类型比较
- 字符列整列转数值
- 不会使用索引查询
举例:
remarkvarchar(50) NOT NULL COMMENT ‘备注,默认为空’,
SELECT id, gift_code FROM gift WHERE deal_id = 640 AND remark = 115127;
→
SELECT id, gift_code FROM pool_gift WHERE deal_id = 640 AND remark = ‘115127’;
- Load data 导数据
批量数据快速导入
- 成批装载比单行装载更快,不需要每次刷新缓存
- 无索引时装载比索引装载更快
- Insert values, values, values 减少索引刷新
- Load data 比 Insert 快约 20 倍 尽量不用 INSERT … SELECT
- 延迟
- 同步出错
- 打散大批量更新 大批量更新凌晨操作,避开高峰 凌晨不限制 白天上限默认为 100 条/秒(特殊再议) 举例: UPDATE post SET tag = 1 WHERE id in (1, 2, 3); SLEEP 0.01; UPDATE post SET tag = 1 WHERE id in (4, 5, 6); SLEEP 0.01; …
- Know Every SQL!
- EXPLAIN
- SHOW PROFILE
- SHOW SLOW LOG
- SHOW PROCESSLIST
- MySQL dumpslow
- SHOW QUERY_RESPONSE_TIME(Percona)
约定
- 隔离线上线下 构建数据库的生态环境
- 开发无线上库操作权限 原则:线上连线上,线下连线下
- 实时数据用 real 库
- 模拟环境用 sim 库
- 测试用 qa 库
- 开发用 dev 库 案例:
- 禁止未经 DBA 确认的子查询
MySQL 子查询
- 大部分情况优化较差
- 特别 WHERE 中使用 IN id 的子查询
- 一般可用 JOIN 改写 举例: ✘ select * from table1 where id in (select id from table2); ✘ insert into table1 (select * from table2); // 可能导致复制异常
- 永远不在程序端显式加锁
永远不在程序端对数据库显式加锁
- 外部锁对数据库不可控
- 高并发时是灾难
- 极难调试和排查 并发扣款等一致性问题
- 采用事物
- 相对值修改
- Commit 前二次校验冲突
- 统一字符集为 UTF8
字符集:
- MySQL 4.1 以前只有 latin1
- 为多语言支持增加了多字符集
- 也带来了 N 多问题
- 保持简单
统一字符集:UTF8
校对规则:utf8_general_ci
乱码:SET NAMES UTF8
- 统一命名规范 库表等名称同意用小写
- Linux VS Windows
- MySQL 库表大小写敏感
- 字段名大小写不敏感 索引命名默认为 “idx_字段名” 库名用缩写,尽量在 2~7 个字母
- DataSharing ==> ds
注意避免使用保留字命名
举例:
✘ select * from return;
√ select * from
return;
MySQL数据库开发的三十六条军规_石展 https://github.com/calvinlau/calvinwiki/wiki/MySQL https://my.oschina.net/BearCatYN/blog/476748 http://www.educity.cn/wenda/402373.htmlMySQL数据库开发的三十六条军规 石展@赶集 http://weibo.com/wushizhan 来自一线的实战经验 每一条军规背后都是血淋林的教训 不要华丽,只要实用 若有一条让你有所受益,慰矣 主要针对数据库开发人员 总是在灾难发生后,才想起容灾的重要性; 总是在吃亏后,才记得曾经有人提醒过。 目录
- 核心军规 5 条
- 字段类军规 6 条
- 索引类军规 5 条
- SQL 类军规 15 条
- 约定类军规 5 条 核心
- 不在数据库做运算
别让脚指头想事情,那是脑瓜子的职责 让数据库做擅长的事情:
- 尽量不在数据库做运算
- 复杂运算移到程序端 CPU
- 尽可能简单应用 MySQL
举例:
✘ md5() / order by rand()
- 控制单表数据量
一年内单表数据量预估:
- 纯 INT 不超过 1000W
- 含 CHAR 不超 500W 合理分表不超载:
- USERID
- DATE
- AREA
- ……
建议单库不超过 300~400 表
- 保持表身段苗条
表字段数少而精 单表多少字段合适? 单表 1 G 体积 500W 行评估
- 顺序读 1G 文件需要 N 秒
- 单行不超过 200 Byte
- 单表不超过 50 个纯 int 字段
- 单表不超过 20 个 char(10) 字段
单表字段数上限控制在 20~50 个
- 平衡范式与冗余
- 严格遵循三大范式?
- 效率优先、提升性能
- 没有绝对的对与错
- 适当时牺牲范式、加入冗余
- 但会增加代码复杂度
- 拒绝 3B
数据库并发像城市交通:非线性增长 拒绝 3B
- Big SQL,大 SQL
- Big Transaction,大事务
- Big Batch,大批量
字段
- 用好数值字段类型 三类数值类型:
- TINYINT, 1 Byte
- SMALLINT, 2B
- MEDIUMINT, 3B
- INT, 4B; BIGINT 8B
- FLOAT, 4B; DOUBLE, 8B
- DECIMAL(M, D)
- 将字符串转化为数字
数字型 vs. 字符串型索引
- 更高效
- 查询更快
- 占用空间更小 举例:用无符号 INT 存储 IP,而非 CHAR(15)
- INT UNSIGNED
- INET_ATON()
- INET_NTOA()
- 优先使用 ENUM 或 SET
优先使用 ENUM 或 SET
- 字符串
- 可能值已知且有限 存储
- ENUM 占用 1 字节,转为数值运算
- SET 视节点定,最多占用 8 字节
- 比较时需要加单引号 ‘ (即使是数值)
举例:
sexenum(‘F’, ‘M’) COMMENT ‘性别’c1enum(‘1’, ‘2’, ‘3’) COMMENT ‘职介审核’
- 避免使用 NULL
避免使用 NULL 字段
- 很难进行查询优化
- NULL 列加索引,需要额外空间
- 含 NULL 复合索引无效
举例:
✘
achar(32) DEFAULT NULL ✘bint(10) NOT NULL √cint(10) NOT NULL DEFAULT 0
- 少用并拆分 TEXT/BLOB
TEXT 类型处理性能远低于 VARCHAR
- 强制生成硬盘临时表
- 浪费更多空间
- VARCHAR(65535) ==> 64K (注意 UTF-8) 尽量不用 TEXT/BLOB 数据类型 若必须使用则拆分到单独的表 举例: CREATE TABLE t1( id INT NOT NULL AUTO_INCREMENT, data text NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB;
- 不在数据库存图片
索引
- 谨慎合理添加索引
谨慎合理添加索引
- 改善查询
- 减慢更新
- 索引不是越多越好 能不加的索引尽量不加
- 综合评估数据库密度和数据分布
- 最好不超过字段数 20% 结合核心 SQL 优先考虑覆盖索引 举例:不要给“性别”列建索引
- 字符字段必须建立前缀索引
区分度:
- 单字母区分度:26
- 4 字母区分度:26 × 26 × 26 × 26 = 456,976
- 5 字母区分度:26 × 26 × 26 × 26 × 26 = 11,881,376
- 6 字母区分度:26 × 26 × 26 × 26 × 26 × 26 = 308,915,776
字符字段必须建前缀索引
pinyinvarchar(100) DEFAULT NULL COMMENT ‘小区拼音’, KEYidx_pinyin(pinyin(8)), ) ENGINE=InnoDB
- 不在索引列做运算
不在索引列进行数学运算或函数运算
- 无法使用索引
- 导致全表扫描 举例:
✘ select * from table where to_days(current_date) - to_days(date_col) <= 10 √ select * from table where date_col >= DATE_SUB(‘2011-19-22’, INTERVAL 10 DAY); where id+1 = 100 和 id = 100 - 1,效率差很远
- 自增列或全局 ID 做 InnoDB 主键
- 对主键建立聚簇索引
- 二级索引存储主键值
- 主键不应更新修改
- 按自增顺序插入值
- 忌用字符串做主键
- 聚簇索引分裂
- 推荐用独立于业务的 AUTO_INCREMENT 列或全局 ID 生成器做代理主键
- 若不指定主键,InnoDB 会用唯一且费控值索引替代
- 尽量不用外键 线上 OLTP 系统(线下系统另论)
- 外键可节省开发量
- 有额外开销
- 逐行操作
- 可“到达”其他表,意味着锁
- 高并发时容易死锁
由程序保证约束
SQL
- SQL语句尽可能简单 大 SQL vs. 多个简单 SQL
- 传统设计思想
- BUT MySQL NOT
- 一条 SQL 只能在一个 CPU 运算
- 5000+ QPS 的高并发中,1 秒大 SQL 意味着?
- 可能一条大 SQL 就把整个数据库堵死 拒绝大 SQL,拆解成多条简单 SQL
- 简单 SQL 缓存命中率更高
- 减少锁表时间,特别是 MyISAM
- 用上多 CPU
- 保持事务(连接)短小 保持事务 / DB 链接短小精悍
- 事务/连接使用原则:即开即用,用完即关
- 与事务无关操作放到事务外面,减少锁资源的占用
- 不破坏一致性前提下,使用多个短事务代替长事务 举例:
- 发帖时的图片等待上传
- 大量的 sleep 连接
- 尽可能避免使用 SP / TRIG / FUNC 线上 OLTP 系统(线下库另论)
- 尽可能少用存储过程
- 尽可能少用触发器
- 减少使用 MySQL 函数对结果进行处理
由客户端程序负责。
- 尽量不用 SELECT * 用 SELECT * 时
- 更多消耗 CPU、内存、IO、网络带宽
- 先向数据库请求所有列,然后丢掉不需要的列? 尽量不用 SELECT *,只取需要的数据列
- 更安全的设计:减少表变化带来的影响
- 未使用 covering index 提供可能性
- Select / Join 减少硬盘临时表生成,特别是有 TEXT/BLOB 时
✘ SELECT * FROM tag WHERE id = 999184
√ SELECT keyword FROM tag WHERE id = 999184
- 改写 OR 为 IN() 同一字段,将 OR 改写为 IN( )
- OR 效率:O(n)
- IN 效率:O(log n)
- 当 n 很大时,OR 会慢很多
注意控制 IN 的个数,建议 n 小于 200
举例:
✘ SELECT * FROM opp WHERE phone = ‘12345678’ or phone = ‘42242233’;
√ SELECT * FROM opp WHERE phone IN (‘12345678’, ‘42242233’);
- 改写 OR 为 UNION 不同字段,将 OR 改为 UNION
- 减少对不同字段进行 OR 查询
- Merge Index 往往很弱智
- 如果有足够信心:set global optimizer_switch = ‘index_merge=off’;
举例:
✘ SELECT * FROM opp WHERE phone = ‘010-88886666’ OR cellPhone = ‘13800138000’;
√ SELECT * FROM opp WHERE phone = ‘010-88886666’
UNION
SELECT * FROM opp WHERE cellPhone = ‘13800138000’;
- 避免负向查询和 % 前缀模糊查询 避免负向查询:NOT, !=, <>, !<, !>, NOT EXISTS, NOT IN, NOT LIKE … 避免 % 前缀模糊查询
- B+ Tree
- 使用不了索引
- 导致全表扫描 举例:
- 减少使用 COUNT(*)
几个有趣的例子
- COUNT(COL) vs. COUNT(*)
- COUNT(*) vs. COUNT(1)
- COUNT(1) vs. COUNT(0) vs. COUNT(100)
示例:
idint(10) NOT NULL AUTO_INCREMENT COMMENT ‘公司的 id’,sale_idint(10) unsigned DEFAULT NULL,
结论:
- COUNT(*) = COUNT(1)
- COUNT(0) = COUNT(1)
- COUNT(1) = COUNT(100)
- COUNT(*) != COUNT(col) WHY? MyISAM vs. InnoDB
- 不带 WHERE COUNT()
- 带 WHERE COUNT() COUNT(*) 的资源开销大,尽量不用/少用 计数统计
- 实时统计:用 memchache,双向更新,凌晨跑基准
- 非实时统计:计量用单独统计表,定期重算
- LIMIT 高效分页 传统分页: SELECT * FROM table LIMIT 10000,10; LIMIT 原理:
- LIMIT 10000, 10
- 偏移量越大则越慢
推荐分页:
SELECT * FROM table WHERE id >= 23423 LIMIT 11;
10+1 (每页 10 条)
SELECT * FROM table WHERE id >= 23434 LIMIT 11; 分页方式二: SELECT * FROM table WHERE id >= (SELECT id FROM table LIMIT 10000,1) LIMIT 10; 分页方式三: SELECT * FROM table INNER JOIN (SELECT id FROM table LIMIT 10000, 10) USING (id); 分页方式四:
程序取 ID
SELECT id FROM table LIMIT 10000, 10; SELECT * FROM table WHERE id IN (123, 456 … ); 可能需按场景分析并重组索引 示例:
- 用 UNION ALL 而非 UNION 若无需对结果进行去重,则用 UNION ALL;UNION 有去重开销 举例: SELECT * FROM detail20091128 UNION ALL SELECT * FROM detail20110427 UNION ALL SELECT * FROM detail20110426 UNION ALL SELECT * FROM detail20110425 UNION ALL SELECT * FROM detail20110424 UNION ALL SELECT * FROM detail20110423;
- 分解连接保证高并发
高并发 DB 不建议进行两个表以上的 JOIN
适当分解连接保证高并发
- 可缓存大量早期数据
- 使用了多个 MyISAM 表
- 对大表的小 ID 使用 IN()
- 连接引用同一个表多次 举例: SELECT * FROM tag JOIN tag_post ON tag_post.tag.id = tag.id JOIN post ON tag_post.post_id = WHERE tag.tag = ‘二手玩具’; → SELECT * FROM tag WHERE tag = ‘二手玩具’; SELECT * FROM tag_post WHERE tag_id = 1321; SELECT * FROM post WHERE post.id IN (123, 456, 314, 141);
- GROUP BY 去除排序
GROUP BY 实现
- 分组
- 自动排序 无需排序:ORDER BY NULL 特定排序:GROUP BY DESC/ASC 举例
- 同数据类型的列值比较
原则:数字对数字,字符对字符
数值列与字符类型比较
- 同时转换为双精度
- 进行比对 字符列与数值类型比较
- 字符列整列转数值
- 不会使用索引查询
举例:
remarkvarchar(50) NOT NULL COMMENT ‘备注,默认为空’,
SELECT id, gift_code FROM gift WHERE deal_id = 640 AND remark = 115127;
→
SELECT id, gift_code FROM pool_gift WHERE deal_id = 640 AND remark = ‘115127’;
- Load data 导数据
批量数据快速导入
- 成批装载比单行装载更快,不需要每次刷新缓存
- 无索引时装载比索引装载更快
- Insert values, values, values 减少索引刷新
- Load data 比 Insert 快约 20 倍 尽量不用 INSERT … SELECT
- 延迟
- 同步出错
- 打散大批量更新 大批量更新凌晨操作,避开高峰 凌晨不限制 白天上限默认为 100 条/秒(特殊再议) 举例: UPDATE post SET tag = 1 WHERE id in (1, 2, 3); SLEEP 0.01; UPDATE post SET tag = 1 WHERE id in (4, 5, 6); SLEEP 0.01; …
- Know Every SQL!
- EXPLAIN
- SHOW PROFILE
- SHOW SLOW LOG
- SHOW PROCESSLIST
- MySQL dumpslow
- SHOW QUERY_RESPONSE_TIME(Percona)
约定
- 隔离线上线下 构建数据库的生态环境
- 开发无线上库操作权限 原则:线上连线上,线下连线下
- 实时数据用 real 库
- 模拟环境用 sim 库
- 测试用 qa 库
- 开发用 dev 库 案例:
- 禁止未经 DBA 确认的子查询
MySQL 子查询
- 大部分情况优化较差
- 特别 WHERE 中使用 IN id 的子查询
- 一般可用 JOIN 改写 举例: ✘ select * from table1 where id in (select id from table2); ✘ insert into table1 (select * from table2); // 可能导致复制异常
- 永远不在程序端显式加锁
永远不在程序端对数据库显式加锁
- 外部锁对数据库不可控
- 高并发时是灾难
- 极难调试和排查 并发扣款等一致性问题
- 采用事物
- 相对值修改
- Commit 前二次校验冲突
- 统一字符集为 UTF8
字符集:
- MySQL 4.1 以前只有 latin1
- 为多语言支持增加了多字符集
- 也带来了 N 多问题
- 保持简单
统一字符集:UTF8
校对规则:utf8_general_ci
乱码:SET NAMES UTF8
- 统一命名规范 库表等名称同意用小写
- Linux VS Windows
- MySQL 库表大小写敏感
- 字段名大小写不敏感 索引命名默认为 “idx_字段名” 库名用缩写,尽量在 2~7 个字母
- DataSharing ==> ds
注意避免使用保留字命名
举例:
✘ select * from return;
√ select * from
return;
MySQL数据库开发的三十六条军规_石展 https://github.com/calvinlau/calvinwiki/wiki/MySQL https://my.oschina.net/BearCatYN/blog/476748 http://www.educity.cn/wenda/402373.html