MySQL 是跑在 Docker 上,在某段时间内 MySQL 磁盘读取飙升,导致服务器卡死。
这是因为查询扫描行数过多导致的磁盘读取飙升吗?还是有其他原因。
磁盘读取飙升的问题该如何得到解决?优化查询 SQL ?换阿里云的 RDS ?
有大佬能解答下吗?
实例规格:ecs.c8i.xlarge
CPU:4 核 vCPU
内存:8G
磁盘:ESSD P1 40G
[mysqld] slow_query_log=ON long_query_time=3 innodb_buffer_pool_size = 1536M innodb_log_file_size=256M innodb_log_buffer_size=32M
阿里监控:
ATOP ( 10 分钟):
慢 SQL 日志:
新增行的主键值都特别大,然后就改成了数据库的自增策略,
但是新增行的 id 还是特别大,一看原来是 AUTO_INCREMENT 的值变成了 1933814216852774914 ,
然后听了 gpt 的使用 ALTER TABLE 表名 AUTO_INCREMENT = 1
命令去修改其值,
但是改完了一看 AUTO_INCREMENT 还是特别大,也没找到什么说明文档🥹
故来求教各位 v 友
]]>搜了下看到很多人推荐《高性能 MySQL 》这本书,但发现几个问题:
请问有读过的 V 友推荐一下现在学习 MySQL 该读哪本书籍,或者哪个老师的视频呢?
跪求解答!
]]>准确地说,这个问题是 MySQL 字符集中的校对规则出了 BUG ,字符集本身是无辜的。
这个 bug 现在都还在,欢迎大家验证哈。
故事是这样的。
同事在连 MySQL 库做测试时发现了一个诡异的现象:查不到匹配的数据。
相关语句简化如下(主键等字段已省略):
create table t1 ( c1 varchar(16), key idx (c1) ) collate=utf8mb4_bin; insert into t1 values ('000\n'), ('123\n'), ('abc\n'); select * from t1 where c1 like 'abc%';
这怎么看,都应该匹配出 'abc\n'
,对吧?
事实情况是:
mysql> select * from t1 where c1 like 'abc%'; Empty set (0.00 sec)
天塌了,查出来竟然是空的。
然后我拿同样的语句在 OceanBase 上跑了一下,竟然也是空。(两眼一黑)
可能会有人说,那肯定是你写的语句有问题,或者 utf8mb4_bin 就这样,吧啦吧啦。
那如果这样呢:
mysql> alter table t1 drop index idx; Query OK, 0 rows affected (0.001 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> select * from t1 where c1 like 'abc%'; +------+ | c1 | +------+ | abc | +------+ 1 row in set (0.001 sec)
哎,索引删了就好了。
总不能说,加个索引,能把结果集搞没吧。那肯定 bug 了。
那到底是咋回事呢:带上索引,我们 explain 看一下。
mysql> explain format=tree select * from t1 where c1 like 'abc%' \G *************************** 1. row *************************** EXPLAIN: -> Filter: (t1.c1 like 'abc%') (cost=0.46 rows=1) -> Covering index range scan on t1 using idx over ('abc' <= c1 <= 'abc?????????????') (cost=0.46 rows=1) 1 row in set (0.001 sec)
原来这个前置匹配,因为有索引,优化为了范围查询。后面的一串 ?
其实是 0xff
,没什么问题。
那看下 'abc\n'
和 'abc'
呢?
mysql> set collation_cOnnection=utf8mb4_bin; Query OK, 0 rows affected (0.000 sec) mysql> select 'abc\n' < 'abc'; +-----------------+ | 'abc\n' < 'abc' | +-----------------+ | 1 | +-----------------+ 1 row in set (0.000 sec)
再次两眼一黑。我倒,怎么会这样。这是什么排序规则。看下 utf8mb4_bin 吧。
mysql> show collation like 'utf8mb4_bin'; +-------------+---------+----+---------+----------+---------+---------------+ | Collation | Charset | Id | Default | Compiled | Sortlen | Pad_attribute | +-------------+---------+----+---------+----------+---------+---------------+ | utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | PAD SPACE | +-------------+---------+----+---------+----------+---------+---------------+ 1 row in set (0.001 sec)
Pad_attribute
是 PAD SPACE
,表示对齐长度时,后面补空格。这下就说通了。空格是 0x20
,换行符是 0x0a
。\n
比
小。
所以!!虽然反直觉,在 utf8mb4_bin 下,'abc\n'
就是 'abc'
小!
结论:like 'abc%'
的范围查询优化有问题。
关于这个 bug ,我已经向 MySQL 提交了 patch ,但是似乎没有得到关注。我看了下更新日志,我提的另一个 patch 已经被合入,但是这个问题依然还在。看来涉及到字符集,这个坑麻烦到他们都不想处理了。
[ 同一时间提交的代码已经合入 ]
[ 这个问题还是打开的 ]
如果哪天他们合入或者解决了,我再 append 新的进展。
]]>现在在这个公司,就感觉需求像个传话游戏:大老板说“要做数据驱动”,传到子公司 A 变成“随便给个看板”,子公司 B 直接装死,最后我们俩夹在中间,用 SQL 给领导造海市蜃楼…
建模跟尼马的侦探破案一样业务部门自己都说不清指标口径,问就是“大概和去年一样”,等开发完了才跳出来说“这数不对”(艹,你们倒是早说啊!)
要个数据像讨债一样,对方一句“系统老旧导不出来”就能打发我们.
最近领导又想起来要治理数据了,要求两个人的团队能集成全集团业务数据搞出体系化的数据建模.会上说了做不来,就要求我们参考其余制造业的经验,要我们去调研.我寻思哪家做的好的制造业能两个人搞数仓.....再说了,两个臭开发的咋能要到别人企业的内部搭建经验,头都裂开
有前辈指点下不?我现在到底该自上而下硬刚,还是自下而上偷跑?
]]>在使用 Metabase 0.53.6 连接 1Panel 自带的 MySQL 8.4.4 时,出现数据库链接失败 API Health 测试失败
Public Key Retrieval is not allowed
。 宿主机
容器类型
操作系统
面板
MySQL 版本
初步怀疑 1Panel 问题:
尝试更换数据库:
怀疑 1Panel 镜像问题:
解决方案:
坑爹啊新版本 纯纯浪费时间 我还搜了一圈没人发相关内容
]]>因为那套程序对于数据库的优化明显不足,有一些很要命的慢查询拖慢整个库。于是,我们加了 2 个索引。性能问题暂时是解决了。
但是奇葩的是,这套程序会主动检测索引,然后给 DROP 掉。频繁的掉索引,我们就写了个定时任务去加索引。于是这套程序就是在不断的 add index 和 drop index, 因为刚开始频率不是很高,所以勉强能用。
但是最近每天的频率越来越高,导致不可用时间占比越来越多。到了无法忍受的地步。
我想的解决办法:给 CDB 这个用户去除索引的权限。但是程序会报错,无法运行。
现在想求教,有没有类似的中间件,可以在不修改代码的情况下,欺骗客户端,丢弃掉 drop index 的操作而不会报错?
]]>BEGIN; select * from sys_hosts; SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID(); COMMIT;
返回如下 +-----------------+ | TRX_ID | +-----------------+ | 421114694075176 | +-----------------+ 1 row in set (0.00 sec)
BEGIN; delete from sys_hosts WHERE id < 0; SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID(); COMMIT;
返回如下 +---------+ | TRX_ID | +---------+ | 1092017 | +---------+ 1 row in set (0.00 sec)
以上,只读事务中的 事务 id 是一个非常大的值 421114694075176 实际上如果数据库有写操作,得到的真实事务 id 是一个逐渐递增的值,比如这里 1092017,这个值会用于实际数据行中的隐藏列 trx_id
那么请问大佬们
tb_quote ( op_date , code , price , ratio , hsl , index unique idx_1 (op_date,code) , index idx_2(code) )
每个交易日 6000 条,一年几百万条规模。
实现语句很简单:
方案一: 第一条语句在 1 秒内返回结果:(20 条记录)
select code from tb_quote where op_date='yyyy-mm-dd' and ratio >3 and ratio <5 and hsl>5 and hsl <10
嵌套第二条后:
select a.* from tb_quote a join ( select code from tb_quote b where op_date='yyyy-mm-d1' and ratio >3 and ratio <5 and hsl>5 and hsl <10 ) t on a.code=t.code and a.op_date='yyyy-mm-d2'
执行时间就超过 1 分钟!
** 查看 mysql 解释: **
** 方案二: ** 但是如果把第一条语句的结果保存在一个表 tmp 里面,并且 code 建立普通索引以后,变成:
select a.* from tb_quote a join ( select code from tmp ) t on a.code=t.codeand a.op_date='yyyy-mm-d3'
执行时间不超过 1 秒!!!
** 查看 mysql 解释:**
发现差别在:
不知道 mysql 是怎么优化的?如果不用中间表过渡的话,应该怎么写 sql ?
]]>我自己的 app 基本上所有的时间查询我都使用了 date 函数 QAQ
现在哭着改代码 QAQ ~
比如
select uid, balance, create_time from day_check_in where date(create_time) = date(%(入参时间)s) and uid = '123';
近期想优化接口,使用 EXPLAIN 一看头皮发麻, 虽然目前数据量不多,但是很难看。
当我翻看文档和搜索后发现 可以使用 online DDL 创建索引的时候 可以尽可能的不影响 CURD 操作,不过一些 特殊的 sql 还是会影响,会等待创建索引后才能执行一些查询。 不过这个问题不大,因为我可以避开这个时间段!
后面我执行了
ALTER TABLE balance_records ADD INDEX index_uid_create_time (uid, create_time)
发现执行了 5 分钟还是没有执行成功。 使用 SHOW PROCESSLIST; 查看 sql 的运行情况,发现没有什么 sql 和创建索引 sql 冲突, 意味着 创建索引 sql 还在执行。
我以为是数据量大的原因, 当我把这个表所有的数据拷贝到 test 库中, 我执行了 创建索引的指令,结果 2 秒就执行完了。
我想知道为啥?
目前一直没有找到问题所在, 我已经尝试了两个表,拷贝的表单都是几秒钟就创建完成了! 唯独开发环境的表没有执行成功。
目前唯一能想到的办法就是在夜深人静的时候,进行停止服务,再执行创建索引操作,也不知道行不行。
有没有大佬知道 为啥我拷贝的表执行几秒钟就执行完了。
]]>(1) 比如转账/扣款相关的业务,推荐用悲观锁还是乐观锁?
(2) 脑补了如下的乐观锁实现,可以满足并发转账和取钱需求吗?
(3) 如果能,那么和悲观锁 select for update 作对比哪个更好?
# 数据库初始状态 TRUNCATE account; INSERT INTO account (id, name, balance, version) VALUES (1, 'ichigo', 10, 1), (2, 'rukia', 10, 1); # 并发事务 1 转出全部余额 START TRANSACTION; SELECT balance, version FROM account WHERE id = 1; SELECT version FROM account WHERE id = 2; UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1; UPDATE account SET balance = balance + 10, version = 2 WHERE id = 2 AND version = 1; COMMIT; # 并发事务 2 取出全部余额 START TRANSACTION; SELECT balance, version FROM account WHERE id = 1; UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1; COMMIT;
]]>pj0001_prod
,但是同事提出了异议说在某些软件使用下划线会出现异常(问了他什么软件又不说). ]]>software_id (int) | author_id (int) | create_time | update_time |
---|---|---|---|
1 | 1 | 2024-11-05 11:00:00 | 2024-11-05 11:00:00 |
2 | 2 | 2024-11-05 11:00:00 | 2024-11-05 11:00:00 |
其中 software_id 是主键,author_id 是普通索引
还有一张 company_software 表,表结构如下
company_software_id (int) | author_id (int) | country(varchar) | software_id (varchar) |
---|---|---|---|
1 | 1 | china | 1 |
2 | 1 | china | kkk |
其中 company_software_id 是主键,author_id 、country 、software_id 是一个复合索引。
以上表数据量只有 1~5w 。
1 、SQL1
SELECT 1 FROM company_software AS company_software WHERE company_software.author_id = 1 and company_software.country in ('china', 'korea', 'england') and company_software.software_id = '1'
并不会导致慢查询
2 、SQL2
SELECT software_id FROM sortware WHERE author_id = 1 and NOT EXISTS (SELECT 1 FROM company_software AS company_software WHERE company_software.author_id = 1 and company_software.country in ('china', 'korea', 'england') and CONVERT(sortware.software_id , char) = company_software.software_id) LIMIT 0, 100
为什么会导致慢查询,懂的大佬帮忙分析下
]]>现在 C 盘坏了, 我换了 1 块 C 盘后, 新装了 1 个同样版本的 mysql, 同时指定数据文件夹为原来的在 D 盘上的 Mysql 数据文件夹.
现在问题是 mysqlworkbench 链接 mysql 后死活不显示 D 盘上的数据库, 请大佬支招. 谢谢
谢谢
]]>在后端程序中少不了要和 sql 接触,一部分人对子查询很抗拒认为 把很多逻辑都隐藏到 sql 中了不利于程序的可读性,然而 你同意吗?
上述 sql 使用了很多子查询,查询效率也并不会低,不过肯定可以省很多代码,因为很多数据如果不用子查询,那么就要多次链接数据库去做查询,然后组合数据。
愚见:适当的使用子查询其实是会更高效的!