杂记

    

    index_init

    index_end


    键值定位:

    index_read


    有key定位:

    index_read_idx

    index_read_last:获取给定键值的最后一行


   无key第一行或者最后一行定位:

    index_first:

      Positionsa cursor on the first record in an index and reads the corresponding row to buf

    index_last


   下一行:

    index_next

    index_next_same

   index_prev

    

   table scan:

    rnd_init

    rnd_next

    rnd_end


 tuple id scan

    rnd_pos:根据row reference fetch a row

 

隔离级别

 

 

RC

RR

SERIALIZABLE

不符合index key

LOCK_NO

LOCK_NO

NEXT KEY锁

不符合index filter

LOCK_NO

LOCK_NO

NEXT KEY锁

符合index key 删除项

LOCK_S后释放

LOCK_S后释放

NEXT KEY锁

符合所有index条件

加LOCK_U或者LOCK_S锁,如果是LOCK_S则下次扫描释放

加LOCK_U或者LOCK_S锁

 

加LOCK_U或者LOCK_S锁

 

不符合scan filter

 

如果是LOCK_S则释放

 

 

 

 

 

 

 

 

 


Memory Barrier

三种顺序:

Program order:代码执行的顺序

Execution order:内存操作指令执行的顺序

Perceived order:给定cpu或者其他cpu感知到内存操作的顺序

我个人理解前一种是静态顺序,取决于代码中的顺序或者编译器优化后的汇编代码顺序

而后两种的顺序为动态顺序,取决于执行时cpu cache的情况

 

OOE的原因:

1.编译器优化导致的乱序

2.指令执行乱序引起的乱序,比如功能单元未就绪或者操作数据未就绪,可能导致后面的指令不等待前面的指令完成就开始执行,比如load指令遇到cache miss或者遇见分支指令

3.Storebuffer/invalid buffer引起的乱序

 

乱序类型

读操作的顺序表示的所读到数据上次被写的时间,而不是指读这个动作所发生的时间

写操作有效的实际为store buffer flush

 

两个读指令本身的执行顺序发生了变化,另外也指读到数据的修改时间发生了变化

 

Init:a=0,b=0

 

写写乱序:两个写入操作执行的顺序与其对应store buffer flush的顺序不一致

Cpu0:

Store a=1

Store b=1

Flush b

Flush a

 

读读乱序: 两个读操作获取的数据与其上次修改store buffer flush的顺序不一致

假设a在cpu cache 0中,状态为M/E,b在cpu cache 0和1,状态均为S

Cpu0:

Store a=1

Flush a(modify cpu cache 0)

Store b=1

Flush b(invalid cpu cache1,仅修改invalid queue)

 

Cpu1

Load a(read from cpu cache 0)

Load b(read from cpu cache 1)

 

 

读读barrier:同步invalid buffer

写读barrier:先flush store buffer后同步invalid buffer

 

读写barrier:同步invalid buffer

写写barrier:flush store buffer

 

比如thread1的执行顺序

 

初始A=0且B=0,同时A在cpu 1的cache中,B在cpu2的cache中

Cpu1:

Store A=1;

Writer barrier

Store B=1;

 

Cpu2:

load A(A不在cache中,需要从cpu1中读取,则读到了1)

load B(可能cpu2并没有同步invalid buffer,则读到了0)

 

 

 

Cpu2:

CPU2:

CPU1:load A(A不在cache中,需要从cpu1中读取,则读到了1)

 

CPU1:store B(可能cpu2并没有同步invalid buffer,则读到了0)

 

Lock free使用场景

l  同步原语本身的开销过大时,考虑改用lock free

l  共享对象修改必须是原子操作

l  访问者不需要相互阻塞,比如生产者和消费者

l  必须考虑memory barrier

性能优化

         Perf:执行时间、cachemiss、branchmiss

         Systemtap

并发优化

l  同步原语优化减少同步操作本身的开销,比如lock free、原子加、latch/mutex的实现改进等

l  缩短临界区

n  减少临界区中的无用功或者提高临界区代码的执行效率

n  临界区拆分,锁拆分

l  减少临界区的争用

n  细化访问粒度:分区

u  客体分区:根据访问对象分区

u  主体分区:根据访问者分区

u  动态分区:多个分区由访问者直接获取可用资源

n  减少访问热点:分离

u  读写分离,将锁拆为多个私有读锁和一个全局写锁,读者的单点争用变为多点正争用,写者加全局写锁和所有读锁

u   

索引与MVCC

1.     索引是否应该记录undo日志?

在innodb中索引不会记录相应的undo日志,而oracle则会

对扫描的影响:保留所有的二级索引行版本,再判断对应的聚集索引行是否可见?

对更新的影响:能否根据聚集索引日志正确的回滚二级索引

 

 

 

聚集索引

l  插入(row_ins_clust_index_entry):如果找到键值相同项,则直接重用(row_ins_clust_index_entry_by_modify—>btr_cur_optimistic_update/ btr_cur_pessimistic_update——>TRX_UNDO_UPD_DEL_REC);否则向索引中插入(btr_cur_optimistic_insert/btr_cur_pessimistic_insert-à TRX_UNDO_INSERT_REC

l  删除(row_upd_del_mark_clust_rec):直接标记该行为删除行(btr_cur_del_mark_set_clust_rec->TRX_UNDO_DEL_MARK_REC

l  更新:如果排序键未变化,则直接更新原有行(row_upd_clust_recàbtr_cur_optimistic_update/ btr_cur_pessimistic_update –>TRX_UNDO_UPD_EXIST_REC),否则通过先删除后插入的方式实现更新(row_upd_clust_rec_by_insertà btr_cur_del_mark_set_clust_recà row_ins_clust_index_entry)

 

二级索引

l  插入(row_ins_sec_index_entry):如果找到所有键值的相同项,则直接重用(row_ins_sec_index_entry_by_modify—>btr_cur_optimistic_update/ btr_cur_pessimistic_update),否则指直接向索引插入(btr_cur_optimistic_insert/btr_cur_pessimistic_insert

l  删除:查找并删除改行(btr_cur_del_mark_set_sec_rec

l  更新(row_upd_sec_index_entry):二级索引的键值肯定发生了变化,先删除改行(btr_cur_del_mark_set_sec_rec),之后再插入(row_ins_sec_index_entry)。

 

Field与col的关系

索引只有field,而col是属于table

表的col包含了3个系统列

有主键的聚集索引的field为user_defined+2,unique为主键个数,包含两个系统列

无主键的聚集索引的field为user_defined+3,unique为1个,包含3个系统列

二级索引的field为user_defined+聚集索引的n_uniqu去掉重复项

 

1.  插入操作:

聚集索引

更新删除行(TRX_UNDO_UPD_DEL_REC)

插入新行(TRX_UNDO_INSERT_REC)

二级索引

    更新删除行

    插入新行

2.  删除操作:

删除聚集索引行(TRX_UNDO_DEL_MARK_REC)

删除二级索引行

3.  更新操作:

聚集索引

更新现有行(TRX_UNDO_UPD_EXIST_REC

分解为删除操作(2)和插入操作(1)

二级索引

不需要更新

  分解为删除操作(2)和插入操作(1)

举个简单的例子:

聚集索引列为(c1 primary key, c2 second  index key

OP1:插入(1,2)——>聚集索引行{12valid},二级索引行{21valid}

OP2:删除(1,2)——>聚集索引行{12invalid},二级索引行{21invalid}

OP3:插入(1,3)——>聚集索引行{13valid},二级索引行{21invalid),(31valid}

 

聚集索引更新引起的更新已有行

二级索引删除已有行后重用已有的删除行

Redo:将原有行标记为delete,并将可重用行标记为未删除

Undo:如果聚集索引行存在之前的有效版本与当前行相匹配,则标记为删除,否则直接删除改行,之后再将原有行标记为未删除行

二级索引删除已有行后插入新行:

Redo:将原有行标记为delete,将插入新行

Undo:该聚集索引行不可能存在之前的有效版本与当前行内容相同,因此当前行会被直接删除,后再重新标记原有行为未删除。

聚集索引插入引起的更新删除行

二级索引重用已有的删除行:

Undo:如果聚集索引行存在之前的有效版本与当前行相匹配,则仅将其标记为删除行,否则将其删除

二级索引插入新行:

Undo:该聚集索引行不可能存在之前的有效版本与当前行内容相同,因此当前行会被直接删除

 

 

 purge:

TRX_UNDO_DEL_MARK_REC

   

TRX_UNDO_UPD_DEL_REC

    如果不存在外存储,则不进行purge

TRX_UNDO_UPD_EXIST_REC

    如果二级索引和聚集索引键值列均未发生变化,则不进行purge

2.     如何防止幻读

Read repeat也不会出现幻读,

l  读语句通过事务起始readview来解决不可重复读和幻读问题

l  更新语句通过加锁来解决不可重复读问题,通过Next key来解决幻读问题

 

3.     索引的空闲空间如何回收:

TRX_UNDO_DEL_MARK_REC

TRX_UNDO_UPD_DEL_REC

TRX_UNDO_UPD_EXIST_REC

 

row_undo_mod_del_mark_or_remove_sec_low

如果指向的聚集索引列可以通过回滚得到有效且索引列相同的版本,则将当前版本标记为删除项,否则直接删除当前版本

如果回滚在上次更新事务purge之前:如果更早之前的

如果回滚在上次更新事务purge之后:purge时发现该行不能被purge,因此在此时需要将该行删除

 

Innodb buffer

 

搜索索引树时只有sec index的叶节点才会使用insert/del buffer

BUF_GET_IF_IN_POOL主要用于insertmarkdel操作

页面在buffer中,但未被watch,则直接该页面

页面不在buffer中或者页面已被watch,则返回NULL

BUF_GET_IF_IN_POOL_OR_WATCH主要用于删除索引行

页面在buffer中但未被watch,直接返回使用

页面已被其他线程watch,返回NULL

页面不在buffer中,从watch中分配页面,并返回NULL

如果buf_page_get_gen返回NULL,则尝试在insert/del buffer中更新

 

 

Innodb Trx

 

 

 

 

Mysql可重复读的幻读:

    读语句使用事务的readview来避免幻读

    而更新语句(包括for update等)通过next key来避免幻读

 

 

 

 

 

 

 

Mysql Sql

 

 

 

 

 

 

Mysql 查询执行

 

do_select

join->first_select(sub_select)

read_record

evaluate_join_record

join_tab->next_select(sub_select/sub_select_op/end_send)

    sub_select:从当前join_tab中获取元祖进行join

sub_select_op:put records,将数据存入join buffer

    end_send:发送数据

join->first_select end(sub_select)

join_tab->next_select end(sub_select/sub_select_op/end_send)

1.  sub_select: (next join_tab)->next_selectend

2.  sub_select_op:

    join_records(end_send)(BNL)

        join_matching_records

            read_recordfrom join_tab

            get_recordsfrom  pre join buffer

generate_full_extensions

check_match判断是否满足条件

join_tab->next_select(sub_select/sub_select_op/end_send)

        next_cache->join_records()

    sub_selectend

3.  end_send:直接返回

 

 

:

Oracleimu&prvs

 

如果开启了_log_private_parallelism,则说明使用zero copy redo,private strand中redo record的格式与磁盘存储格式相同,可直接从ps中写入磁盘,否则redo record的格式与log buffer中的格式相同

 

 

Waiting for alog switch because the log that the LGWR will be switching into has not beenarchived yet. Check the alert file to make sure that archiving has not stoppeddue to a failed archive write. To speed archiving, consider adding more archiveprocesses or putting the archive files on striped disks.

 

你可能感兴趣的:(杂记)