MySQL理解innodb的线程模型
第一部分, innodb的线程模型:
innodb内部大体上有三类线程, 主线程(只有一个); 用户线程 , 用户线程根据不同的职责也可以分成两类; 辅助线程
整个innodb由一个主线程控制, 这个线程是innodb一起来的时候就开始运行(入口函数是srv_master_thread), 它的优先级相对于其他innodb内部的线程来说优先级较高, 大部分时间下, 这个线程处于sleep状态, 每隔一定间隔这个线程起来看看是否有什么任务需要处理,这些任务包括:
1. drop延迟删除的表(如果有的话)
2. 把日志缓冲区(log buffer)中的日志(如果有的话)通过同步写的方式flush到硬盘
3. 检查缓冲控制区的完整性
4. 把缓冲区(buffer pool)中的数据写入到硬盘
5. 插入缓冲区(ibuf)的合并(merge)
6. binlog的purge
7. 生成检查点(checkpoint)
innodb运行了一些处于normal优先级的被称作用户线程的线程,大体分成二大类:
一类是包括专门负责处理客户端请求的线程, 处理控制台请求的线程等;每个用户线程负责一个客户端的请求, 其完成的工作包括接收输入, 启动处理过程, 当查询处理完成后负责把结果返回给客户端;也就是说, 没有专门的负责通信的线程和专门负责处理的线程之分, 这样做的好处是少了一些线程的切换开销,个人觉得,这个节省到底有多大和必要是和机器的状况, 客户端的数量, 客户端所处的位置相关的;另外, 这种模型在客户端多且请求频繁的情况下对于设置并发线程(innodb的并发模型后面会谈到)的上限来说比较困难; 顺便提一下, mysql客户端和服务器的通信时单工的, 所谓单工, 就是会出现服务器端和客户端同时发送信息给对方的情况。
第二类线程是负责执行sql语句的线程, 这些线程会把sql请求分解,然后执行, 待执行完成后, 通知客户端线程(第一类线程)来返回数据
辅助线程, 完成一些辅助工作, 比如异步读写线程, 负责插入缓冲区flush的线程和负责日志flush的线程等
innodb维护有一个线程表的数据结构来管理所有的线程, 其中记录了线程的状态信息,用来控制挂起的信号量等.
innodb的”线程调度器”
innodb自己实现了一个”线程调度器”: 在同一时刻, 只允许一定数量的处于活动状态的并发线程位于innodb内核, 全局变量srv_thread_concurrency(变量innodb_thread_concurrency)控制着这个数目,
对于一台4cpu, 4disk的机器, 推荐的srv_thread_concurrency是10, 这样做的目的是尽量减少不必要的线程切换; 对于srv_thread_concurrency来说,0是个特殊值, 用来表示不进行任何并发线程数量的控制; 当然, 处于等待状态的线程(等待某些资源的进程)是不算在内的, 因为如果这些都算的话, 可能会导致死锁。 当一个线程试图进入innodb内核时, 都会调用函数srv_conc_enter_innodb来取得进入innodb的许可,如果当前处于内核的线程数目达到了上限,且这个线程所进行的事务没有占用资源的话(这样做的目的是为了防止死锁), 会尝试把这个线程睡眠一段时间(变量innode_thread_sleep_delay)然后再尝试(这么做的目的还是为了减少线程上下文切换的开销, 注意, 这里不是较少上下文切换),如果并发数目已经低于上限则会允许进入内核,否则会尝试把这个线程挂起, 以使其处于等待状态, 如果已经处于等待状态的线程还没有达到等待队列的上限, 则把这个线程挂起,并把它放到等待队列;如果等待对列已经满了的话, 也会允许这个线程进入内核;一般来说,处于某个事务当中的线程, 在进入内核后会在不近的将来再次进入内核, 因此, 通过并发控制的线程会分配到一定数量的进入券(n_tickets_to_enter_innodb控制, 变量innodb_concurrency_tickets), 当没有用完这些券之前, 该线程是可以直接进入内核的, 这其实是允许已经获得资格的线程优先再次进入内核。当然了, 主从复制的线程不在这个控制的范围内。
