[MySQL] MySQL逻辑架构

6.1 SQL执行过程 https://xiaolincoding.com/mysql/base/how_select.html#mysql-%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B%E6%98%AF%E6%80%8E%E6%A0%B7%E7%9A%84 MySQL架构分为两层:server层和存储引擎层 Server层负责建立连接、分析和执行SQL MySQL大多数核心功能模块都在这里:连接器、查询缓存、解析器、预处理器、优化器、执行器等 还有所有的内置函数 所有跨存储引擎的功能 存储引擎层负责数据的存储和提取 支持InnoDB、MyISAM、Memory等多个存储引擎 6.1.1 连接器 MySQL是基于TCP协议进行传输的,所以在连接MySQL的时候需要先进行TCP三次握手,在命令行使用命令进行连接 mysql -h $ip -u$user -p 用户通过用户密码成功连接后,连接器会获取用户的权限,然后保存起来,在后续的此连接的任何操作,都会基于连接开始的时候读取到的权限逻辑进行判断 建立连接后,即使修改了该用户的权限,也不影响已连接的权限。只有新建的连接才会有新的权限设置 6.1.1.1 查看MySQL服务的客户端连接 可以执行show processlist 命令进行查看 6.1.1.2 空闲连接会一直占着 不会,MySQL定义了空闲连接的最大空闲时长,由wait_timeout 参数控制,默认值是8小时,超过这个时间,连接器就会把这个连接断开 使用命令可以查看该值 show variables like 'wait_timeout'; 可以手动断开空闲的连接,使用的是 kill connection + id 当空闲的连接被服务端主动断开后,这个客户端并不会马上知道,等到客户端在发起下一个请求时,才会收到报错 “ERROR 2013 (HY000): Lost connection to MySQL server during query” 6.1.1.3 MySQL的连接限制 MySQL服务支持的最大连接数由max_connections 参数控制 show variables like 'max_connections'; MySQL的连接跟HTTP一样,有短连接和长连接的概念 // 短连接 连接 mysql 服务(TCP 三次握手) 执行sql 断开 mysql 服务(TCP 四次挥手) // 长连接 连接 mysql 服务(TCP 三次握手) 执行sql 执行sql 执行sql .... 断开 mysql 服务(TCP 四次挥手) 一般推荐长连接,但是使用长连接可能会占用内存增多,因为_MySQL在执行查询过程中临时使用内存管理连接对象__,_只有在连接断开的时候才会释放 ...

October 31, 2024 · 1 min · Chen-Hang

[MySQL] MySQL内存

为什么要有Buffer Pool MySQL的数据存储在磁盘的,如果每次都从磁盘里面读取数据,这样性能是很差的 提高性能,就需要加入缓存。当数据从磁盘中取出来之后,缓存内存中,下次查询同样的数据,直接从内存中读取 为此InnoDB存储引擎设计了一个缓存池(Buffer Pool),来提高数据库的读写性能 有了缓冲池后: 读取数据时,如果数据存在于Buffer Pool中,客户端就会直接读取Buffer Pool中的数据,否则再去磁盘中读取 当修改数据时,首先修改Buffer Pool中数据所在的数据页,然后将该页设置为脏页,最后由后台线程将脏页写入到磁盘 Buffer Pool有多大? Buffer Pool在MySQL启动的时候,向操作系统申请的一片连续的内存空间,默认配置下Buffer Pool只有128MB 可以通过调整innodb_buffer_pool_size 参数来设置Buffer Pool的大小,一般建议设置为可用物理内存的60%~80% Buffer Pool缓存什么? InnoDB会把存储的数据分为若干个页,以页作为磁盘和内存交互的基本单位,一个页的默认大小为**16kb,**因此Buffer Pool同样需要按页来划分 在MySQL启动的时候,**InnoDB会为Buffer Pool申请一片连续的内存空间,然后按照默认的16kb的大小划分出一个个的页,Buffer Pool中的页就叫做缓存页。**这些缓存页都是空的,之后随着程序的运行,才会有磁盘上的页被缓存到Buffer Pool中 所以,MySQL刚启动的时候,其使用的虚拟内存空间很大,而使用到的物理内存空间很小,这时因为这些虚拟内存被访问后,操作系统才会触发缺页中断,接着将虚拟地址和物理地址建立映射关系 Buffer Pool缓存了以下的: 索引页 数据页 插入缓存页 Undo页 自适应哈希索引 锁信息 为了更好管理Buffer Pool中的缓存页,InnoDB为每一个缓存页都创建了一个**控制块,**控制块包括缓存页的表空间,页号,缓存页地址,链表节点等,控制块也占据内存空间,它是在Buffer Pool的最前面,接着才是缓存页 暂时无法在飞书文档外展示此内容 上面的控制块和缓存页之间的空白空间称为碎片空间 碎片空间:每一个控制块对应一个缓存页,在分配足够多的控制块和缓存页后,可能剩余的空间不足够一个控制块和缓存页的大小,那么这块空间就不被使用,剩下的这块空间就被称为碎片 当Buffer Pool的大小设置的刚刚好,就不会产生碎片 查询一条记录时,InnoDB会把整个页的数据加载到Buffer Pool中,通过索引只能定位到磁盘中的页,而不能定位到页中一条记录。 mp.weixin.qq.com(从数据页的角度看B+树——InnoDB存储引擎) 记录是按照行来存储的,但是数据库的读取并不是以行为单位,否则一次读取(一次IO操作)只能处理一行数据,效率会非常低,因此,InnoDB的数据是按照数据页为单位来读写的 数据页的结构分为7个部分 File Header(38) 文件头,表示页的信息 Page Header(56) 页头,表示页的状态信息 infimum+supermun(26) 两个虚拟伪记录,分别表示页中最小记录和最大记录 User Records(unclear) 存储行记录内容 Free Space(unclear) 页中还没被使用的 Page Directory(unclear) 页目录,存储用户记录的相对位置,对记录起索引作用 File Tailer(8) 校验页是否完整 其中,行记录由infimum+supremum 和 User Records构成 ...

October 30, 2024 · 1 min · Chen-Hang

[redis] 集群

redis集群

October 30, 2024 · 1 min · Chen-Hang

[MySQL] MySQL日志

先理解执行一条sql语句,在mysql内部会发生什么? 以执行一条update 语句为例: 客户端会先通过连接器建立连接,连接器会判断用户身份 这里是一条update语句,所以不需要经过查询缓存(注意,当表上有更新语句,会把整个查询缓存清空,所以在Mysql8.0这个功能就被移除了) 解析器会通过词法分析识别出关键字,构建出语法树,接着做语法分析,判断输入的语句是否符合MySQL语法 预处理器会判断表和字段是否存在 优化器确定执行计划(使用索引或者全表查询) 执行器负责具体执行,找到这一行然后更新 不过,更新语句的流程会涉及到undo log**,redo log,binlog**三种日志: undo log(回滚日志):是InnoDB存储引擎生成的日志,实现了事务中的原子性,主要用于事务回滚和MVCC redo log(重做日志):是InnoDB存储引擎生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复 bing log(归档日志):是Server层生成的日志,主要用于数据备份和主从复制 1.为什么需要undo log? 在执行一条“增删改”语句的时候,MySQL会隐式开启事务,执行完后自动提交事务 MySQL中执行一条语句后是否自动提交事务,是由autocommit 参数来决定的,默认是开启的 当事务执行过程中,都记录下回滚时需要的信息到一个日志中,那么在事务执行过程中发生MySQL崩溃后,可以通过这个日志回滚到事务之前的数据 实现这一机制就是 undo log**(回滚日志),它保证了事务的ACID特性中的原子性** 每当InnoDB引擎对每种操作进行回滚时,进行相反操作就行: 插入 - 删除 删除 - 插入 更新 - 更新为旧值 一条记录每次进行操作产生的undo log格式都有一个roll_pointer和一个trx_id事务id: trx_id:记录该记录是被哪些事务修改的 roll_pointer:指针可以将这些undo log串成一个链表,这个链表被称为版本链 另外,undo log可以跟Read View一起实现MVCC(多版本并发控制): 对于 读提交 和 可重复读 隔离级别的事务来说,它们的快照读(普通select语句)是通过Read View + undo log来实现的,区别在于创建Read View的时机不同 读提交:是在每一个select都会生成一个新的Read View,也意味着事务期间的多次读取同一数据,前后两次读的数据可能会出现不一致(不可重复读) 可重复读:是在启动事务时生成一个Read View,然后整个事务期间都在用这个Read View,这样保证了事务期间读到的数据都是事务启动时的记录 这两个隔离级别实现是通过事务的Read View里的字段和记录两个隐藏列trx_id和roll_pointer的对比 事务隔离级别是怎么实现的? 因此,undo log两大作用: 实现事务回滚,保障事务的原子性 实现MVCC(多版本并发控制)关键因素之一 Undo log是如何刷盘? ...

October 29, 2024 · 2 min · Chen-Hang

[redis] 场景

六、场景 1.缓存 Redis由于性能高效,通常可以做数据库存储的缓存,比如给Mysql做缓存 通常业务都满足二八原则,80%的流量在20%的热点数据上,所以缓存可以很大程度提高系统的吞吐量 1.1缓存基础 一般而言,缓存分为服务器缓存,客户端缓存 缓存一般有以下几种模式: 旁路缓存模式: 读穿透模式: 写穿透模式: 异步缓存写入模式: 旁路缓存模式(适用于读多写少) Cache Aside,旁路缓存模式,是最常见的模式,应用服务把缓存当作数据库的旁路,直接和缓存交互 读操作:服务端收到查询请求,先查询数据是否在缓存上,如果在,就用缓存数据直接打包返回,如果不存在,就去数据库查询,并放到缓存 写操作:cache aside模式一般先更新数据库,再直接删除缓存(更新相比删除更容易造成时序性问题) 适用于读多写少的场景,缺点是可能会出现缓存和数据库不一致的情况 这里的写操作,更新相比删除更容易造成时序性问题,具体举例:线程1更新mysql -> 线程2更新mysql -> 线程2更新缓存 -> 线程1更新mysql,这样就出现了时许性问题 该模型的缺点: 可能出现缓存和数据库不一致的情况,具体见:数据库和缓存如何保证一致性? 读穿透模式 与cache aside模式的区别主要在应用服务不再与缓存直接交互,而是直接去访问数据服务。 这里的数据服务理解为一个**代理服务,**用它来访问缓存和数据库 相比于旁路缓存模式,读穿透模式的优势是缓存对业务是透明的;缺点是缓存命中的性能不如旁路缓存模式,会多一层服务调用 写穿透模式 WriteThrough做了一层封装:有缓存服务先写入Mysql,再同步写入Redis,这样及时加载或更新了缓存数据(理解为,应用程序由一个单独的访问源,而存储服务自己维护访问逻辑) 在使用WriteThrough时,一般都配合使用ReadThrough来使用 适用情况: 对缓存及时性要求更高 不能忍受数据丢失和数据不一致 异步缓存写入模式(Write-Behind) write-Behind和Write-Through相同点是都是写入时会更新数据库、也会更新缓存 不同点是:Write-Behind是先写缓存,后异步把数据一起写入数据库 数据库写操作可以用不同的方式完成: 收集写操作并在某个时间点慢慢写入 合并几个写操作成为一个批量操作,一起批量写入 异步写操作极大降低了请求延迟,并减轻了数据库的负担,但是代价是安全性不够,如果缓存中的数据还没写入数据库,存储服务发生了崩溃,那么数据就丢失了 1.2缓存异常 缓存穿透 问题背景 缓存穿透是指**缓存和数据库都没有的数据,**而用户不断发起请求。 在流量大的时候,DB可能就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞 解决方案 接口层增加校验,如用户鉴权校验,id做击穿校验,id<=0的直接拦截 从缓存取不到的数据,在数据库中也没有取到,这时可以将key-value对写成key-null,缓存有效时间写短点,例如30s 布隆过滤器:bloomfilter类似于一个hash set,用于快速判断某个元素是否存在于集合中,关键在于hash算法和容器大小 布隆过滤器: 原理:布隆过滤器底层是一个64位的整型,将字符串用多个Hash函数映射不同的二进制位置,将整型中对应位置设置为1 优点:空间、时间消耗都很小 缺点:结果不完全准 缓存击穿 问题背景 缓存击穿是指缓存中没有但数据库中有的数据(一般缓存时间到期),这时由于并发的用户过多,同时读缓存没有数据又同时查询数据库,引起数据库压力瞬时增大 解决方案 热点数据支付续期,持续访问的数据不断续期,避免因为过期失效而被击穿 发现缓存失效,重建缓存加互斥锁,当线程查询缓存发现缓存不存在就会尝试加锁,线程抢锁,拿到锁的线程进行查询数据库,然后重建缓存 缓存雪崩 问题背景 指大量的应用请求因为异常无法在Redis缓存中处理,直接打到数据库。这里的异常就是:缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚者宕机 ...

October 29, 2024 · 1 min · Chen-Hang