多版本并发控制
MVCC在InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读
当前读和快照读
快照读读取的是之前版本的信息,利用不同版本间的信息从而解决并发读的问题
当前读读取的是最新的数据
MVCC的实现原理
隐藏字段
DB_TRX_ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本(存储在rollback segment里)
DB_ROW_ID:隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
Undo log
ReadView
creator_trx_id:创建这个ReadView的事务ID
trx_ids:表示在ReadView时,当前系统中活跃的事务ID
up_limit_id:活跃的事务中最小的事务ID
low_limit_id:表示生产ReadView时系统中下一个事务应该分配事务ID
解决幻读问题
当我们开启一个事务的时候,进行第一次查询操作时就会生成一个ReadView试图,即当前快照
之后的所有的数据都是按照快照中存储的数据来的
这里需要注意除非规定在开启事务是进行事务生成,不然正常说话应该是在第一次查询时生成
常见的问题
为啥有时候会查询的很慢(这里只谈因为MVCC影响的)
有两个客户端a和b
这个时候有一个线程给1加了10000次1
a查询的是当前读,耗时极低
但是b查询的是快照读,就需要往前翻10000次,速度就很慢
MVCC可以解决幻读问题吗?
首先要知道MVCC是无法解决幻读问题的
只有配合间隙锁才能解决
主从复制
作用
读写分离:提高了并发性
数据备份
高可用性:当主机崩溃时从机可以及时顶上去
原理
三个线程
主库线程:二进制日志转储线程,将二进制日志发给从库
从库线程:IO线程,从库向主库发送请求更新binlog日志后,从库可以通过IO线程读取到主库的二进制日志转储现场发送的binlog更新部分,并拷贝到本地的中继日志
从库线程:SQL线程,从中继日志中读取并更新数据,将从库的数据于主库保持同步
同步数据一致性问题
要求
主从数据一致
写数据必须去写库
读数据则不一定要到读库
主从延时问题的根源
备库接受binlog和执行完这个事务之间的时间差
最常见的表现:从库消费中继日志(relay log)的速度,比主库生产binog的速度要慢
减少延迟的方法
降低多线程大事务并发的概率,优化业务逻辑
优化SQL,减少慢查询和减少批量操作
提高从库的机器配置
尽量采用短的链路,即减少从库和主库的距离,提升端口宽带,降低binlog传输的网络延迟
实时性要求的业务读强制走出库,从库只做备份。
解决一致性问题(从弱到强)
异步复制
当客户端提交完commit后,不需要主库进行返回结果,而是直接将结果返回给客户端
问题就是可能会导致数据一致性弱的情况
半同步复制
当客户端提交commit后,至少需要一个从机进行应答后才能返回给客户端
数据一致性会比之前好一点点,但是降低了主库键的读写效率
组复制
将多个节点共同组成一个复制组,在执行读写事务的时候需要通过一致性协议层的同意,也就是说对于一个存在写的事务如果想提交,必须要经过组内大多数人的统一(N/2+1),而不是发送放自己说的。
换句话将,就是组内的每个节点都是一台可以独立完成事务的服务器,只是对于RW事务在提交的时候,需要使用广播的形式向周围的组进行通知。告诉别的节点执行了那些变化,然后进行数据的同步。这样子就保证了数据的一致性。但是如果发生了并发情况,那么由组进行调整,按照先后顺序为主。
如何保证主备一致
我们都知道主机和备机之间是通过binlog日志进行通信的,即主机完成操作后,将对应的binlog日志传递给备机,之后备机读取到本地变成了中继日志,然后操作中继日志来更新操作
那么主备一致性就是在研究binlog日志到底存的啥,以及为啥有三种格式
statement
这种格式就是将我们完整的sql语句以及一些其他的信息保存下来
缺点对于多条件且有索引的范围查询第一条语句,可能会因为选择索引不同而导致数据不一致问题
row
这种格式是将我们修改的数据进行保存,即数据的主键是啥,以及修改的内容是啥
解决了statement的遗留问题,但是对于批量的操作,我们会生成n条数据,恢复和修改不方便,且文件比较大
mixed
他就是将上面两种格式进行了一个结合,在可能产生歧义的地方就使用row格式,其他地方用statement
但是现在越来越多的情况是将binlog设置为row格式,原因是row格式容易恢复数据
对于一些SQL语句中的函数操作,例如now函数,mixed会使用statement格式,只是说使用set timestamp命令锁死数据
在我们日常的使用中,其实并不是说使用的一主一从的这种单向关系,而是互为主备关系,这样子方便于切换,不用修改相关的信息,但是也会产生问题
循环复制问题
假设A主机和B主机,当我在A上有一个操作,生成对应的binlog,B拿到后也会进行更新操作,也有binlog,此时A作为B的备机又收到了B的binlog,继续操作,以此类推(建议将log_salve-updates=on,备机在进行操作时也会生成日志)
所以现在就是有一个属性叫做server_id,他记录的是客户端连接进行操作的主机id,且互为主备的机器的id不能一致
假设在A上操作,在有binlog日志的时候,他的server_id=1,然后传递给B之后,他的server_id是不允许变得,还是1,当Bbinlog传递到A的时候发现和A的id一致,就放弃操作
但是还有可能产生循环复制问题
A和B单向的,B和C是双向的,就会造成循环复制
修改了server_id值,就会产生B的binlog传回后发现不一致继续操作
索引
索引的数据结构
B+树
B树和B+树的区别
双向链表:范围查询
聚簇索引叶子节点即数据,以及什么时候回表
hash索引:虽然快,但是只能进行精确查询,可以在特殊情况下使用
索引的类型
非聚簇索引
普通索引
联合索引(最左前缀,索引覆盖)
索引是越多越好吗
答案是不一定的,因为索引有一定的空间浪费
对于查询语句而言,索引多了可能会导致优化器选择错索引,从而导致速度下降
对于修改语句而言,由于需要进行值的修改,那么就可能会存在着分裂,从而降低修改效率
那么对于大量数据的批量修改或者删除,我们一般建议删除无用索引来保证效率。
关于慢查询问题
1,打开慢查询日志:set global slow_query_log='ON'
2,查看慢查询日志:show varibales like 'slow_query_log%'
3,利用MySQL自带的慢查询分析工具进行查找慢查询的SQL:mysqldumpslow -s r -t 10 /var/lib/mysql/慢查询日志名 表示返回记录集最多的10个SQL
4,使用explain 慢查询语句分析走了哪个索引,或者有没有走索引,为什么。
如何给字符串添加索引
添加完整索引
字符串一般情况下内存 较大,查询效率低,如果要使用最好索引覆盖
前缀索引
进行截取前面的几个值,但是需要注意区分度的问题
倒序存储
其实就是将正常的字符串进行倒序然后截取
例如身份证就可以这样子搞
使用hash字段
给一个值进行哈希计算,然后进行查找这个哈希值。
hash和倒序的区别
都不能进行范围查询
倒序没有利用多余的空间,而hash则是需要创建一个多余的列
hash的查询更加稳定一点
索引选错的原因
MySQL的索引选择是由优化器去进行结果预测,选择一个最好的索引,而扫描行数的是影响决定因素之一。但是由于他的特殊采样技术,一般索引统计值和真实情况是大差不差的。
优化器还需要关注回表的代价,所以这里就是他出问题的一个原因。
举个例子,事物A生成了一个read view,然后事物B要进行删除数据时,只能逻辑删除,因为事物A还需要使用,这样子导致了计算表的数据和我们想的不是同一个,从而进行索引的选择错误,我们可以使用analyze进行修正。
解决方案
自己写SQL的时候主动引导优化器进行选择索引
如果是统计错误的话,可以使用analyze来进行
也可以通过强制使用索引解决 forcel index来完成
为啥一样的查询逻辑,而结果却相差甚远
其本质就是没有使用到索引
有以下一些情况
对于一些简单的操作,我们最好用自己写条件代替使用函数
数据的隐式转化,如比较的数据不是同一个类型
创建表时隐式字符编码转化