# =============================
# 目标1:
# 1、测试顶级上下文中定义的实例变量的作用域
# =============================
# 结论
# 1、顶级上下文中定义的实例变量只能被main对象访问,因为实例变量暴露在toplevel的context中,self为main
#    相当于运行main.instance_eval{@toplevel_variate="toplevel variate"}
# 2、私有方法不属于instance_methods的集合,它属于private_instance_methods
# =============================

@toplevel_variate = "toplevel variate"

def my_method
  "@toplevel_variate in my_method which define in toplevel:#{@toplevel_variate}"
end

Object.class_eval do |variable|
  puts "Object can acccess @toplevel_variate:#{@var}" # nil
end

self.instance_eval do
  puts "@toplevel_variate in self object:#{@toplevel_variate}" # toplevel variate
end

puts my_method
puts "Object.private_instance_methods:" + Object.private_instance_methods(false).grep(:my_method).to_s # :my_method
puts "Object.instance_methods:" + Object.instance_methods(false).grep(:my_method).to_s #nil


# =============================
# 目标1:
# 1、类扩展:使用include,让模块中定义的方法分别插入类方法与实例方法中
# 2、obj实例对象调用extend,则模块中定义的方法为?
# 3、在模块中定义的实例变量能否被包含它的类的实例对象看到
# =============================
# 结论
# 1、通过重定义横块中的self.included方法,并对参数(receiver),使用关键字extend/include,分别向包含它的类中插入了
#    类方法(定义在ClassMethods模块中的方法)和实例方法(定义在InstanceMethod模块中)
# 2、obj实例对象调用extend,则模块中定义的方法为这个obj的单例方法,可以通过把方法定义在module中,让实例对象调用extend
#    来方便快速的增加自身的单例方法
# 3、可以。在模块中定义的实例变量可以被包含它的类的实例对象看到,因为被include的模块相当于当前类的父类,当前对象的
#    实例变量对其所有实例方法可见
# 4、extend方法的实质就是把module中定义的方法插入到对象的单件类中
# 5、调用MyClass.instance_methods(false)可以发现,在它的实例方法中并不包含实例方法instance_method,而MyClass
#    的实例对象可以调用这个方法,这正说明了类中include的模块(被代理类封装)充当这个类的父类。也验证了结论4
# =============================
lambda {
  module M
    module ClassMethods
      def class_method
        "i am a class method"
      end
    end

    module InstanceMethods
      def instance_method(from)
        @instance_variate = "i am a instance variate in M"
        "i am a instance method from:#{from}"
      end
    end

    def self.included(receiver)
      receiver.extend         ClassMethods
      receiver.send :include, InstanceMethods
    end
  end

  #<!-- more -->
  class MyClass
    include M

    def get_instance_variate_from_module(from)
      "#{@instance_variate} from: #{from}"
    end
  end

  obj = Object.new
  obj.extend M::InstanceMethods
  my = MyClass.new

  puts "================test1 OUTPUT================"
  puts MyClass.class_method
  puts my.instance_method("MyClass.new obj")
  puts my.get_instance_variate_from_module("MyClass.new")
  puts obj.instance_method("Object.new")
  puts obj.instance_eval("@instance_variate")
  puts "MyClass.instance_methods:"+MyClass.instance_methods(false).to_s
  puts "================test1 OUTPUT================"

  # ================test1 OUTPUT================
  # i am a class method
  # i am a instance method from:MyClass.new obj
  # i am a instance variate in M from: MyClass.new
  # i am a instance method from:Object.new
  # i am a instance variate in M
  # MyClass.instance_methods:[:get_instance_variate_from_module]
  # ================test1 OUTPUT================

}.call

# =============================
# 目标1:
# 1、Kernel模块内定义实例方法,哪些对象可以使用
# 2、Kernel的单件方法哪些对象可以使用
# =============================
# 结论
# 1、Kernel模块内定义实例方法,ruby的所有对象都可以使用,因为Kernel会被Mixin入Object,其实例方法(用def定义的),可以
#    被所有ruby对象使用(MyClass/"string")
# 2、Kernel的单件方法只能被其自己使用,其它类和方法都不能使用(MyClass/MyClass.new)
# =============================
lambda {
  module Kernel
    def self.im_singleton_method
      "i'm a singleton method of Kernel define in Kernel"
    end

    def im_instance_method(from="Kernel")
      "i'm a instance method define in Kernel and call from #{from}"
    end
  end

  class MyClass; end
  puts Kernel.im_singleton_method
  puts "Kernel.singleton_methods:" + Kernel.singleton_methods(false).grep(/im_singleton_method/).to_s
  puts "Kernel.im_instance_method:" + Kernel.instance_methods(false).grep(/im_instance_method/).to_s
  puts MyClass.new.im_instance_method("obj")
  puts MyClass.im_instance_method("MyClass")
  puts begin MyClass.im_singleton_method rescue "undefined method `im_singleton_method' for MyClass:Class" end
  puts begin MyClass.new.im_singleton_method rescue "undefined method `im_singleton_method' for #<MyClass:0x007fbb22813640>" end

  # ================test1 OUTPUT================
  # i'm a singleton method of Kernel define in Kernel
  # Kernel.singleton_methods:[:im_singleton_method]
  # Kernel.im_instance_method:[:im_instance_method]
  # i'm a instance method define in Kernel and call from obj
  # i'm a instance method define in Kernel and call from MyClass
  # undefined method `im_singleton_method' for MyClass:Class
  # undefined method `im_singleton_method' for #<MyClass:0x007fbb22813640>
  # ================test1 OUTPUT================
}.call

在sublime中用cmd+b运行代码时,其运行环境往往和你正在使用的rvm指定的ruby版本不一样,安装RVM switch for sublime插件可以较好的解决这个问题。

在安装后,你可以选择

  • RVM Auto Ruby:设置当前环境和你的RVM一致(最好在cmd下先用rvm指定ruby版本,再用cmd打开sublime)
  • RVM Default Ruby
  • RVM System Ruby

# =============================
# 目标1:
# 1、测试块中的代码什么时候被执行
# =============================
# 结论
# 1、只有在块被真正调用的时候才会被执行:对块调用方法yield/call
# 说明: 一般方法中都会用yield去调用块,使得传入方法的块中的代码马上执行,这难免会让人产生代码只要被放在do..end或{}
# 就会被马上执行的错觉。
# 下面的代码只有当去掉注释时,块中的代码才会被执行
# 2、调用yield后,就一定要传入块,否则就产生错误,除非在加入了block_given?判断
# =============================
lambda {
  def my_method(&block)
    # yield
    # block.call
  end

  my_method do
    lastname = "Caroline"
    puts lastname # =>这里不会有输出,除非去掉方法中的注释
  end
}.call

文章已经修改,之前的结论有误,现已经修改

# =============================
# 重要结论:
# 1、块绑定局部变量的时间:在块被定义的时开始绑定的,绑定局部变量只是名子绑定,块内绑定的同名局部变量的值还可以在块外被修改
#(在块绑定局部变量后,如果块外部的同名局部变量改变了其值,则块中绑定的同名变量的值会被改变)
# 以下测试的局部绑定都证明了绑定是从块被定义时开始的
# 2、注意那些在块中初始化的变量,是否这些变量也出现在块定义外部的前后,不同位置的同名局部变量会对块内的变量产生不同的影响
# =============================

# =============================
# 目标1:
# 测试方法中的局部变量firstname,lastname是否会影响到块中的被绑定的同名变量的值
# =============================
# 结论
# 1、块会绑定局部变量firstname,lastname,把它们以块的方式传到my_method方法
# 它们的值不会受到方法中同名变量的影响(闭包特性)
# =============================
lambda {
  def my_method
    firstname = "Maria"
    lastname = "Sharapova"
    yield "Hi! I'm"
  end
  firstname = "Max"
  lastname = "Black"
  myname = lambda { |say| puts "test1: #{say} #{firstname} #{lastname}" }
  my_method &myname #=> Hi! I'm Max Black
}.call

# <!-- more -->

# =============================
# 目标2:
# 1、测试块可以绑定局部变量的范围
# 2、在块后面出现的局部变量是否会覆盖其块内已经绑定的同名局部变量的值(块中已经绑定的局部变量的值是否会被改变)
# =============================
# 结论
# 1、块会绑定在它定义时出现在它前面的局部变量firstname,lastname.
# 2、块中last变量的值为lastname="wang",因为块在定义时绑定了lastname,块中同名的lastname对上下文中的其它成员
# 是可见的,所以对局部变量赋值lastname="Wang",也就改变了块内lastname的值。它覆盖了前面lastname="Black"的值。
# 3、局部变量lastname="Green"出现在块被调用以后,块调用在赋值语句之前执行,对lastname="Green"的赋值不会影响到
# 块内部的同名变量lastname,此时块内部的lastname="wang"
# =============================
lambda {
  def my_method
    firstname = "Maria"
    lastname = "Sharapova"
    yield "Hi! I'm"
  end
  firstname = "Max"
  lastname = "Black"
  myname = lambda { |say| puts "test2: #{say} #{firstname} #{lastname}" }
  lastname = "Wang"
  my_method &myname #=> Hi! I'm Max Wang
  lastname = "Green"
}.call

# =============================
# 目标3:
# 测试在块中定义的已经初始化的变量的值是否受外部的同名局部变量值的影响:(局部变量的定义在块定义的前面)
# =============================
# 结论
# 1、局部变量不能影响块中同名变量的值
# 2、局部变量的值会随块中同名变量的值在执行过程中的改变而改变,day_changed的值为被改变的值:Mon
# 3、发生1、2情况的原因是,在块被执行时,它会先看到块内的say="Mon",而不再去看被绑定的局部变量say的值("Sun")
# 因为局部变量day已经被绑定到块,块中的day对其上下文可见,在块执行后,块中同名变量day的值会改变局部
# 变量day的值,此时局部变量day的值为"Mon"
# =============================
lambda {
  def my_method
    firstname = "Maria"
    lastname = "Sharapova"
    yield "Hi! I'm"
  end
  firstname = "Max"
  lastname = "Black"
  day = "Sun"
  myname = lambda { |say| day = "Mon"; puts "test3: #{say} #{firstname} #{lastname} at #{day}"}
  #day = "Sun"
  my_method &myname #=> Hi! I'm Max Black at Mon
  day_changed = day
  puts "test3: day:#{day_changed}"
  # day = "Wed"
  # my_method &myname #=> Hi! I'm Max Black at Mon
  # day_changed = day
  # puts "test3: day:#{day_changed}"
}.call

# =============================
# 目标4:
# 测试在块中定义的已经初始化的变量的值是否受外部的同名局部变量值的影响:(局部变量的定义在块定义的后面)
# =============================
# 结论
# 1、最终的值是块中变量的值,局部变量不能影响块中同名变量的值
# 2、局部变量的值会不随块中同名变量的值在执行过程中的改变而改变,day_changed的值:Sun
# 3、发生1、2情况的原因是:因为day局部变量的定义出现在块定义的后面,这就导致了块根本没有绑定day这个局部变量
# 块内的变量day是块内部附加的变量,此时块内部的day与块外部的day没有任何关系
# =============================
lambda {
  def my_method
    firstname = "Maria"
    lastname = "Sharapova"
    yield "Hi! I'm"
  end
  firstname = "Max"
  lastname = "Black"
  # day = "Sun"
  myname = lambda { |say| day = "Mon"; puts "test4: #{say} #{firstname} #{lastname} at #{day}"}
  day = "Sun"
  my_method &myname #=> Hi! I'm Max Black at Mon
  day_changed = day
  puts "test4: day:#{day_changed}"
  # day = "Wed"
  # my_method &myname #=> Hi! I'm Max Black at Mon
  # day_changed = day
  # puts "test5: day:#{day_changed}" # => Web
}.call

So each redo log record has a type. A redo log record type helps to identify the function that will be used to apply or execute the redo log during recovery. The contents of the redo log record must then contain all the arguments or parameters needed by the function

redo日志应首先持久化在磁盘上,然后事务的操作结果才写入db buffer,(此时,内存中的数据和data file对应的数据不同,我们认为内存中的数据是脏数据),db buffer再选择合适的时机将数据持久化到data file中。这种顺序可以保证在需要故障恢复时恢复最后的修改操作。先持久化日志的策略叫做Write Ahead Log,即预写日志,也叫日志先行。

数据不会被删除,除非page中的数据被重新组织

Of course actually deleting the record still doesn’t actually delete it — it just links the record into a “garbage” list, where record space may be reused. However nothing is guaranteed to be really removed from a page until the page is re-organized.

mysql undo日志写入磁盘,以512B写入磁盘,这样就保证了写入的原子性,因为磁盘扇区的size为512B,所以写入要么成功,要么失败,不会发生只写入部分写入的情况,如只写入了100B,这种情况是绝对不会发生的。

mysql当前读与快照读如何产生?

select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values ();
update table set ? where ?;
delete from table where ?;

-- 所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

在READ COMMIT与REPEATABLE-READ隔离级别下,在读取时由于使用了非锁定一致性读,所以在读取数据时是不加读锁的(读取的是snapshot数据,所以不用加锁)。由于没有加锁,所以自然不用等待其它事务锁资源的释放。从而提高了并发性。

在日志文件中的操作记录应该具有幂等性。幂等性,就是说同一个操作执行多次和执行一次,结果是一样的。例如,51 = 5111,所以对5的乘1操作具有幂等性。日志文件在故障恢复中,可能会回放多次(比如第一次回放到一半时系统断电了,不得不再重新回放),如果操作记录不满足幂等性,会造成数据错误。

innodb启动日志

23:24:30 28434 [Note] Plugin 'FEDERATED' is disabled.
23:24:30 28434 [Note] InnoDB: The InnoDB memory heap is disabled
23:24:30 28434 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
23:24:30 28434 [Note] InnoDB: Compressed tables use zlib 1.2.3
23:24:30 28434 [Note] InnoDB: Using CPU crc32 instructions
23:24:30 28434 [Note] InnoDB: Initializing buffer pool, size = 128.0M
23:24:30 28434 [Note] InnoDB: Completed initialization of buffer pool
23:24:30 28434 [Note] InnoDB: Highest supported file format is Barracuda.
23:24:30 28434 [Note] InnoDB: 128 rollback segment(s) are active.
23:24:30 28434 [Note] InnoDB: Waiting for purge to start
23:24:30 28434 [Note] InnoDB: 5.6.13 started; log sequence number 1287659157
23:24:30 28434 [Note] Server hostname (bind-address): '127.0.0.1'; port: 3306
23:24:30 28434 [Note]   - '127.0.0.1' resolves to '127.0.0.1';
23:24:30 28434 [Note] Server socket created on IP: '127.0.0.1'.
23:24:30 28434 [Note] Event Scheduler: Loaded 0 events

-- 可以获取不少有用的信息,如file format is Barracuda.说明了支持的行格式的兼容情况。

innodb 锁处理
当两个事务对同一资源时行修改操作时,会对这一资源加上X锁。b的原始值为1140

T1: update t_lock set b = 1143 where id = 1;没有执行commit
T2: update t_lock set b = 1145 where id = 1;操作会因为执行超时而抛出异常:

Lock wait timeout exceeded; try restarting transaction

# 因为T2在等待T1执行commit,释放资源。因为T1一直没有commit,所以导致了T2的超时。这里可以看做是T1被T2阻塞了。

看到网上的例子都是用50%的填充因子来举例的,这样举例具不太容易让人明白。下面是我对填充因子的理解

什么是填充因子

设置数据要占用page空间的比例。比如设置70%,即数据至少要填充page空间的70%。

设置填充因子的主要作用是为新数据预留一定的空间,当有新数据时,可以插入到预留的空间里,从而避免分页的发生。

Read more »

1.Start at the root page of the index.
2.Binary search using the page directory (repeatedly splitting the directory in half based on whether the current record is greater than or less than the search key) until a record is found via the page directory with the highest key that does not exceed the search key.
3.Linear search from that record until finding an individual record with the highest key that does not exceed the search key. If the current page is a leaf page, return the record. If the current page is a non-leaf page, load the child page this record points to, and return to step 2.

查找过程如下:

Read more »

There is one thing of note for secondary index non-leaf pages: the clustered key fields (PKV) are included in the record and is considered part of the record’s key, not its value. Secondary indexes may be non-unique, but each record in the page must have a unique identifier, so the PKV must be included in the record to ensure uniqueness. This will mean that records in non-leaf pages of secondary keys will be 4 bytes larger than their leaf page counterparts