[Mysql]漫游bin log

binlog是server层的日志,对于innodb来说,只有binlog写完后,才能提交redo log。binlog记录逻辑语句,只会记录写类似于sql的日志。binlog主要的作用 崩溃恢复 主从复制 组织结构 文件 binlog的相关配置可以执行show variables like '%bin%';查看 mysql> show variables like '%bin%'; +--------------------------------------------+---------------------------------+ | Variable_name | Value | +--------------------------------------------+---------------------------------+ | bind_address | * | | binlog_cache_size | 32768 | | binlog_checksum | CRC32 | | binlog_direct_non_transactional_updates | OFF | | binlog_error_action | ABORT_SERVER | | binlog_format | ROW | | binlog_group_commit_sync_delay | 0 | | binlog_group_commit_sync_no_delay_count | 0 | | binlog_gtid_simple_recovery | ON | | binlog_max_flush_queue_time | 0 | | binlog_order_commits | ON | | binlog_row_image | FULL | | binlog_rows_query_log_events | OFF | | binlog_stmt_cache_size | 32768 | | binlog_transaction_dependency_history_size | 25000 | | binlog_transaction_dependency_tracking | COMMIT_ORDER | | innodb_api_enable_binlog | OFF | | innodb_locks_unsafe_for_binlog | OFF | | log_bin | ON | | log_bin_basename | /data/mysql3306/mysql-bin | | log_bin_index | /data/mysql3306/mysql-bin....

May 26, 2020

[Mysql]漫游redo log

redo log负责记录物理数据页,所以无论执行多少次都是幂等的;而binlog是记录逻辑数据,执行多次就可能重复数据。 数据结构是一个环形数组,innodb将未写入磁盘的页叫做脏页,redo log的作用就是记录脏页的数据。 在宕机恢复时,一个事务是否持久化是根据redo log刷盘情况决定的。如果一个事务的redo log已经全部刷入磁盘,那么这个事务是有效的,反之需要根据undo log回滚。 redo log在硬盘中是分成多块来存储的,以ib_logfile[number]命名。 执行SHOW GLOBAL VARIABLES LIKE "innodb_log%";, +-----------------------------+-----------+ | Variable_name | Value | +-----------------------------+-----------+ | innodb_log_buffer_size | 67108864 | | innodb_log_checksums | ON | | innodb_log_compressed_pages | ON | | innodb_log_file_size | 536870912 | | innodb_log_files_in_group | 4 | | innodb_log_group_home_dir | ./ | | innodb_log_write_ahead_size | 8192 | +-----------------------------+-----------+ innodb_log_files_in_group指定了redo log文件被分为几部分,每一部分的文件大小都是一样的,当写入ib_logfile3后,又继续写入ib_logfile0,如此循环。 这是在磁盘里的文件, [root@dev_test63 mysql3306]# ll ib* -rw-r----- 1 mysql mysql 683671552 5月 20 01:25 ibdata1 -rw-r----- 1 mysql mysql 536870912 5月 20 01:24 ib_logfile0 -rw-r----- 1 mysql mysql 536870912 5月 19 11:10 ib_logfile1 -rw-r----- 1 mysql mysql 536870912 5月 20 01:24 ib_logfile2 -rw-r----- 1 mysql mysql 536870912 5月 20 01:25 ib_logfile3 -rw-r----- 1 mysql mysql 79691776 5月 20 01:01 ibtmp1 ibtmp1是临时表空间,ibdata1是共享表空间。 为什么要有redo log: redo log是顺序写入的,而数据页落盘是随机存储。 延迟刷脏页可以起到合并多次修改的作用,mysql的最小存储单位是页,一个页有多行,如果每次修改一行就要更新整个页,并不是那么能接收。 有了redo log之后,对于一行数据,首先更新buffer pool(在这之前还有undo log),然后再写入log buffer。...

May 19, 2020

[Mysql]漫游undo log

Innodb学会是不可能学会的,这辈子都学不会的。 innodb是一个日志先行(Write-ahead logging)的存储引擎,这也是大部分关系型数据库的特点。而像redis这样的nosql就是数据为先,再进行落盘。 undo log和redo log和binlog,这三个log是mysql及innodb的关键。这三种日志都会刷盘,其中: undo log: 事务原子性和多版本控制MVCC(事务隔离) redo log: 事务持久性,宕机恢复 binlog: 宕机恢复,主从同步 可以看出三个日志在对应功能上需要相互协作。undo log和redo log是事务日志;redo log要等binlog写入成功才能commit;undo Log保证事务的原子性,redo log保证事务的持久性。 网上大多都是讲undo log能做什么,但没几篇讲清楚undo log组织结构。innodb最小存储粒度是页page,而页就分为FIL_PAGE_INDEX索引页(索引即数据)和FIL_PAGE_UNDO_LOGundo页。 部分概念是关于MVCC的,需要配合[Mysql]Innodb的快照读实现食用,本文不做讨论。 undo log就是个历史版本,落盘后不和redo log存在一起。 表空间 InnoDB存储引擎提供二种数据库表的存储方式 系统表空间:所有数据上放在一起,物理文件可以拆成多个文件。 独占表空间:每个表有自己的物理文件,性能更好。 关于更多不在讨论范围。 结构层次 Rollback Segment(rseg)称为回滚段。Mysql5.6之前undo默认记录到系统表空间(ibdata),如果开启了 innodb_file_per_table ,将放在每个表的.ibd文件中。5.6之后还可以创建独立的undo表空间,8之后更是默认打开独立undo表空间,最低数量为2,这样才能保证至少一个undo表空间进行truncate,一个undo表空间继续使用。独立undo表空间的文件格式是undo001,undo002…… 每个rollback Segment中默认有1024个undo log segment,mysql5.5后1个undo表空间支持128个rollback Segment。0号rollback Segment默认在系统表空间ibdata中,1-32rollback Segment在临时表空间,33~128在独立undo表空间中(没有打开则在系统表空间ibdata中,这样系统表空间会太大),所以1个表空间最多支持96*1024个事务,超了就报错啦。 一个undo log segment称为undo log或undo slot或undo;一个undo log对象对应多个undo log record,也就是记录的历史版本。 一个undo log segment有一个page链表,undo log record就是放在page中的,当一个page不足以放下新的undo log record时,会分配新的page,放到链表尾部。 一个undo log segment其实是一个页叫undo log header page,有INSERT/UPDATE两个类型。这个页有一项内容TRX_UNDO_PAGE_LIST是一个链表,即undo page链表。 简单来说,结构是这样的: rollback segments(128) undo log segments(1024) undo page(N) undo record undo record … A collection of undo logs....

May 10, 2020

[网络]端口转发

之前买的GGC家的HK vps,从去年开始电信访问一直丢包,丢包率一上去,带宽再大速度也是提不上去([网络]TCP拥塞控制那些事)。 于是想到买个nat机中转一下流量。 (2核384内存,适合用来中转流量) iptables端口转发 nat到手后,机器镜像用的是centos7,关闭firewall,把iptables装上。 # 关闭 firewalld systemctl disable firewalld --now # 装iptables yum install iptables-services # 开机启动 systemctl enable iptables.service --now 接着开启ipv4转发,默认iptables是关闭ipv4转发 # 临时开启 ipv4 转发 echo 1 > /proc/sys/net/ipv4/ip_forward # 永久开启 ipv4 转发 echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf # 使生效 sysctl -p 接着是加iptables规则,需要在nat表加入一条DNAT和一条SNAT,DNAT是修改目的地址,转发流量到HK vps;SNAT是修改源地址,保证流量回到这台机上(只有这台机知道怎么回到我家)。 假设NAT机监听10086端口,HK vps的ip是104.104.104.104,ss服务端口是10010,NAT机内网地址是192.168.1.10 # DNAT iptables -t nat -A PREROUTING -p tcp -m tcp --dport 10086 -j DNAT --to-destination 104.104.104.104:10010 # SNAT,--to-source ip[:port] port可以不指定,会是随机的 iptables -t nat -A POSTROUTING -p tcp -m tcp -d 104....

May 1, 2020

[k8s]istio自动注入失败

查了我一天喵的 ...

February 27, 2020

[Mysql]Innodb的快照读实现

前言 有一次面试,面试官问我:mysql事务隔离级别有哪些? 我:balabala…… 面试官问:那可重复读是怎么实现的? 我:emm。。第一次读会有快照。。 面试官:嗯。。 我:。。。 然后呢,然后就不知道啦。 事实上,Innodb的RC和RR隔离级别下,读有**快照读(snapshot read)和当前读(current read)**之分,当前读就是SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE,快照读就是普通的SELECT操作。 快照读的实现,利用了undo log和read view。 快照读不是在读的时候生成快照,而是在写的时候保留了快照。 快照读实现了Multi-Version Concurrent Control(多版本并发控制),简称MVCC,指对于同一个记录,不同的事务会有不同的版本,不同版本互不影响,最后事务提交时根据版本先后确定能否提交。 但是,Innodb的读写事务会加排他锁,不同版本其实是串行的,所以首先要指出的是,Innodb事务快照读不是严格的MVCC实现。 实现 隐藏字段 Innodb每一行都有三个隐藏字段,分别是DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR。 DB_ROW_ID:如果表没有设置主键,用来作为记录的主键,因为Innodb使用聚簇索引的方式存储,记录必须有主键。 DB_TRX_ID:记录修改这行记录事务的id。 DB_ROLL_PTR:指向这行记录的前一个版本。 undo log 多版本其实是用undo log来实现的,undo log听起来是做回滚使用的,没错,但是事务提交后undo log可不会立刻清除,它作为历史版本存在着。只有当前没有事务依赖在这行记录上时,mysql的清理线程才会清理掉无用的undo log。 insert操作的undo log在事务回滚或提交后就会删除,因为只有回滚会用到。 update、delete的undo log需要保留(可见delete只是逻辑删除,其实也是个update操作,逻辑删除后由后台线程清理)。 下面假设有一条记录如下,column_1、column_2初始值为1和2。 假设这时有个三个事务ABC,A,C事务开始,对这条记录的查询结果如下: column_1 column_2 1 2 接着A事务执行更新column_1为11; 具体过程是:给记录加X锁,复制记录为undo_log_1,然后再将记录的column_1改为11,DB_TRX_ID为A,DB_ROLL_PTR指向前一个版本。 虽然数据行和undo log画的一样,但实际undo log有自己的数据结构。 A事务提交,释放X锁。 接着B开启事务,执行更新column_2为22; 具体过程是:给记录加X锁,复制A事务更新完的记录为undo_log_2,然后再将记录的column_2改为22,DB_TRX_ID为B,DB_ROLL_PTR指向前一个版本。 然后B事务提交。 注意!!如果A事务没有提交,X锁是不会释放的,那么B事务对这行记录执行update为了获取X锁会阻塞住的,而MVCC标准各个版本应该是不会相互影响的,所以说Innodb事务快照读不是严格的MVCC实现。 那么问题来了,C事务这时执行第二次查询,查询结果会是什么呢。 read view 光有多版本还不够,需要一个机制对undo log进行可见性判断,决定当前事务读到的是哪个版本,这个机制就是通过read view完成, read view是对当前系统中活跃的所有事务列表的封装,注意是所有事务,而不是作用于目标行的事务。 read view最早的事务id记为up_limit_id,最迟的事务id记为low_limit_id(low_limit_id = 未开启的事务id = 当前最大事务id+1),活跃事务id列表记为descriptors。...

December 11, 2019

[Redis]《Redis设计与实现》

前言 《Redis设计与实现》第二版基于redis3.0,现在6.0都快出了。现在redis和书里有些内容已经不一致了,不过用来探索redis是挺好的。 碎碎念 编码 现在的redis已经增加了quicklist、stream编码。 quicklist是ziplist和linkedlist的整合,作为list的唯一编码,其思想就是将ziplist分段,ziplist内存碎片少但每次操作都要申请内存,将ziplist分段,并用操作性能比较好的双向链表把段串起来,这算是时间与空间的折中。 stream编码用于消息队列,没有去了解。 字典 字典expand/resize是redis的一个大话题。 字典执行BGSAVE或BGREWRITEAOF时负载因子必须达到5才能进行扩容,执行BGSAVE或BGREWRITEAOF时不能进行缩容。 之所以,是因为BGSAVE或BGREWRITEAOF使用了copy-on-write,也就是写时复制。执行BGSAVE或BGREWRITEAOF时redis会fork子进程,这时候如果进行一个内存的拷贝(保证数据一致性),那么内存的浪费是很大的。使用写时复制,会将父进程的内存设置为只读,将内存和子进程共享,由于内存是分页机制,当某一页内存要发生写操作时,会发生中断,操作系统会把这一页内存复制出来进行修改。 因此,为了减少写操作导致内存页复制,redis才有了在上面的策略。 下个2的幂 redis在expand/resize都将新数组的长度设置为2的幂,这是因为把数组长度设置为2的幂,就可以把取模运算转化为位运算,java里也是这么做。 redis作者使用了这么一个算法来求给定一个数的下个2的幂 /* Our hash table capability is a power of two */ static unsigned long _dictNextPower(unsigned long size) { unsigned long i = DICT_HT_INITIAL_SIZE; if (size >= LONG_MAX) return LONG_MAX + 1LU; while(1) { if (i >= size) return i; i *= 2; } } 很简单的循环,不过有人给他提出可以用位运算:传送门,java里用的也是这个算法,HashMap的tableSizeFor()。 作者说不错,但是没必要,这种位运算的魔法对现实来说都是假的,只会把代码搞复杂😮 EMBSTR 书里的REDIS_ENCODING_EMBSTR支持最大长度39字节,而现在最大支持44字节,原因是3.2版本之后sdshdr变了。REDIS_ENCODING_EMBSTR使用sdshdr8来表示,原来的sdshdr需要8字节,现在使用sdshdr8只需要三字节,那么:44 + 1('\0')+ 3 + 16(robj) = 64,刚好是64字节,可以达到64字节内存对齐。...

December 3, 2019

[分布式]手撕raft

前言 Raft作为一个简单的一致性算法,实现一下还是挺好玩的。代码基于6.824 lab-raft,6.824是麻省理工的分布式课程的一个编号,里面有4个lab,第二个就是raft协议的实现,第三个是基于raft协议的kv存储设计,有待实现(oh我居然在做麻省理工的课程设计)。该lab要求使用go实现算法,并提供了一个具有故障模拟功能的RPC,即通过模拟网络,在单台机器我们就可以运行raft算法。 做实验前,你应该熟读raft论文,这里是中文版 实现 参照raft论文和lab提示,整体利用channel作为事件驱动、mutex保证线程安全,写出一个raft算法骨架还是比较容易的。不过在跑test的时候,小小的细节不对就会导致test failed。 raft-lab提供了17个test,检验了各种情况下的一致性,模拟了各种奇葩网络变化(网络变成这样还是跑路吧),要求4分钟内pass。 数据结构 参照论文,定义几个数据结构 const ( Follower = iota Candidate Leader HeartbeatInterval = 100 * time.Millisecond ) type ApplyMsg struct { CommandValid bool Command interface{} CommandIndex int } type LogEntry struct { Term int Command interface{} } type AppendEntriesArgs struct { Term int LeaderId int PrevLogIndex int PrevLogTerm int Entries []LogEntry LeaderCommit int } type AppendEntriesReply struct { Term int Success bool NextIndex int } type Raft struct { currentTerm int mu sync....

October 3, 2019

[k8s]k8s部署踩坑

前言 自己搭个k8s集群,踩了一些坑 镜像 kubeadm init命令会去k8s.gcr.io拉镜像,这个地址是得挂代理才能上的(可以指定地址忽略代理),可以用kubeadm config images pull尝试一下,十有八九是不行。不想挂代理的话,用下面这个方法。 先执行kubeadm config images list列出镜像,输出信息中有两行WARN是获取版本timeout可以不理会。 接着把列出来的信息放到下面的bash脚本中,运行脚本就把镜像下载好啦(其实就是从阿里云下镜像改tag)。 images=( # 下面的镜像应该去除"k8s.gcr.io"的前缀,版本换成上面获取到的版本 kube-apiserver:v1.15.3 kube-controller-manager:v1.15.3 kube-scheduler:v1.15.3 kube-proxy:v1.15.3 pause:3.1 etcd:3.3.10 coredns:1.3.1 ) for imageName in ${images[@]} ; do docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName done token和ca-cert-hash 在master进行kubeadm init后会输出token和ca-cert-hash,这个要记住,如果忘记了虽然可以执行kubeadm token list获取token,但是ca-cert-hash是不会输出的,忘记ca-cert-hash只能重新执行kubeadm token create从输出中拿到。 ip转发 在node主机执行kubeadm join的时候,报 [ERROR FileContent--proc-sys-net-ipv4-ip_forward]: /proc/sys/net/ipv4/ip_forward contents are not set to 1` 意思是没有开启ipv4转发,设置一下就好了:echo 1 > /proc/sys/net/ipv4/ip_forward 时间同步 在node主机执行kubeadm join的时候,一直卡住,加上--v=2可以输出详细信息,输出了一个信息 I0824 21:58:46.950161 16866 token....

August 25, 2019

[网络]在浏览器输入一个url到页面展现发生了什么

前言 被问起的时候总是两三句话就结束了。这一次想好好总结,描述我所知道的流程。 过程 DNS 向DNS发起请求,通常是udp协议,获得域名对应的ip地址。 查找顺序是:浏览器缓存 -> 操作系统缓存 -> 路由器缓存 -> ISP的DNS缓存 -> 根服务器 ARP 如果不是同一网络的地址,按照路由表找下一跳的ip,通过广播ARP请求获得下一跳mac地址,将报文发往此地址。 目标网络的网关接收到此报文后,同样发起ARP广播请求,寻找目标ip对应的mac地址,将报文发往此地址。 TCP三次握手 发送端随机选择一个端口和接收端端口之间发起三次握手,之后建立起TCP连接。 Linux执行sysctl -a|grep ip_local_port_range可以看到随机端口选择范围 MSS协商与TCP分段 MSS,Maximum Segment Size,TCP报文数据不能大于这个值,MSS = MTU - IP首部长度,20 - TCP首部长度,20 为了得出路径最小MSS,TCP一端设置IP报文DF标志(Don’t Fragment flag)告诉IP层不要分片,这样IP必须分片的时候,就会传回一个ICMP差错报文。 高级的ICMP差错报文会返回发生差错的MTU大小,如果ICMP差错报文没有带回MTU大小,需要发送端不断减少MSS并重发报文,得出合适的MSS。注意,一段时间后TCP会重新协商路径最小MSS,调整路径最小MSS。 将一个数据分组根据MSS拆成多个TCP报文,这就是TCP分段。 HTTP 建立起连接之后,发送HTTP报文。HTTP由请求行、请求头、请求主体组成。 请求行只有一行,由方法,path,协议版本,以一个\r\n结束。 请求头由多对key-value组织而成,以一个\r\n换行,以两个\r\n结束。 请求主体,包含请求的数据/响应数据。 http1.1版本加入了Connection:Keep-Alive,使得一个TCP连接可以复用多次,而不是一个请求建立起一次连接。 http2版本加入TCP多路复用。虽然http1.1版本可以复用TCP连接,但是一次只能发一个HTTP请求报文,想要并行发起多个请求,追能多建立TCP连接,而浏览器一般会限制并发6~8个连接,其余请求只能排队。加入TCP多路复用之后,减少了连接;此外http2采用了二进制传输,头部压缩大幅提高了性能。虽然二进制传输在调试过程不是很方便,但是调试工具都会帮我们转成明文格式展示。 更多信息http2可参见再谈HTTP2性能提升之背后原理—HTTP2历史解剖。本站也有启用http2。 HTTPS 如果启用用HTTPS,客户端会校验服务端的证书,根据证书和服务器协商一个对称加密算法和一个密钥,这一部分是RSA非对称加密,之后客户端和服务端会使用这个算法和密钥进行数据加密传输。HTTPS的原理出门左转TLS完全指南(一):TLS和安全通信,这文章讲了HTTPS的部分内容,主要内容是证书方面的内容;还有一部分内容看图解SSL/TLS协议,主要补充了用DH算法代替RSA进行密钥交换,避免了密钥在网络中传输。 TCP拥塞控制 TCP需要拥塞控制逻辑使用网络不好的情况,详见TCP拥塞控制那些事。 应用 关于应用层, 大多数是请求走cdn-> TCP四次挥手 发送端和接收端之间进行四次挥手断开连接,接着主动断开的端口进入FIN_WAIT_1,收到ACK报文后进入FIN_WAIT_2,收到接收端的FIN报文后最终会进入TIME_WAIT状态, 默认保持2MSL的不可用时间,防止相同五元组的连接建立后,收到上一代连接的重复报文,而产生混乱。被动断开的端口先进入CLOSE_WAIT,由服务器执行断开后发送FIN报文进入LAST_ACK状态,收到客户端ACK报文进入CLOSED状态。 后记 这个问题能反映出对计算机的网络的了解,其实算是一个能扯很久的问题。

August 21, 2019