MySQL的锁
MySQL锁 全局锁: flush tables with read lock,执行此条命令,整个数据库处于只读状态 unlock tables, 释放全局锁,会话断开时,也会自动释放全局锁 一般用来全库逻辑备份 表级锁:MySQL 里面表级别的锁有这几种: 表锁:通过lock tables 语句可以对表加表锁,表锁除了会限制别的线程的读写外,也会限制本线程接下来的读写操作。 元数据锁:当我们对数据库表进行操作时,会自动给这个表加上 MDL,对一张表进行 CRUD 操作时,加的是 MDL 读锁;对一张表做结构变更操作的时候,加的是 MDL 写锁;MDL 是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。 意向锁:当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。意向锁的目的是为了快速判断表里是否有记录被加锁。 行级锁:InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。 记录锁(Record Lock) 锁住某行记录,分为读写锁,事务提交后自动释放,例如select * from...
MySQL的MVCC
MVCC多版本并发控制 MVCC 的目标:让读操作尽量不阻塞写操作,写操作也尽量不阻塞读操作。 核心机制: MVCC主要依赖以下机制实现: 数据库行记录中的隐藏字段:最近修改本行数据的事务id、回滚指针指向本行数据的上个版本(其实是指向undo,通过undo计算上一个版本) •undo log:回滚日志,在insert、delete、update时产生,记载了与操作相反的操作,比如delete操作对应了一条insert日志 •readview:快照读SQL执行时产生的读视图,生成的一个快照,记录了以下字段,用于数据的可见性判断:◦ 当前活跃的事务id集合◦ 最小活跃事务id◦ 预分配事务id,即应该分配的下一个事务id◦ readview创建者id,即本事务id ...
MySqL事务特性,隔离级别
MySqL事务隔离级别 事务的特性: 原子性:事务要么全部提交成功要么全部失败 一致性:事务操作前和操作后具有完整性约束,数据库保持一致性状态 隔离性:事务执行时相互隔离互不影响,可以防止多个事务并发执行由于交叉执行导致的不一致 持久性:事务操作结束后修改是永久性的,即使关机故障也不会消失 MySQL InnoDB 引擎通过什么技术来保证事务的这四个特性的呢? 持久性是通过 redo log (重做日志)来保证的; 原子性是通过 undo log(回滚日志) 来保证的; 隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的; 一致性则是通过持久性+原子性+隔离性来保证; 事务的隔离级别有哪些? 读未提交 最低的隔离级别, 会导致脏读、幻读或不可重复读。 读已提交 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。这是大多数数据库(如 Oracle, SQL Server)的默认隔离级别。 ...
Redis的ziplist到ListPack
Redis的压缩列表ziplist到紧凑列表listpack ziplist 的结构(老版本用的) 大概长这样: 12345[zlbytes][zltail][zllen][entry1][entry2]...[entryN][0xFF]● *zlbytes*,记录整个压缩列表占用对内存字节数;● *zltail*,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;● *zllen*,记录压缩列表包含的节点数量;● *0xFF*,标记压缩列表的结束点,固定值 0xFF(十进制255)。 每个 entry 里: 1[prevlen][encoding][data] prevlen:前一个 entry 的长度(1 或 5 字节) encoding:当前 entry 是字符串还是整数,长度是多少 data:真正的数据 listpack 的结构(新版本用的) 大概长这样: 1234[total_bytes][count][entry1][entry2]...[entryN][0xFF]total_bytes:整个 listpack...
Redis网络模型
Redis网络模型(io多路复用) 阻塞IO(Blocking IO) 假设服务端只开启一个线程处理请求,第一个请求到来,开始调用内核read函数,然后就会发生阻塞,第二个请求到来时服务端将无法处理,只能等第一个请求读取完成。这种方式的缺点很明显,每次只能处理一个请求,无法发挥cpu多核优势,性能低下。 为了解决这个问题,我们可以引入多线程,这样就可以同时处理多个请求了,但服务端可能同时有成千上万的请求需要处理,随之而来的是线程数膨胀,频繁创建、销毁线程带来的性能影响,当然我们可以使用线程池,但服务能处理的总体数量就会受限于线程池线程数量。 非阻塞IO(NON-Blocking...
MySQL 联合索引的最左匹配原则
MySQL 联合索引的最左匹配原则 执行计划基础知识 possible_keys:可能用到的索引 key:实际用到的索引 type: ref:当通过普通的二级索引列与常量进行等值匹配的方式 询某个表时 const:当我们根据主键或者唯一得二级索引列与常数进行等值匹配时,对单表的访问方法就是 const range:如果使用索引获取某些单点扫描区间的记录。 index:当可以使用覆盖 ,但需要扫描全部的索引记录时。 Extra: Using index 索引覆盖 Using Where 当某个搜索条件需要在 server 层进行判断时 Using index for skip scan 跳跃扫描 Using index condtion 索引下推 最左匹配原则 最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。 比如有联合索引 [a、b、c],where 过滤条件中哪些排列组合可以用到索引?(比如这种:where a=xxx b=xxx and c=xxx) 以下排列组合都会走索引:...
redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性) 在项目中,我们主要通过 Redisson 的读写锁机制 来保证数据的强一致性。 当执行读取操作时,我们会加上读锁(共享锁),这样可以做到: 读读不互斥; 读写互斥。 而在更新数据时,我们会加上写锁(排他锁),此时写写、读写都互斥。 这样可以确保在写入数据库的同时,其他线程无法读取缓存数据,从而避免出现脏读或不一致的情况。 这里需要注意的一点是:读写方法必须使用同一把锁,才能真正保证互斥关系成立。 除了使用分布式锁,我们也考虑过使用 “延迟双删”策略 来实现最终一致性。 具体做法是: 先删除缓存; 再更新数据库; 最后延时一段时间后,再删除一次缓存。 不过,延迟的时间点很难精确把握。在延时窗口内仍可能出现脏数据问题,无法满足强一致性场景,因此我们最终没有采用这种方式。 在我最近做的项目中(例如简历中提到的 xxxx 功能), 系统对数据一致性的实时性要求没有那么高,可以接受一定的同步延时。 因此我们采用了 阿里巴巴的 Canal 组件 来实现最终一致性的数据同步。 Canal...
Redis分布式锁redisson看门狗机制
Redis分布式锁方案 使用 Redis 的 SET key value NX EX实现分布式锁时,主要问题是锁过期时间不好设置:如果设置过短,业务未完成锁就过期;设置过长,会降低系统并发度。为此可以使用 Redisson 封装的分布式锁,它内部通过「看门狗机制」自动续期锁(默认锁 30 秒,每 10 秒续期一次),同时通过 Lua 脚本保证加解锁的原子性,从而确保分布式环境下的并发安全。 Redisson分布式锁自动续约机制(看门狗) 线程1尝试tryLock锁未指定锁的过期时间 获取成功后会自动启动看门狗机制 初始加锁: 当调用 lock() 时,Redisson 会通过 Lua 脚本向 Redis 发送加锁命令,设置锁的初始过期时间为 LockWatchdogTimeout(默认 30 秒)。 锁的数据结构为 Hash,键为锁名称,field 为线程 ID,value 为重入次数。 启动定时任务: 加锁成功后,Redisson 会启动一个后台定时任务(通过 Netty 的 HashedWheelTimer 实现),每隔 10 秒执行一次。 ...
Redis的数据过期淘汰策略
Redis 的数据过期策略 Redis 采用 「惰性删除 + 定期删除」 两种策略相结合的方式,在 CPU 性能开销 与 内存占用 之间取得平衡。 一、惰性删除(Lazy Expiration) 当客户端访问或修改某个键时,Redis 会在操作前调用 expireIfNeeded() 函数检查该键是否已过期: 若已过期: Redis 会立即删除该键,然后向客户端返回 null。 删除方式由配置项 lazyfree-lazy-expire 决定: 若开启(yes),则采用 异步删除; 若关闭(no),则采用 同步删除。 若未过期: 直接返回该键对应的正常值,不做任何额外处理。 这种方式的优点是 删除时机精准、不会浪费 CPU 时间; 缺点是:若某些过期键一直未被访问,就无法被清除,会长期占用内存。 二、定期删除(Active Expiration Cycle) 为了解决惰性删除的不足,Redis 还会周期性地扫描部分键空间,清除已过期的数据。 1. 检查频率 Redis 默认每秒执行 10 次过期扫描,这一频率由配置文件 redis.conf 中的参数 hz...
缓存穿透,雪崩,击穿
缓存穿透 缓存穿透指的是:一个缓存和数据库中都不存在的数据,由于数据库无法写入缓存,导致频繁请求直接打到数据库,从而造成数据库压力过大甚至被“打穿”。 常见的解决方法包括:缓存空值 或 布隆过滤器。 布隆过滤器由一个全 0 的位图数组和 n 个哈希函数组成。当一个数据在请求缓存前,会先经过布隆过滤器。过滤器会通过 n 个哈希函数计算出 n 个哈希值,再分别对数组长度取模,并将对应的数组下标置为 1。 查询时,只需要查看位图数组中这 n 个位置是否全部为 1: 若存在某个位为 0,则说明该数据一定不存在于数据库中; 若全部为 1,则说明该数据可能存在,但不一定真的存在,因为可能出现哈希碰撞。 因此,布隆过滤器判断“存在”时并不能保证数据真的存在,但判断“不存在”时一定准确。 缓存雪崩 缓存雪崩指的是:大量的缓存 Key 在同一时间失效,或者 Redis 服务器宕机,导致所有请求同时打到数据库,造成数据库瞬时压力过大,甚至宕机的情况。 解决方法: 为缓存设置 固定过期时间 + 随机过期时间,让不同 Key 的过期时间错开,避免在同一时间集中失效。 ...




