Q&A
# mysql
# MyIsam 和 Innodb 对比
myisam:不支持事务,表锁,不支持外键,查询行数直接返回,索引即数据 innodb:支持事务,行锁,mvcc,遍历表返回行数,索引和数据分开
# 底层数据类型
hash b+tree
# acid
原子性:成功 or 失败 一致性:满足约束(主键、外键、唯一约束) 隔离性:事务之间的操作相互隔离,互不影响 持久性:持久化
redo log 保证:原子性和持久性 lock 保证:隔离性 undo log 保证:一致性
# 隔离级别
读未提交:读到一个事务未提交的数据 读已提交:事务提交的数据才会被读取到,多次读取结果可能不一样(当前读) 可重复读:多次读取结果一样(事务开始时的快照),MVCC 解决幻读 串行化:单线程执行
脏读:读到未提交的数据 幻读:返回的数据行数不一致 不可重复读:返回的行数一致,结果不一致
# mvcc
record lock:行锁 gap lock:间隙锁,不包含当前行 next key lock:record lock + gap lock
# 死锁
事务超时默认 50s,默认不回滚事务(什么都没做)
死锁检测,wait-for-graph 等待图,kill 事务最小的会话。
# 优化
buffer_pool_size 默认 128m,适当调大为机器内存的 50%~60%,剩余内存给其他应用及 mysql 的其他数据结构使用 建表时字段值尽量小,一页容纳更多的数据
代码优化:
- 利用索引,防止全表扫描
- 查询时只查所需要的字段,不要 select *,利用索引,减少回表
- 数据各种聚合,在代码层面聚合,数据库资源宝贵,业务机器可以水平扩展
- 批量写
- 数据量太大,分区表
- 写多读少,分库分表
- 读多写少,读写分离,数据缓存
# 存储过程
好处:
- 减少服务交互次数,一次返回所需结果
- 简化操作,数据库层面进行处理
坏处:
- 不易维护,需求变更,存储过程修改
- 调试和测试困难
- 可移植性差
# 分区表
好处:
- 提高查询性能
- 管理数据更容易
- 提高并发性能 坏处:
- 额外管理和维护
- 选择合适的分区键和分区策略,否则可能性能下降
- 需要合理的设置分区数量,单表最多 1024 分区
- null 值会使分区过滤失效
# 索引下推
5.6 开始支持索引下推,之前是 server 层进行结果过滤。 5.6 开始由 engine 来进行结果过滤,不满足最左匹配原则,查询条件包含索引列,直接在取出索引时进行过滤
# redis
为什么 redis 快
- 内存
- 单线程,无锁
- reactor 模型
- 多个 socket 连接到 io 多路复用器(epoll),io 多路复用将事件丢给队列,队列消费者即文件事件处理器(客户端连接,读写请求)
# 数据类型
string:sds list:linkedlist,ziplist hash:dict,ziplist set:intset,dict zset:ziplist,skiplist
# 使用场景
string:分布式锁,token,计数器 list:红包,默认好友 hash:购物车 set:抽奖 spop,ip 黑名单 sismember zset:排行榜
# 分布式锁
Distributed Locks with Redis | Redis (opens new window) set 进行加锁,lua 进行锁释放
redisson 看门狗,默认加锁 30s,1/3 时长后自动续期。 set key 的 value 是 uuid + threadId,保证是当前线程释放锁
SET resource_name my_random_value NX PX 30000
仅当密钥尚不存在时,该命令才会设置密钥(NX
选项),过期时间为 30000 毫秒(PX
选项)。该键设置为值“my_random_value”。该值在所有客户端和所有锁定请求中必须是唯一的。
基本上,使用随机值是为了以安全的方式释放锁,并使用一个脚本告诉 Redis:仅当密钥存在且存储在密钥中的值正是我期望的值时才删除该密钥。这是通过以下 Lua 脚本完成的:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
这对于避免删除另一个客户端创建的锁非常重要。例如,客户端可能获取锁,在执行某些操作时被阻止,时间长于锁有效期(密钥过期的时间),然后删除已被其他客户端获取的锁。使用 justDEL
(opens new window)并不安全,因为客户端可能会删除另一个客户端的锁。使用上面的脚本,每个锁都用随机字符串“签名”,因此只有当它仍然是客户端尝试删除它时设置的锁时,锁才会被删除。
这个随机字符串应该是什么?我们假设它是 20 个字节/dev/urandom
,但您可以找到更便宜的方法来使其对于您的任务来说足够独特。例如,一个安全的选择是用 播种 RC4 /dev/urandom
,并从中生成伪随机流。一个更简单的解决方案是使用微秒精度的 UNIX 时间戳,将时间戳与客户端 ID 连接起来。它不太安全,但对于大多数环境来说可能足够了。
“锁有效期”是我们用作密钥生存时间的时间。它既是自动释放时间,也是客户端在另一个客户端能够再次获取锁之前执行所需操作的时间,而不会在技术上违反互斥保证,互斥保证仅限于给定的窗口从获取锁的那一刻起的时间。
现在我们有了一个获取和释放锁的好方法。使用此系统,推理由单个始终可用的实例组成的非分布式系统是安全的。让我们将这个概念扩展到没有这样保证的分布式系统。
# 持久化机制及文件
# rdb
二进制文件
900 1
300 10
60 10000
文件结构
REDIS
db_version
databases
EOF
check_sun
# aof
redis 协议格式的命令,客户端的命令会写入到 aof_buf 即 aof 缓冲区,根据配置文件进行数据持久化
appendfsync always
appendfsync eversec
appendfsync no
文件结构
select 0
set k1 v1
appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$2
k1
$2
v1
rewirteaof
- fork 子进程进行文件写入
- 父线程将此时收到的数据进入 aof 缓冲期 和 aof 重写缓冲期
- 子进程完成通知父进程
- 父进程将 aof 重写缓冲期的数据追加到文件
- 原子的替换 aof 文件
# 混合持久化
4.0 才开始支持 rdb + aof 混合持久化,默认开启
aof-use-rdb-preamble yes
在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾。
AOF 格式的开头是 *,而 RDB 格式的开头是 REDIS。
# mq
rabbitmq和kafka对比 如何选择
# amqp 和 mqtt 对比
amqp 术语:
- 连接(Connection):一个网络连接,比如TCP/IP套接字连接。
- 会话(Session):端点之间的命名对话。在一个会话上下文中,保证“恰好传递一次”。
- 信道(Channel):多路复用连接中的一条独立的双向数据流通道。为会话提供物理传输介质。
- 客户端(Client):AMQP连接或者会话的发起者。AMQP是非对称的,客户端生产和消费消息,服务器存储和路由这些消息。
- 服务器(Server):接受客户端连接,实现AMQP消息队列和路由功能的进程。也称为“消息代理”。
- 消息头(Header):描述消息数据属性的一种特殊段。
- 消息体(Body):包含应用程序数据的一种特殊段。消息体段对于服务器来说完全透明——服务器不能查看或者修改消息体。
- 消息内容(Content):包含在消息体段中的的消息数据。
- 交换器(Exchange):服务器中的实体,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
- 交换器类型(Exchange Type):基于不同路由语义的交换器类。
- 消息队列(Message Queue):一个命名实体,用来保存消息直到发送给消费者。
- 绑定器(Binding):消息队列和交换器之间的关联。
- 绑定器关键字(Binding Key):绑定的名称。一些交换器类型可能使用这个名称作为定义绑定器路由行为的模式。
- 路由关键字(Routing Key):一个消息头,交换器可以用这个消息头决定如何路由某条消息。
- 消费者(Consumer):一个从消息队列中请求消息的客户端应用程序。
- 生产者(Producer):一个向交换器发布消息的客户端应用程序。
- 虚拟主机(Virtual Host):一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。客户端应用程序在登录到服务器之后,可以选择一个虚拟主机。
- 主题(Topic):通常指发布消息;AMQP规范用一种或多种交换器来实现主题。
mqtt 术语:
- Broker:有时我们也会直接将服务端称为 Broker,这两个术语可以互换使用。
- Client:使用 MQTT 协议连接到服务端的设备或应用程序,并通过服务端完成发布订阅。
- Client ID:Client ID 用于唯一标识客户端连接与会话,MQTT 允许客户端自行指定 Client ID,也支持由服务端统一为客户端分配 Client ID。
- Connection:MQTT 客户端与 MQTT 服务端之间的网络连接。MQTT 客户端之间并不会直接建立连接。
- Enhanced Authentication:MQTT v5.0 通过新增的 AUTH 报文实现了对增强认证的支持,在原先通过 Username 和 Password 提供的密码认证和 Token 认证的基础上进行了扩展。它更像是一种认证框架,允许使用各种更安全的认证机制,例如 SCRAM 认证就支持服务端和客户端互相确认对方的身份,以抵御中间人攻击。
- Message:通常指 PUBLISH 报文。
- Message Expiry Interval:MQTT v5.0 允许客户端为消息设置过期时间,避免在服务端中停留了较长时间的消息仍然被转发给订阅端。
- Payload:MQTT 报文中的有效载荷部分,根据报文类型,有效载荷的内容会有所不同。对于 PUBLISH 报文来说,有效载荷即消息的实际内容。而对于 SUBSCRIBE 报文来说,有效载荷指的是订阅列表。不过大部分情况下,如无特别说明,Payload 都是指 PUBLISH 报文中消息的实际内容。
- PINGREQ & PINGRESP:客户端需要及时发送 PINGREQ 报文告知服务端自己还活着。服务端也需要及时响应 PINGRESP 报文以便客户端判断网络和服务端的活动状态。
- Publish/Subscribe:发布订阅机制是 MQTT 协议的核心。它解耦了消息的发送方(发布者)和接收方(订阅者),引入了一个中间代理的角色来完成消息的路由和分发。发布者和订阅者不需要知道彼此的存在,他们之间唯一的联系就是对消息的一致约定,例如消息将使用什么主题、消息将包含哪些字段等等。通过发布订阅机制,我们可以实现消息的广播、组播和单播。
- QoS:MQTT 定义了三种 QoS 等级,来分别提供不同的消息可靠性保证。每条消息都可以在发布时独立设置自己的 QoS。
- QoS 0:最多交付一次,消息可能丢失。
- QoS 1:至少交付一次,消息可以保证到达,但是可能重复到达。
- QoS 2:只交付一次,消息保证到达,并且不会重复。
- Server:在发布消息的客户端和订阅的客户端之间充当中介的设备或应用程序,它的首要职责是将所有接收到的消息转发给匹配的订阅客户端。
- Session:MQTT 的会话机制确保了 QoS 1、2 消息的协议流程得以实现。会话是客户端与服务端之间的有状态交互,存储了 QoS 1、2 消息的传输状态以及订阅信息等状态信息。它可以仅持续和网络连接一样长的时间,也可以跨越多个网络连接存在。我们通常将后者称为持久会话。我们可以选择让连接从已存在的会话中恢复,也可以选择从一个全新的会话开始。
- Topic:主题被用来标识和区分不同的消息,它是 MQTT 消息路由的基础。发布者可以在发布时指定消息的主题,订阅者则可以选择订阅自己感兴趣的主题来接收相关的消息。
- Topic Filter:主题过滤器在订阅时使用,可以包含主题通配符来同时订阅多个主题。
- Topic Name:主题名在发布消息时使用,不允许使用主题通配符。
- Topic Wildcards:MQTT 提供了两种主题通配符,分别是 + 表示的单层通配符和 # 表示的多层通配符。通配符只能在主题过滤器中使用。
- Will Delay Interval:用于指示遗嘱消息可以在连接断开后延迟多久发出,仅限 MQTT v5.0。
- Will Message:如果客户端不正常地断开连接,那么该客户端在连接时设置的遗嘱消息就会被服务端转发给其他的客户端。遗嘱消息和普通消息一样具有主题、QoS、Payload、保留消息标识位等字段。
- $ Topic:以 $ 开头的主题必须由服务端来决定其使用方式和场景,客户端不能仅出于自己的目的来随意使用这类主题。例如 $share 开头的主题用于共享订阅,$SYS 开头的主题通常被服务端用于发布系统消息。EMQX 还定义了 $delay 前缀用于实现消息的延迟发布。
# mqtt 优势
物联网首选协议,关于 MQTT 你需要了解这些 | EMQ (opens new window)
MQTT (opens new window) 是一种基于发布/订阅模式的轻量级消息传输协议,专门针对低带宽和不稳定网络环境的物联网应用而设计,可以用极少的代码为联网设备提供实时可靠的消息服务。MQTT 协议广泛应用于物联网、移动互联网、智能硬件、车联网、智慧城市、远程医疗、电力、石油与能源等领域。
MQTT 协议由 Andy Stanford-Clark (opens new window) (IBM)和 Arlen Nipper(Arcom,现为 Cirrus Link)于 1999 年发布。 按照 Nipper 的介绍,MQTT 必须具备以下几点:
- 简单容易实现
- 支持 QoS(设备网络环境复杂)
- 轻量且省带宽(因为那时候带宽很贵)
- 数据无关(不关心 Payload 数据格式)
- 有持续地会话感知能力(时刻知道设备是否在线)
# 场景
业务线程池报错
大文件读取
# 事务传播
# 注解问题
@Transcation m1(){ @Transcation m2 }
m1(){ @Transcation m2 }
# Java设计一个分布式锁
# maven 依赖
maven 项目,里面有 a,a 包含 b 和 c,b 和 c 依赖的 log4j 版本不一样,打包 a 会出现报错吗?会使用哪个版本?
- 使用第一声明者优先原则:谁先定义的就用谁的传递依赖,即在 pom.xml 文件自上而下,先声明的 jar 坐标,就先引用该 jar 的传递依赖。
- 使用路径近者优先原则:即直接依赖级别高于传递依赖。
- 排除依赖:execlude
类加载机制,同一个类只会加载一次,不会导入同一个依赖的多个版本。 是否报错,取决于 log4j 的兼容性,是否兼容低版本。 一般不会,想 spring 各种中间件,那么多依赖,都没问题。
# shell
shell中使用环境变量 $+环境变量。 变量命令=,使用$+变量名来使用变量
var1="123"
echo $var1
for i in {1..5}
do
echo $i
done
var2="222"
# 数字比较 -eq,== 对比字符串
if [ $var1 -eq $var2 ]
then
echo "="
else
echo '!='
fi
name="John"
echo 'Hello, $name' # 输出: Hello, $name
echo "Hello, $name" # 输出: Hello, John
echo "Hello, \$name" # 输出: Hello, $name
echo "Hello, 'John'" # 输出: Hello, 'John'
# $()相当于``,执行结果赋值给变量
current_date=$(date)
echo "Current date is: $current_date"
shell中换行,使用反斜杠 \。参考 docker