Redis的IO模型
Redis的IO 模型Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率。但是由于是单线程如果线程被阻塞了,就无法进行多路复用了。
多路复用的高性能 I/O 模型Redis是基于多路复用的高性能 I/O 模型,该机制允许内核中,同时存在多个监听套接字和已连接套接字(使用的非阻塞模式)。并且为了在请求到达时能通知到 Redis 线程,select/epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。这些事件会被放进一个事件队列,Redis 单线程对该事件队列不断进行处理。这样一来,Redis 无需一直轮询是否有请求实际发生,这就可以避免造成 CPU 资源浪费。
Redis6.0新特性随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度。
单线程结构:
在之前Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写),但是,从网络 IO 处理到实际的读 ...
MySQL用户权限
用户权限创建用户
create user 'ua'@'%' identified by 'pa';
这条语句的逻辑是创建一个用户’ua’@’%’,密码是pa。(%表示可以从任意ip登录,可以指定ip)
这条命令做了两个动作:
磁盘上,往mysql.user表里插入一行,由于没有指定权限,所以这行数据上所有表示权限的字段的值都是N;
内存里,往数组acl_users里插入一个acl_user对象,这个对象的access字段值为0。
权限操作
赋予权限:grant 权限列表 on 数据库.[.表名] to 用户名 [with grant option];(with grant option表示可以传递给其他用户)
取消权限:revoke 权限列表 on 数据库.[.表名] from 用户名;
全局权限:
全局权限,作用于整个 MySQL 实例用于管理MySQL服务器的操作,这些权限信息保存在 mysql 库的 user 表里,只能用*.*赋予。
revoke命令与grant命令,做了如下两个动作:
磁盘上,将mysql.user表 ...
MySQL快速的复制一张表
快速的复制一张表mysqldump你可以使用下面的命令:
mysqldump -h$host -P$port -u$user --add-locks --no-create-info --single-transaction --set-gtid-purged=OFF db1 t --where="a>900" --result-file=/client_tmp/t.sql
这条命令中,主要参数含义如下:
–single-transaction的作用是,在导出数据的时候不需要对表db1.t加表锁,而是使用START TRANSACTION WITH CONSISTENT SNAPSHOT的方法;
–add-locks设置为0,表示在输出的文件结果里,不增加” LOCK TABLES t WRITE;” ;
–no-create-info的意思是,不需要导出表结构;
–set-gtid-purged=off表示的是,不输出跟GTID相关的信息;
–result-file指定了输出文件的路径,其中client表示生成的文件是在客户端机器上的。
再使用这个命令 ...
MySQL自增主键
自增主键自增值存在哪?不同的引擎对于自增值的保存策略不同:
MyISAM引擎的自增值保存在数据文件中。
InnoDB引擎的自增值,其实是保存在了内存里(重启重新去找自增值的最大值max(id)),到了MySQL 8.0版本后,才有了“自增值持久化”的能力(将自增值的变更记录在了redo log中,重启的时候依靠redo log恢复重启之前的值)。
自增值修改机制在MySQL里面,如果字段id被定义为AUTO_INCREMENT,在插入一行数据的时候,自增值的行为如下:
如果插入数据时id字段指定为0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT值填到自增字段;
如果插入数据时id字段指定了具体的值,就直接使用语句里指定的值。
根据要插入的值和当前自增值的大小关系,自增值的变更结果也会有所不同。假设,某次要插入的值是X,当前的自增值是Y。
如果X<Y,那么这个表的自增值不变。
如果X≥Y,就需要把当前自增值修改为新的自增值。
其中,auto_increment_offset 和 auto_increment_increment是两个系统参数 ...
MySQL内部临时表的运用
内部临时表的运用Union例如:(select 1000 as f) union (select id from t1 order by id desc limit 2);
这个语句的执行流程是这样的:
创建一个内存临时表,这个临时表只有一个整型字段f,并且f是主键字段。
执行第一个子查询,得到1000这个值,并存入临时表中。
执行第二个子查询:
拿到第一行id=1000,试图插入临时表中。但由于1000这个值已经存在于临时表了,违反了唯一性约束,所以插入失败,然后继续执行;
取到第二行id=999,插入临时表成功。
从临时表中按行取出数据,返回结果,并删除临时表,结果中包含两行数据分别是1000和999。
如果把上面这个语句中的union改成union all的话,就没有了“去重”的语义。这样执行的时候,就依次执行子查询,得到的结果直接作为结果集的一部分,发给客户端。因此也就不需要临时表了。
group by例如:select id%10 as m, count(*) as c from t1 group by m;
这个语句的执行流程是这样的:
创建内存临时表,表里有 ...
MySQL优化JOIN语句
Join优化被驱动表使用了索引的情况例如这个简单的join语句select * from t1 straight_join t2 on (t1.a=t2.a);
在这条语句里,被驱动表t2的字段a上有索引,join过程用上了这个索引,因此这个语句的执行流程是这样的:
从表t1中读入一行数据 R;
从数据行R中,取出a字段到表t2里去查找;
取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分;
重复执行步骤1到3,直到表t1的末尾循环结束。
在这个join语句执行过程中,驱动表是走全表扫描,而被驱动表是走树搜索。
假设被驱动表的行数是M,驱动表的行数是N。每次在被驱动表查一行数据,要先搜索索引a,再搜索主键索引。每次搜索一棵树近似复杂度是以2为底的M的对数,记为log2M,所以整个流程近似复杂度是 N + N2log2M。
这个算法称为**Index Nested-Loop Join(NLJ)**。
显然,N对扫描行数的影响更大,因此应该让小表来做驱动表。
Multi-Range Read优化(需要设置set optimizer_switch="mrr_cost ...
MySQL误删数据怎么办
误删数据怎么办误删行如果是使用delete语句误删了数据行,可以用Flashback工具通过闪回把数据恢复回来。
Flashback恢复数据的原理,是修改binlog的内容,拿回原库重放。而能够使用这个方案的前提是,需要确保binlog_format=row 和 binlog_row_image=FULL。
需要说明的是,我不建议你直接在主库上执行这些操作。
恢复数据比较安全的做法,是恢复出一个备份,或者找一个从库作为临时库,在这个临时库上执行这些操作,然后再将确认过的临时库的数据,恢复回主库。这是因为,一个在执行线上逻辑的主库,数据状态的变更往往是有关联的。可能由于发现数据问题的时间晚了一点儿,就导致已经在之前误操作的基础上,业务代码逻辑又继续修改了其他数据。所以,如果这时候单独恢复这几行数据,而又未经确认的话,就可能会出现对数据的二次破坏。
事前预防
把sql_safe_updates参数设置为on。这样一来,如果我们忘记在delete或者update语句中写where条件,或者where条件里面没有包含索引字段的话,这条语句的执行就会报错。
代码上线前,必须经过SQL审计。
...
MySQL备库并行复制策略
备库并行复制策略按表分发按表分发事务的基本思路是,如果两个事务更新不同的表,它们就可以并行。因为数据是存储在表里的,所以按表分发,可以保证两个worker不会更新同一行。
每个事务在分发的时候,跟所有worker的冲突关系包括以下三种情况:
如果跟所有worker都不冲突,coordinator线程就会把这个事务分配给最空闲的woker;
如果跟多于一个worker冲突,coordinator线程就进入等待状态,直到和这个事务存在冲突关系的worker只剩下1个;
如果只跟一个worker冲突,coordinator线程就会把这个事务分配给这个存在冲突关系的worker。
按行分发要解决热点表的并行复制问题,就需要一个按行并行复制的方案。按行复制的核心思路是:如果两个事务没有更新相同的行,它们在备库上可以并行执行。显然,这个模式要求binlog格式必须是row。
按行复制和按表复制的数据结构差不多,也是为每个worker,分配一个hash表。只是要实现按行分发,这时候的key,就必须是“库名+表名+唯一键的值”。(如果存在唯一索引也需要加上唯一索引)
按行分发的策略有两个问题:
...
MySQL保证高可用
Mysql的高可用主备延迟所谓主备延迟,就是同一个事务,在备库执行完成的时间和主库执行完成的时间之间的差值。
你可以在备库上执行show slave status命令,它的返回结果里面会显示seconds_behind_master,用于表示当前备库延迟了多少秒。
主备延迟的来源:
有些部署条件下,备库所在机器的性能要比主库所在的机器性能差。
第二种常见的可能了,即备库的压力大:由于主库直接影响业务,大家使用起来会比较克制,反而忽视了备库的压力控制。结果就是,备库上的查询耗费了大量的CPU资源,影响了同步速度,造成主备延迟。
解决方式:
一主多从。除了备库外,可以多接几个从库,让这些从库来分担读的压力。
通过binlog输出到外部系统,比如Hadoop这类系统,让外部系统提供统计类查询的能力。
第三种,大事务。如果一个主库上的语句执行10分钟,那这个事务很可能就会导致从库延迟10分钟。
例如:
一次性地用delete语句删除太多数据
大表DDL
主备切换可靠性优先策略:
在图中的双M结构下,从状态1到状态2切换的详细过程是这样的:
判断备库B现在的seconds_ ...
MySQL保证主备一致
保证主备一致
在状态1中,虽然节点B没有被直接访问,但是我依然建议你把节点B(也就是备库)设置成只读(readonly)模式。这样做,有以下几个考虑:
有时候一些运营类的查询语句会被放到备库上去查,设置为只读可以防止误操作;
防止切换逻辑有bug,比如切换过程中出现双写,造成主备不一致;
可以用readonly状态,来判断节点的角色。
同步完整流程:
一个事务日志同步的完整过程是这样的:
在备库B上通过change master命令,设置主库A的IP、端口、用户名、密码,以及要从哪个位置开始请求binlog,这个位置包含文件名和日志偏移量。
在备库B上执行start slave命令,这时候备库会启动两个线程,就是图中的io_thread和sql_thread。其中io_thread负责与主库建立连接。
主库A校验完用户名、密码后,开始按照备库B传过来的位置,从本地读取binlog,发给B。
备库B拿到binlog后,写到本地文件,称为中转日志(relay log)。
sql_thread读取中转日志,解析出日志里的命令,并执行。
binlog的格式:
statement ...