V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  asmile1993  ›  全部回复第 1 页 / 共 1 页
回复总数  19
原来的操作逻辑有问题,你第一步是查询用户积分总额,然后更新积分总额,最后再将积分余额插入到积分记录表中,第一步只读查询并没有加锁,因此是可以并发的,这可能导致并发的线程读取到的余额是不一致的,又因为你积分总表的更新逻辑是正确的,这会造成积分记录表中记录的积分余额错乱,而积分总表中的余额又是正确的。

-- 步骤一:先变更,这样会锁住 `uid`='22' 这条记录,别的会话只能查询,不能变更
UPDATE `api_credits` SET `credits1`=`credits1`-'100' WHERE `uid`='22' AND `credits1`>='100'
-- 步骤二:拿到变更后的最新的积分余额
SELECT * FROM `api_credits` WHERE `uid`='22' LIMIT 1
-- 将步骤二中获取到的积分余额插入到积分记录表中
INSERT INTO `api_credits_log` SET `uid`='22', `cid`='3', `credits`='100', `balance`='79900', `time`='1701001020'

将这三步放入到同一个事务中
从哪本书截的图?看这文字描述,我觉得这本书质量不是很高。

1) 假设 Q2 能利用上索引 (a, b),那么由于 a 是范围查询,根据高性能 MySQL 中的知识,可以知道“如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找”, 这里说的无法使用索引优化查找,是指“不能用索引快速定位”,但"快速过滤"是可以做的。

2) 由 1 可知,索引(a, b) 中,只有列 a 能用作索引快速定位,而列 b 不能用来快速定位,但 MySQL 有索引下推优化,因此对于列 b 来说,可以利用 ICP 来达到在索引遍历中快速过滤,从而减少回表次数。

有点没看懂你这个例子的回答,一直说索引有序是什么意思,虽然有关系,但有点答不对题,让人疑惑。
我估计楼主在 create_time 字段上建立了单列索引,查询具有 ORDER BY 或 GROUP BY 和 LIMIT 子句的情况下,控制优化器是否尝试使用有序索引而不是无序索引、文件排序或其他优化。 每当优化器确定使用它可以更快地执行查询时,默认情况下都会执行此优化。
因为这种算法不能处理所有可能的情况(部分原因是假设数据的分布总大体是均匀的),所以在某些情况下,这种优化可能是不可取的。 在 MySQL 5.7.33 之前,无法禁用此优化,但在 MySQL 5.7.33 及更高版本中,虽然它仍然是默认行为,但可以通过将 prefer_ordering_index 标志设置为 off 来禁用它。可以参考下文档 https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

解决方案 1:禁用 prefer_ordering_index 优化(不推荐),可能导致 pt 工具或其他根据 order by 的查询耗时过长(无法利用最优索引)
解决方案 2:order by create_time, 其他字段(推荐),加上除主键外的其他列,来达到禁用 create_time 上索引的效果
解决方案 3:使用 ignore index idx_name 来禁用掉 create_time 上的索引
解决方案 4:使用联合索引(推荐)
2022-11-17 19:33:40 +08:00
回复了 qiyong 创建的主题 程序员 面试中遇到的一道 sql 题
with goods_sales as(
-- 获取每种商品的总销售额
select distinct
g.id as goods_id,
g.name as goods_name,
g.group_id,
gg.name as group_name,
sum(gsr.sales_volume) over(partition by g.id, g.name, g.group_id, gg.name) sum_goods_sales_volume,
sum(gsr.sales_volume) over(partition by g.group_id, gg.name) sum_group_sales_volume
from goods g
inner join goods_group gg on g.group_id= gg.id
inner join goods_sales_record gsr on g.id= gsr.goods_id
order by sum_goods_sales_volume desc -- 以商品的总销售倒序排列,并取前三名
limit 3
)
select goods_id,
goods_name,
group_id,
group_name,
sum_goods_sales_volume, -- 每种商品的总销售额
sum_group_sales_volume -- 每种分类的总销售额
from goods_sales
order by sum_group_sales_volume desc, sum_goods_sales_volume desc -- 以每种分类的总销售额、商品的总销售倒序排列
2022-10-24 17:20:08 +08:00
回复了 NoKey 创建的主题 程序员 统计一下,今天多少公司发橙子🤣
羡慕,我只有一句祝福
2022-10-09 16:55:38 +08:00
回复了 OeO 创建的主题 MySQL 请教 sql 数据表设计的问题
可以考虑用 json 数据类型来存储这种数据,比如 json 数组,数组中每个对象是一个 json 对象来存储车辆信息
2022-09-20 14:03:06 +08:00
回复了 coderstory 创建的主题 MySQL 关于 MYSQL8 时间类型字段的一些疑问
@coderstory 我的版本是 8.0.26 ,加索引操作没问题
2022-09-20 11:42:37 +08:00
回复了 coderstory 创建的主题 MySQL 关于 MYSQL8 时间类型字段的一些疑问
Q1: mysql 的那两个默认值为什么会变
CURRENT_TIMESTAMP and CURRENT_TIMESTAMP() are synonyms for NOW().
CURRENT_DATE and CURRENT_DATE() are synonyms for CURDATE().

Q2: 为什么加索引提示错误默认值
执行成功了,你是不是分别有 Table2 和 table2 这两张表?看看自己的 lower_case_table_names 参数是否为 0
2022-07-27 17:31:52 +08:00
回复了 sunmoon1983 创建的主题 MySQL 求大神解答,这种 sql 应该怎么写?
-- 创建张维表,结构如下, 划分好每个 code 的年龄范围
-- 也可以不创建,用子查询创建个临时的结果集
create table code_age(
code varchar(20),
age_start varchar(20),
age_stop varchar(20)
)

-- 再根据 code 和原表 table1 关联,并根据 age 来筛选
select a.*
from table1 a inner join
code_age b
where a.code = b.code
and a.age > b.age_start
and a.age < b.age_stop
2022-05-27 11:21:26 +08:00
回复了 zzzain46 创建的主题 MySQL 如何高效地取连续三个月同一时间有交易的商户
@thinkingbullet MySQL 版本得是 8.0 的
2022-05-23 10:39:20 +08:00
回复了 mrjnamei 创建的主题 MySQL 这个 SQL 有优化空间吗
c 、d 表数据量不大的情况下用 1 楼的方法没有问题,但要执行两次,再 union 起来,感觉代价还是大了点。用 exists 去判断好了(在 1 对多的情况下也可以避免数据重复)

select a.c_id
from a inner join
b on a.c_id = b.c_id
where exists (
select 1
from c
where c.c_id = a._cid
)
or exists (
select 1
from d
where d.c_id = a._cid
)
2022-05-23 10:33:18 +08:00
回复了 mrjnamei 创建的主题 MySQL 这个 SQL 有优化空间吗
执行计划呢?
2022-05-20 15:19:17 +08:00
回复了 zzzain46 创建的主题 MySQL 如何高效地取连续三个月同一时间有交易的商户
@lookStupiToForce 为什么不可以,1 个 cust_id 又没限定一天只能交易一次,一天交易多次,你 lag order by 取的就不是下个月了吧,而是下一次交易的值
2022-05-20 13:45:26 +08:00
回复了 zzzain46 创建的主题 MySQL 如何高效地取连续三个月同一时间有交易的商户
@lookStupiToForce
一天进行多次交易或一个月多次交易还能生效吗? lag/lead 只是取前一条记录和后一条记录的值而已,并不能确保取得是上一个月和下一个月
2022-05-20 11:27:16 +08:00
回复了 zzzain46 创建的主题 MySQL 如何高效地取连续三个月同一时间有交易的商户
-- 测试数据
drop table t;
create table t(
id int auto_increment primary key,
cust_id int,
trx_dte date,
trx_amt decimal(10, 2)
);

insert into t (cust_id, trx_dte, trx_amt) value(10086, '2022-01-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10086, '2022-02-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10086, '2022-03-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10086, '2022-04-05', 23.56);

insert into t (cust_id, trx_dte, trx_amt) value(10085, '2022-01-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10085, '2022-02-05', 23.56);

insert into t (cust_id, trx_dte, trx_amt) value(10010, '2021-01-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10010, '2021-02-05', 13.56);
insert into t (cust_id, trx_dte, trx_amt) value(10010, '2021-03-05', 33.56);
insert into t (cust_id, trx_dte, trx_amt) value(10010, '2021-04-05', 53.56);
insert into t (cust_id, trx_dte, trx_amt) value(10010, '2021-05-05', 23.56);

insert into t (cust_id, trx_dte, trx_amt) value(10020, '2021-01-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10020, '2021-02-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10020, '2021-04-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10020, '2021-05-05', 23.56);
insert into t (cust_id, trx_dte, trx_amt) value(10020, '2021-07-05', 23.56);



-- 递归调用生成 '2021-05-01' 到 '2022-05-30' 的日期表
with recursive Date_Ranges AS
(
select '2021-05-01' as Date
union all
select Date + interval 1 day
from Date_Ranges
where Date < '2022-05-30'
),
-- 生成月份相连,日期相同并且连续三个月的日期数组,例如:["2021-01-05", "2021-02-05", "2021-03-05"]
-- 注意这里限制了日期,因为过了 '2022-03-30' 后,就不再满足数据的过滤条件
date_array_ranges as (
select JSON_ARRAY(date, date_add(date, interval 1 month), date_add(date, interval 2 month)) json_array_trx_dte
from date_ranges
where date <= '2022-03-30'
),
-- 根据 cust_id ,trx_amt 进行聚合,并生成用户相同,金额相同的交易日期 json 数组
cust_trx_amt_dte_array as
(
select cust_id, trx_amt, JSON_ARRAYAGG(trx_dte) json_array_trx_dte
from t
group by cust_id, trx_amt
)
-- 由于是连续三个月,那么交易日期的 json 数组的数量肯定是大于等于 3
-- 在满足上述条件后,进一步判断交易日期 json 数组是否包含连续三个月日期相同的元素
-- 这里用 exists 来判断是为了避免数据重复
select *
from cust_trx_amt_dte_array a
where json_length(a.json_array_trx_dte) >= 3
and exists (select 1
from date_array_ranges b
where json_contains(a.json_array_trx_dte->'$', b.json_array_trx_dte)
);

-- 返回结果
+-----------+---------+----------------------------------------------------------+
| cust_id | trx_amt | json_array_trx_dte |
+-----------+---------+----------------------------------------------------------+
| 10086 | 23.56 | ["2022-01-05", "2022-02-05", "2022-03-05", "2022-04-05"] |
+---------------------+----------------------------------------------------------+
2022-05-07 16:00:11 +08:00
回复了 irisdev 创建的主题 程序员 mysql 怎么迁移数据库到另一台机器
mysqlddump -u root -p -all-databases > /f/beifen.sql ,这种备份方式获取到的是非一致性备份,不可用的,恢复之后,数据很可能对不上,要加上--master-data=2 和 --single-transaction 这两种方式。

mysqldump --master-data=2 --single-transaction -A >db_full_backup_`date +%Y%m%d`.sql
2022-04-27 16:21:18 +08:00
回复了 oneisall8955 创建的主题 MySQL MySQL explain key_len 和联合索引问题
情况 1 和情况 2 都可以用到索引中的 c 字段 — 使用 use index condition 。问题的关键在于有能力使用到,但不是一定会用到,use index condition 是一种优化的方法,在大部分情况下对性能提升没有那么明显。在你的查询语句中,你查询条件中的列和查询的列都是列 a ,b ,c ,那么直接走你建立的索引就好了。如果你对表多添加几个列,比如 d ,e ,f ,索引不变,查询条件保持不变,直接 select * 去查询数据,会发现用不上了 c ,因为直接无法使用索引就返回数据,还需要回表查询一次。


#### MySQL 版本
8.0.26


#### 表结构
CREATE TABLE `foo` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`a` varchar(7) NOT NULL DEFAULT '',
`b` int NOT NULL DEFAULT '1',
`c` varchar(3) NOT NULL DEFAULT '',
`d` int DEFAULT NULL,
`e` int DEFAULT NULL,
`f` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_a_b_c` (`a`,`b`,`c`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ;



CREATE TABLE `bar` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`a` varchar(7) NOT NULL DEFAULT '',
`b` varchar(10) NOT NULL DEFAULT '',
`c` varchar(3) NOT NULL DEFAULT '',
`d` int DEFAULT NULL,
`e` int DEFAULT NULL,
`f` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_a_b_c` (`a`,`b`,`c`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

#### 执行语句及执行计划
其中 used_key_parts 代表使用到索引中的哪几个列

root@localhost [zst]>EXPLAIN format=json SELECT * FROM bar where a = 'a1' and b like 'x%' and c = 'cc'\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "0.71"
},
"table": {
"table_name": "bar",
"access_type": "range",
"possible_keys": [
"idx_a_b_c"
],
"key": "idx_a_b_c",
"used_key_parts": [
"a",
"b"
],
"key_length": "66",
"rows_examined_per_scan": 1,
"rows_produced_per_join": 1,
"filtered": "100.00",
"index_condition": "((`zst`.`bar`.`a` = 'a1') and (`zst`.`bar`.`b` like 'x%') and (`zst`.`bar`.`c` = 'cc'))",
"cost_info": {
"read_cost": "0.61",
"eval_cost": "0.10",
"prefix_cost": "0.71",
"data_read_per_join": "88"
},
"used_columns": [
"id",
"a",
"b",
"c",
"d",
"e",
"f"
]
}
}
}

root@localhost [zst]>EXPLAIN format=json SELECT a, b, c FROM bar where a = 'a1' and b like 'x%' and c = 'cc'\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "0.35"
},
"table": {
"table_name": "bar",
"access_type": "index",
"possible_keys": [
"idx_a_b_c"
],
"key": "idx_a_b_c",
"used_key_parts": [
"a",
"b",
"c"
],
"key_length": "66",
"rows_examined_per_scan": 1,
"rows_produced_per_join": 1,
"filtered": "100.00",
"using_index": true,
"cost_info": {
"read_cost": "0.25",
"eval_cost": "0.10",
"prefix_cost": "0.35",
"data_read_per_join": "88"
},
"used_columns": [
"a",
"b",
"c"
],
"attached_condition": "((`zst`.`bar`.`a` = 'a1') and (`zst`.`bar`.`b` like 'x%') and (`zst`.`bar`.`c` = 'cc'))"
}
}
}
mysql 是向后兼容的,可以试试用 mysql 5 连接到 mysql 8 ,而不是反过来。
2022-04-08 17:19:45 +08:00
回复了 proletarius 创建的主题 MySQL 请教一个关于 mysql 优化的问题
你不贴表结构、执行计划,没人会去看的,只是浪费时间而已
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4996 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 43ms · UTC 05:40 · PVG 13:40 · LAX 22:40 · JFK 01:40
Developed with CodeLauncher
♥ Do have faith in what you're doing.