自我介绍+项目介绍
目录
2.在 MySQL 中,如果您有一个 (a, b, c) 的联合索引,查询条件仅包含 a 和 c 而没有 b索引的生效情况?
13.Integer a = 321; Integer b = 321; 用==比较会返回什么?
16.订单状态值很多,流转时用if else不太方便,有什么解决方案?
索引是数据库表中一列或多列的值存储的数据结构,通常是 B+ 树(B-Tree 的变种),它允许快速查找表中的行。
应该尽量避免违反最左前缀法则,已经其他使索引失效的场景,用慢查询日志以及explain可以分析慢sql,作出进一步优化。
(a, b, c)
的联合索引,查询条件仅包含 a
和 c
而没有 b索引的生效情况?索引最左前缀规则:MySQL 联合索引遵循最左前缀规则,这意味着查询时必须包含索引最左边的列才能使用索引。所以,如果您的查询包含了 a
,索引就可以被使用。
索引选择性:如果 a
的选择性很高(不同的值很多),即使没有包含 b
,查询也可能因为 a
的选择性而受益。
1. 共享锁(Shared Locks)
共享锁,也称为读锁,允许多个事务同时读取同一资源,但不能修改它。当一个事务对数据加上共享锁后,其他事务可以继续加共享锁进行读取,但不能加排它锁进行写入。
2. 排它锁(Exclusive Locks)
排它锁,也称为写锁,允许事务修改数据。当一个事务对数据加上排它锁后,其他事务不能对该数据加任何类型的锁。
3. 行级锁(Row-level Locks)
MySQL 支持行级锁定,这是最细粒度的锁,只锁定相关的行记录。InnoDB 存储引擎使用行级锁来实现事务的隔离性。
4. 表级锁(Table-level Locks)
表级锁锁定整个表,MyISAM 和其他一些存储引擎使用表级锁。由于锁定了整个表,所以并发性能不如行级锁。
5. 间隙锁(Gap Locks)
间隙锁是一种行级锁,它锁定一个范围内的间隙,但不包括记录本身。间隙锁用于防止其他事务插入间隙中的新记录,以维护事务的可重复读性。
6. 临键锁(Next-Key Locks)
临键锁是 InnoDB 存储引擎中的一种锁,它结合了行锁和间隙锁。临键锁锁定一个记录以及记录前面的间隙。
7. 意向锁(Intention Locks)
意向锁是一种表明事务想要在更细粒度上加锁的锁。它们用于在锁定层次结构中向上升级,从行级到表级。意向锁分为意向共享锁(Intention Shared Lock)和意向排它锁(Intention Exclusive Lock)。
8. 自增锁(AUTO-INC Locks)
在 InnoDB 中,自增锁用于管理表的自增字段。当插入新记录时,InnoDB 会请求一个自增锁,以确保自增字段的值是唯一的。
9. 全局锁(Global Locks)
全局锁是 MySQL 中最高层次的锁,它锁定整个数据库系统。这种锁通常用于复制和恢复操作。
10. 元数据锁(Metadata Locks)
元数据锁用于控制对数据库结构的更改,如添加或删除表。
11. 死锁(Deadlocks)
死锁发生在两个或多个事务相互等待对方持有的锁,导致无法继续执行。
假设你有一个表my_table
,其中包含字段id
(假设是主键)和column1
,你想删除column1
字段的重复数据,保留每个重复组的第一个记录。
- DELETE FROM my_table
- WHERE id NOT IN (
- SELECT MIN(id)
- FROM my_table
- GROUP BY column1
- );
这个查询首先找出每个column1
值的最小id
,然后删除不在这些最小id
列表中的所有记录。
使用LEFT JOIN来保留重复数据中的第一条记录。
- DELETE my_table
- FROM my_table
- LEFT JOIN (
- SELECT MIN(id) as min_id
- FROM my_table
- GROUP BY column1
- ) AS subquery
- ON my_table.id = subquery.min_id
- WHERE my_table.id > subquery.min_id;
这个查询将my_table
与一个子查询进行左连接,子查询返回每个column1
值的最小id
。然后,它删除连接后my_table
中id
大于最小id
的所有记录。
如果你使用的数据库支持窗口函数(如SQL Server、PostgreSQL、MySQL 8.0+等),可以使用ROW_NUMBER()
来为每个重复组分配一个序号,然后删除序号大于1的记录。
- DELETE FROM my_table
- WHERE row_num > 1;y.min_id;
首先,你需要创建一个临时表或使用WITH语句来确定每个记录的序号:
- WITH RankedRecords AS (
- SELECT *, ROW_NUMBER() OVER (PARTITION BY column1 ORDER BY id) AS row_num
- FROM my_table
- )
- DELETE FROM RankedRecords
- WHERE row_num > 1;
在这个例子中,ROW_NUMBER()
为每个column1
值的分组分配一个序号,序号从1开始,并且按id
排序。然后,删除序号大于1的所有记录。
HashMap
是 Java 中实现的一个基于哈希表的 Map
接口,它存储键值对(key-value pairs)。在 Java 中,有几种不同的方式可以遍历 HashMap
:
1. 使用 keySet()
方法
这是最常用的遍历 HashMap
的方法。首先获取所有键的集合,然后遍历这个集合,并使用每个键来获取对应的值。
- HashMap
map = new HashMap<>(); - // 假设map已经初始化并填充了数据
-
- for (KeyType key : map.keySet()) {
- ValueType value = map.get(key);
- // 处理键和值
- }
2. 使用 entrySet()
方法
entrySet()
方法返回的是一个 Set
集合,集合中的元素是 Map.Entry
对象。每个 Map.Entry
对象都包含一个键和一个值。
- for (Map.Entry
entry : map.entrySet()) { - KeyType key = entry.getKey();
- ValueType value = entry.getValue();
- // 处理键和值
- }
3. 使用 Java 8 的 forEach()
方法
从 Java 8 开始,Map
接口提供了 forEach()
方法,它接受一个 BiConsumer
函数式接口作为参数,可以对每个键值对执行操作。
- map.forEach((key, value) -> {
- // 处理键和值
- });
4. 使用 values()
方法
values()
方法返回的是一个 Collection
,包含 HashMap
中所有的值。通常,这种方法不推荐使用,因为如果你需要同时访问键和值,它就不如 keySet()
或 entrySet()
方法高效。
- Collection
values = map.values(); - for (ValueType value : values) {
- // 仅处理值,如果需要键,需要再次查询
- }
5. 使用 Java 8 的 Stream
API
从 Java 8 开始,可以使用 stream()
方法来获取 HashMap
的流,并进行更复杂的操作。
- map.entrySet().stream()
- .forEach(entry -> {
- KeyType key = entry.getKey();
- ValueType value = entry.getValue();
- // 处理键和值
- });
6. 使用迭代器(Iterator)
可以使用 iterator()
方法获取迭代器,然后使用迭代器遍历 HashMap
。
- Iterator
> iterator = map.entrySet().iterator(); - while (iterator.hasNext()) {
- Map.Entry
entry = iterator.next(); - KeyType key = entry.getKey();
- ValueType value = entry.getValue();
- // 处理键和值
- }
每种遍历方式都有其适用场景,选择哪一种取决于你的具体需求。例如,如果你需要修改 HashMap
,那么使用迭代器可能是更好的选择。如果你只是需要读取数据,那么 keySet()
、entrySet()
或 forEach()
方法可能更简单、更直观。
在 MySQL 中,NULL
不是一个数据类型,而是一个特殊的标记,用来表示字段中没有数据。在 SQL 中,NULL
用于表示未知或缺失的数据值。
每个数据类型都可以存储 NULL
值,例如:
INT
:整数类型,可以存储 NULL
值。VARCHAR
:可变长度的字符串类型,可以存储 NULL
值。DATE
:日期类型,可以存储 NULL
值。FLOAT
:浮点数类型,可以存储 NULL
值。NULL
值在 SQL 中的处理与其他值不同,例如:
NULL
与任何值(包括另一个 NULL
)的比较总是产生 NULL
结果,这意味着比较是不确定的。COUNT()
函数时,默认情况下 NULL
值不会被计入总数。ORDER BY
子句时,NULL
值通常会被排序在结果集的开始或结束位置,这取决于具体的 SQL 配置和版本。因此,设计数据库时,需要根据数据模型和业务需求来决定哪些字段可以存储 NULL
值,以及如何处理这些 NULL
值。
垃圾回收(Garbage Collection, GC)通常在以下情况下触发:
内存分配请求时:
内存使用达到阈值:
定时触发:
系统空闲时:
显式调用:
内存碎片整理:
新生代到老年代的晋升:
堆空间不足:
外部触发:
特定事件:
1. 继承 Thread
类
2. 实现 Runnable
接口
3. 使用 Callable
和 Future
4. 使用 Executors
框架
在 Redis 中,单个命令的执行是原子性的。这意味着当一个命令被执行时,它要么完全执行,要么完全不执行,不会出现执行到一半的情况。Redis 通过单线程来处理命令,确保了命令执行的原子性。
然而,如果一个操作涉及多个命令,Redis 默认情况下并不保证这些命令作为一个整体的原子性。为了实现多个命令的原子性,Redis 提供了以下两种机制:
MULTI
、EXEC
、WATCH
和 DISCARD
命令,Redis 允许客户端将多个命令打包在一起执行,要么全部成功,要么全部失败。Redis 默认运行在单线程模式下,这意味着在任何给定时间点,只有一个命令被执行。因此,Redis 自然地提供了一定程度的隔离性,因为不会有多个命令同时干扰彼此的执行。
但是,Redis 的隔离性与传统数据库的隔离级别(如读已提交、可重复读、可串行化)不同。在 Redis 中:
WATCH
命令监视键值的变化,以及使用事务来保证操作的一致性。Redis 还提供了持久性选项,通过 RDB 快照和 AOF 日志记录,确保数据在系统故障后能够恢复。
Redis 的原子性和隔离性与传统的关系型数据库不同,它通过单线程模型和事务机制来保证操作的原子性,并通过单线程执行来自然地提供隔离性。然而,Redis 不提供传统数据库中的严格隔离级别,所以在设计系统时需要考虑到这一点,并根据需要使用 Redis 提供的机制来保证数据的一致性和完整性。
字符串(Strings):
列表(Lists):
集合(Sets):
有序集合(Sorted Sets):
哈希(Hashes):
noeviction:这是默认的淘汰策略。当内存达到限制时,Redis 将拒绝所有会修改数据集的命令(例如 SET、LPUSH 等),但不会自动删除任何键。
allkeys-lru:在所有键中,根据最近最少使用(Least Recently Used)算法淘汰数据。
volatile-lru:在设置了过期时间的键中,根据 LRU 算法淘汰数据。
allkeys-random:在所有键中随机选择一个键进行淘汰。
volatile-random:在设置了过期时间的键中随机选择一个键进行淘汰。
volatile-ttl:在设置了过期时间的键中,选择即将过期的键进行淘汰。
@SpringBootApplication:
@Configuration
、@EnableAutoConfiguration
和 @ComponentScan
。@RestController:
@Controller
和 @ResponseBody
。@RequestMapping:
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping:
@RequestMapping
,分别用于处理 GET、POST、PUT 和 DELETE 请求。@Autowired:
@Service:
@Repository:
@Component:
@Configuration:
@Bean:
@Value:
@PropertySource:
@EnableAutoConfiguration:
@ComponentScan:
@RestControllerAdvice:
@PathVariable:
@RequestParam:
@RequestBody:
@ResponseBody:
@Profile:
@Async:
@Scheduled:
在 Java 中,Integer
类型的对象进行 ==
比较时,比较的是它们在内存中的引用地址,而不是它们所封装的值。如果两个 Integer
对象是通过自动装箱(autoboxing)创建的,它们通常指向内存中的同一个缓存对象。Java 为每个整数值 -128 到 127 缓存了一个 Integer
对象,所以当你创建一个这个范围内的 Integer
对象时,实际上是从一个缓存中取得的。超过范围会返回false。
分库键(Shard Key)
业务无关性:选择与业务逻辑无关的字段作为分库键,以避免数据倾斜。
数据均匀性:选择能够使数据均匀分布的字段,例如用户ID、订单ID等。
查询模式:根据查询模式选择分库键,使得查询操作能够快速定位到特定的库。
范围分区:如果数据具有时间属性,可以选择时间字段作为分库键,实现时间范围分区。
分表键(Partition Key)
数据量预估:根据数据增长趋势和表的大小预估,选择合适的字段进行分表。
访问模式:根据数据的访问模式,选择能够提高查询效率的字段作为分表键。
数据关联性:如果表之间存在关联关系,可以选择关联字段作为分表键,以保持数据的一致性。
复合分区:可以使用多个字段的组合作为分表键,实现复合分区策略。
常见分库分表策略
哈希分区:使用哈希函数对分库/分表键进行哈希计算,根据哈希值将数据分配到不同的库或表。
范围分区:根据分库/分表键的数值范围进行分区,例如按年份、按ID范围等。
列表分区:将分库/分表键的值映射到一个预定义的列表中,根据列表中的顺序分配数据。
一致性哈希:使用一致性哈希算法分配数据,可以在增减节点时减少数据迁移。
AOP 的核心概念包括:
切面(Aspect):切面是一组横切关注点的模块化表示,它包括通知(Advice)、切点(Pointcut)、目标对象(Target Object)、代理(Proxy)等。
通知(Advice):通知是切面的一部分,它定义了何时以及如何增强目标对象的方法执行。通知可以在方法的执行前后、方法抛出异常时或在方法正常执行完成后执行。
切点(Pointcut):切点定义了一组特定的连接点(Join Point),这些连接点是程序执行过程中的特定位置,如方法的调用或异常的处理。
连接点(Join Point):连接点是程序执行过程中可以插入切面的特定位置,通常是方法的调用或处理程序的执行。
目标对象(Target Object):目标对象是被增强的对象,通常是应用程序中的业务逻辑类。
代理(Proxy):代理是目标对象的一个替代品,它在不修改目标对象的情况下,通过拦截方法调用来实现增强。
策略模式(Strategy Pattern):
工厂模式(Factory Pattern):