[MySQL] MySQL锁

锁的类型 Mysql的锁,根据加锁的范围可以分为全局锁、表级锁和行锁三类 全局锁 要使用全局锁,执行下面这条命令: flush tables with read lock 执行之后,整个数据库就处于只读状态,这时其他线程执行以下操作,就会被阻塞 对数据的增删改,比如insert、delete、update等 对表结构的更改操作,比如alter table、drop table等 要释放全局锁,执行下面的命令: unlock tables 全局锁的应用场景: 全局锁主要用于做全库逻辑备份,这样在备份数据库期间,不会因为数据或者结构的更新,而出现备份文件的数据与预期的不一样 加全局锁带来的缺点:会导致业务停滞,因为加全局锁之后,整个数据库都只是只读状态,不能更新数据 可以通过开启事务,在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的Read View, 备份数据库的工具是mysqldump ,在使用mysqldump时加上-single-transaction 参数的时候,就会在备份数据库之前开启事务 表级锁 MySQL里面表级锁有以下几种: 表锁 元数据锁(MDL) 意向锁 AUTO-INC锁 表锁 使用下面的命令对表加锁和释放锁 // 加读锁 lock tables <table_name> read; // 写锁 lock tables <table_name> write; // 释放锁 unlock tables; 表锁会影响别的线程和本线程的读写操作 元数据锁(MDL) 对于MDL,我们不需要显示使用,因为当我们在对数据库进行操作时,会自动给这个表上加MDL: 对一张表进行CURD操作时,加的是MDL读锁 对一张表做结构变更操作的时候,加的是MDL写锁 MDL是为了保证当前用户对表执行CRUD操作时,防止其他线程对这个表结构做了变更 MDL是在事务提交之后才会释放,这意味着事务执行期间,MDL是一直持有 需要注意的是,在事务启用之后,如果事务A没有提交,此时如果有表结构的修改请求发起,就会发生阻塞,这个阻塞也会导致其他CURD的请求被阻塞住 这是因为申请MDL锁的操作会形成一个队列,队列中写锁获取优先级大于读锁,一旦出现MDL写锁等待,会阻塞该表后续的CRUD操作 意向锁 在使用InnoDB引擎的表里对某些记录加上共享锁之前,需要先在表级别加上一个意向共享锁 在使用InnoDB引擎的表里对某些记录加上独占锁之前,需要先在表级别加上一个意向独占锁 在执行insert、update、delete操作时,需要先对表上加 意向独占锁,然后对该记录加独占锁 而普通的select是不会加行级锁,普通的select语句是利用MVCC实现一致性读,是无锁的 // select也是可以对记录加共享锁和独占锁, // 先在表上加上意向共享锁,然后对读取的记录加共享锁 select ... lock in share mode; // 先表上加上意向锁,然后再读取记录加独占锁 select ... for update 意向锁的目的是为了快速判断表里是否有记录被加锁 AUTO-INC锁 表里面的主键通常设置成自增的,在插入数据时,可以不指定主键的值,数据库会自动给主键赋值递增的值,这主要是通过AUTO-INC锁实现的 Auto-Inc锁是特殊的表锁机制,不是在一个事务提交后才释放,而是再执行完插入语句后就会立即释放 行级锁 InnoDB引擎是支持行级锁的,而MyISAM引擎并不支持行级锁 行级锁的类型主要有三类: ...

October 28, 2024 · 3 min · Chen-Hang

[redis] 持久化

五、持久化 0.持久化介绍 redis是跑在内存里的,当程序重启或者服务崩溃,数据就会丢失,所以需要持久化,即把数据保存到可永久保存的存储设备中 持久化方法 redis提供两种方式来持久化: 1.RDB(Redis Database) 记录Redis某个时刻的全部数据,这个方法的本质就是数据快照,直接保存二进制数据到磁盘,后续通过加载RDB文件恢复数据 2.AOF(Append Only File) 记录执行的每条命令,重启之后通过重放命令来恢复数据,AOF是记录操作日志,后续通过日志重放恢复数据 两种持久化方法的对比(*) 上面两种持久化方法对比:RDB(快照恢复)和AOF(日志恢复) 体积方面:相同数据量下,RDB体积小,因为RDB记录的是二进制紧凑型数据 恢复速度方面:RDB是数据快照,可以直接加载,而AOF文件恢复,相当于重放情况,RDB显然更快 数据完整性:AOF记录了每条日志,RDB是间隔一段时间记录一次,用AOF恢复数据通常会更加完整 RDB好还是AOF好 业务本身需要的是缓存数据并且不是一个海量访问,可以不用开持久化 对数据本身十分重视,可以同时开启RDB和AOF,注意,在同时开启的情况下,只会用AOF来加载,如果只有RDB文件而没有AOF文件,不会用RDB文件去恢复数据,如果逻辑是你自主开启选择AOF,表明要强一点的一致性,但是AOF文件缺失,此时不会去使用RDB,业务RDB会少很多数据,此时启动是一个空库 1.RDB RDB文件的内容是**二进制数据,**记录的是某一瞬间的内存数据,是实际的数据,也叫做快照 在Redis恢复数据时,RDB恢复数据的效率会比AOF高,因为会直接读取RDB文件到内存即可,不需要像AOF那样还需要额外执行操作命令的步骤才能恢复数据 1.1 RDB怎么使用 Redis提供两个命令来使用RDB文件,分别是save 和bgsave save :执行了save命令,会在主线程生成RDB文件,由于和执行操作命令在同一个线程,所以写入RDB文件的时间太长,会阻塞主线程 bgsave :执行了bgsave命令,会创建一个子进程来生成RDB文件,这样可以避免主线程的阻塞 RDB文件的加载工作是在服务器启动时自动执行,Redis并没有提供专门加载RDB文件的命令 在Redis的配置文件中,有以下的选项来实现每隔一段时间自动执行一次bgsave命令 # 这里写的是save,实际上是bgsave save 900 1 # 900s之内,对数据库进行至少1次修改 save 300 10 # 300s之内,对数据库进行至少10次修改 save 60 10000 # 60s之内,对数据库进行至少10000次修改 Redis的快照是全量快照,也就是说每次执行快照,都是把内存中的所有数据记录到磁盘中 RDB快照的缺点:当服务器发送故障时,丢失的数据会比AOF更多 1.2 执行快照时(bgsave),修改数据会发生什么? 在执行bgsave时,主线程是可以继续执行操作命令,由子线程来构建RDB文件,所以内存里的数据是可以修改的 实现这一功能靠的是写时复制****技术 流程如下: 执行bgsave命令时,会fork()创建子进程,此时子进程和父进程是共享同一片内存数据 主进程创建子进程时,子进程会复制父进程的页表,但是页表指向的物理内存跟主进程是同一个物理内存 此时如果主线程由写命令的执行,就会发生写时复制,物理内存才会被复制一次 发生写时复制后,RDB快照保存的是原本的内存数据 这是因为创建bgsave子进程后,由于父子进程共享所有内存数据,所以可以直接将数据写入到RDB文件 当主进程对共享的内存数据是只读****操作,那么主进程和bgsave子进程是相互不影响 当主进程对共享的内存数据进行写操作时,就会发生写时复制,这块数据的物理内存就会被复制一份,然后主线程在这个数据副本进行写操作,此时bgsave子进程继续把原来的数据(原物理内存)写入到RDB文件 执行bgsave时,在极端情况下,如果所有的共享内存都被修改,则此时的内存占用是原先的两倍 1.3 什么时候执行RDB持久化 主动执行命令save 主动执行命令bgsave ...

October 28, 2024 · 1 min · Chen-Hang

[MySQL] MySQL事务

1.事务有哪些特征 原子性,隔离性,一致性,持久性 原子性:要么全做,要么全不做 隔离性:保证其它的状态转换不会影响到本次状态的转 一致性:数据全部符合现实世界的约束 持久性: 更新后的数据存储到磁盘 InnoDB引擎通过以下技术来保证事务的四个特性 持久性是通过 redo log(重做日志)来保证 原子性是通过 undo log(回滚日志)来保证 隔离性是通过 mvcc(多版本并发控制)或者锁机制来保证 一致性是通过持久性+原子性+隔离性来保证 2.并发事务会引发的问题 MySQL服务端是允许多个客户端连接,这意味着MySQL会出现同时处理多个事务的情况 在同时处理多个事务的时候,可能会出现脏读、不可重复读、幻读的问题 脏读:一个事务读到了另一个未提交事务修改过的数据 不可重复读:在一个事务中多次读取同一个数据,出现前后两次读到的数据不一样的情况 幻读:在一个事务中多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数据不一样的情况 以上三个现象,问题的严重性是 脏读 > 不可重复读 > 幻读 3.事务的隔离级别 四种隔离级别: 读未提交:指一个事务还没有提交时,它做的变更就能被其他事务看到 读提交:指一个事务提交之后,它做的变更才能被其他事务看到 可重复读:指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB引擎的默认隔离级别 串行化:对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生读写冲突的时候,后访问的事务必须等前一个事务执行完成 按隔离水平高低排序如下: 串行化 > 可重复读 > 读已提交 > 读未提交 针对不同的隔离级别:并发事务时可能发生的现象也不同 读未提交:脏读、不可重复读、幻读 读提交:不可重复读、幻读 可重复读:幻读 串行化: 可重复读的隔离级别下,可以很大程度上避免幻读现象的发生,所以MySQL不使用串行化隔离级别来避免幻读现象的发生,因为串行化隔离级别会影响性能 InnoDB在默认隔离级别:可重复读的情况下很大程度上解决幻读现象的解决方案有两种: 针对**快照读(普通 select 语句),**是通过MVCC方式解决幻读 针对**当前读(select … for update),**通过next-key lock(记录锁+间隙锁)方式解决了幻读 四种隔离事务是怎么实现的 对于读未提交:可以读到未提交事务修改的数据,所以直接读取就行 对于串行化,通过加读写锁的方式来避免并行访问 对于读提交和可重复读这两种隔离级别的事务,是通过Read View来实现的,它们的区别是在于创建Read View时,读提交隔离级别是在每个语句执行之前都会重新生成一个Read View;而可重复读隔离级别是启动事务时生成一个Read View,然后整个事务都在用这个Read View 在执行开启事务命令,并不意味着启动了事务: 在MySQL中,开启事务有两种命令,分别是: ...

October 27, 2024 · 1 min · Chen-Hang

[redis] 执行流程

四、执行流程 内存结构 核心执行是单线程 多线程负载一些异步任务 1.Redis在内存中是怎么存储的 redis是内存存储,将数据放在redis时,都是以键值对形式存到内存 数据库结构 redisDb代表Redis数据库结构,各种操作对象,都是存储在dict数据结构里 // redisDb 结构 type struct redisDb { dict *dict; //字典 dict *expires; // 过期键 dict *blocking_keys; dict *ready_keys; dict *watched_keys; int id; long long avg_ttl; list *defrag_later; } redisDb; // dict 结构 typedef struct dict { dictType *type; void *privdata; dictht ht[2]; long rehashidx; unsigned long iterators; } dict; redisDb即数据库对象,指向了数据字典,字典包含我们平常存储的k-v数据,v支持任意redis对象 在增加、查询、更新、删除的操作后,分别在内存存储是怎么体现的? 增删改查在Redis内存中的体现 添加数据 即添加键值对,添加到dict结构字典中,Key必须为String对象,value为任何类型的对象 添加数据后,会在redisDb里字段dict上添加dict对象 查询数据 直接在dict找到对应的key,即完成查询 更新数据 对已经Key对象的任何变更操作,都是更新 删除数据 删除即把key和value从dict结构里删除 过期键 Redis可以设置过期键,到达一定时间,这些对象会被自动过期并回收 **过期键存储在expires字典上,**expires字典中,value就是过期时间 在redisDb中,dict和expires中Key对象,实际都是存储String对象指针,两个的key都会指向内存相应的字符串地址 2.Redis是单线程?还是多线程? redis是一个能高效处理请求的组件 核心处理逻辑,Redis一直都是单线程,其他辅助模块会有一些多线程、多进程的功能,例如:复制模块用的多进程;某些异步流程从4.0开始用多线程;网络I/O解包从6.0开始用多线程; 核心处理逻辑:Redis在处理客户端的请求时,包括获取(socket写)、解析、执行、内容返回等都是由一个顺序串行的主线程处理,这就是所谓的单线程 Redis为什么选择单线程 redis的定位是内存k-v存储,是做短平快的热点数据处理,一般来说执行会很快,执行本身不会成为瓶颈,瓶颈通常在网络I/O,处理逻辑多线程并不会有太大收益 同时Redis本身秉持简洁高效的理念,代码的简单性、可维护性是redis一直依赖的追求,执行本身不应该成为瓶颈,而且多线程本身也会引起额外成本 1.多线程引入的复杂度是极大的 多线程引入后,redis原来的顺序执行特性就不复存在,为了事务的原子性、隔离性,redis就不得不引入一些很复杂的实现 redis的数据结构是极其高效,在单线程模式下做了很多特性的优化,如果引入多线程,那么所有底层数据都要改为线性安全,这很复杂 多线程模式使得程序调试更加复杂和麻烦,会带来额外的开发成本及运营成本 2.多线程带来额外的成本 除了引入复杂度,多线程还会带来额外成本,包括 上下文切换成本,多线程调度需要切换线程上下文,这个操作先存储当前线程的本地数据,程序指针,然后载入另一个线程数据,这种内核操作的成本不可忽略 同步机制的开销,一些公共资源,在单线程模式下直接访问就行,多线程需要通过加锁等方式进行同步 一个线程本身也占据内存大小,对redis这种内存数据库来说,内存非常珍贵,多线程本身带来的内存使用的成本也需要谨慎决策 总结 多线程会引入额外的复杂度和成本,而redis是追求简洁高效的存储组件,而且事实也证明,虽然redis是单线程处理架构,redis性能还是经受住了考验 3.Redis单线程为什么能这么快 Redis单线程 Redis核心的请求处理是单线程,但是Redis却能使用单线程模型达到每秒数万级别的处理能力,这是Redis多方面极致设计的一个综合结果 ...

October 27, 2024 · 1 min · Chen-Hang

[MySQL] MySQL索引

1. B+树(索引数据结构) 什么是索引? 为什么索引能加快查询? 索引的数据结构是什么? B+ 树 和(B 树 和 红黑树)有什么区别? 为什么选择 B+树 作为索引数据结构? 为什么Mysql InnoDB选择B+ Tree作为索引? B+ 树 vs B 树 B+ 树只在叶子节点存储数据,B树的非叶子节点也要存储数据,所以B+ 树的单个节点的数据量更小 B+ 树 vs 二叉树 对于有N个叶子节点的B+ 树,搜索复制度为O(logdn) B+ 树 vs Hash 08 索引:排序的艺术 为什么 MySQL 采用 B+ 树作为索引? 2. 索引组织表(索引存储) 堆表和索引组织表有什么区别? 分别应用场景是什么? Mysql InnoDB存储引擎中数据存储方式:索引组织表 数据存储有堆表和索引组织表两种。 堆表中的数据是无序存放的,数据的排序完全依赖索引 索引组织表,数据根据主键进行排序存放在索引中,主键索引也叫聚集索引(Clustered Index) 在索引组织表中,数据即索引,索引即数据 二级索引 InnoDB存储引擎的数据是根据主键索引排序存储的,除了主键索引外,其它的索引都称为二级索引(Secondeary Index),或者非聚集索引 二级索引也是一颗B+树索引,但是它和主键索引不同的是叶子节点存放的是索引键值、主键值 通过二级索引idx_name 只能定位主键值,需要额外再通过主键索引进行查询,才能得到最终结果。 这种二级索引通过主键索引进行再一次查询的操作叫做“回表” 这样的二级索引设计的好处:若记录发生了修改,则其它索引无须进行维护,除非记录的主键发生了修改 在索引组织表中,万物皆索引,索引就是数据,数据就是索引。 二级索引的性能评估 要比较顺序,对聚集索引性能友好 尽可能紧凑,对二级索引的性能和存储友好 函数索引(先了解) … 09 索引组织表:万物皆索引 3.组合索引(联合索引) 联合索引的结构是什么? ...

October 26, 2024 · 2 min · Chen-Hang