Free To Feel

    年末总结(Dragon Year Wish List)

    开始慢慢享受除夕之夜了。最尽意的莫过于总结过去,展望明年。 今年除夕只是飘着小雪,但家乡依然比上海冷。 过去的一年我真的努力尝试去喜欢上海,去适应它。尽管为此我已经付出了代价。2011年,身体状况不佳,这个倒是给我提了一个很大的醒。 2011年10月份开始,着实让我体会到了忙,所有的事情交织在一块,忙的我正事都来不及做了。

    2011 完成的目标

    • 结婚,自己都觉得不好意思了;
    • 下半年装修;
    • 完成学车;
    • 回家把护照给办了;
    • 希望可以在工作中做的更多,得到更大的认可。
    • 和亲戚朋友多联系,2010年,我们联系的太少了;
    • 争取做一次说的过去的远徒旅行或者国外旅行一次 :-);
    • 多写点有价值的文章

    2011 未完成的事情

    • 上线一个新的产品, 如果有精力将其它几个产品打包上线 --
      事情多蹉跎,这个是我最想做的事情,在最后的几个月只能被搁置,完成率60%。
    • 在互联网上混的更实在,看到这领域太多的肤浅和吹嘘 --
      因为第一次定目标,此目标定的太虚。想的没有用实体表达出来,以至目前无法测量。
    • 寻找一种锻炼身体的方式,这个是最急切的任务 --
      尝试了去打羽毛球,也确实坚持了半年多,但最后还是级别太低,没能最后坚持下来。

    2012 TODO LIST:

    • 强身健体第一位,具体目标是锻炼腰部腹部肌肉,提高身体协调能力。
    • 职业发展:
      a. 空间的预测;
      b. 时间的估计,把持重点;
      c. Have a better idea about how to manage team and how to manage company.
    • 增加静心休息的时间。
    • 买车。
    • 读书>=15本;
      电影>=5;
    • 完成网站建设,其实现在已经不再是为了完成而完成了。练手;练脑;提高思维的整体性,连续性,可扩展性。
    • 蜜月履行。
    • 去年的时间管理基本满意,需要继续坚持下来。

    2012 WISH LIST:

    祝福我,我的家人,我认识的亲戚朋友身体健健康康!

    How And When To Use Mysql Federated Storage Engine

    Learned from mixi update that they are using mysql federated engine for db partitioning.

    Use FEDERATED TABLE from MySQL 5.
    Or do SELECT twice which is faster than using FEDERATED TABLEs
    

    Mysql Official document didn't talk too much about how and when to use federated engine. You can learned more from Mysql Federated storage engine 1 and Mysql Federated storage engine 2.

    Finally I find some clues in Mysql Federated storage engine 2:

    Strengths:
    
    For example, pretend you have a set of distributed servers working on small parts of a large task, and their results need to be merged back together when done without conflict. Many solutions to this problem involve modulo arithmetic for generating primary keys. This could be a good use of a FEDERATED table: just federate one central table on all the servers, have the processes INSERT into the table, and they’ll get non-conflicting primary key numbers. That’s a trivially easy way to coordinate distributed resource requests.
    
    The way it lets you mis-define tables holds great potential. For example, Giuseppe Maxia has already noted that you can define a FEDERATED table against a view. Views don’t have indexes (yet), but that shouldn’t stop you from telling the engine it does! That way, your WHERE clauses are sent through to the remote server unharmed, where the view can execute GROUP BY queries and the like. Giuseppe even outlines a way to get the remote server to execute arbitrary commands via a FEDERATED table!
    
    What about combinations with replication, triggers and so forth? There must be many more cool hacks waiting to be discovered.
    
    

    And in Accessing Distributed Data with the Federated Storage Engine, you can find a simple demo that 'merge' two partioned tables into one by creating a view. Although, this is not what I expected. It will still make sense in some cases.

    Still have some questions about how the performance looks like when use view? And what's the limitation of rows or table size when using federated storage engine?

    Form Ajax Submit With Jquery Form Plugin

    I can't remember how I make file ajax upload work last time. So a blog might be the best place to record it.

    Because XMLHttpRequest didn't support file upload, and I didn't want to spend much time to hack it. So it might be simple to find a plugin to do it. I compared several plugins supporting file ajax upload.

    At last I choose Jquery Form Plugin. Althrough it didn't sound like it supposed to be. Especially there is file field in your form. And the data format that client communicate with server is json

    So what does it look like?

    JS Client:

                                var options = {
                                    beforeSubmit: function(formData, jqForm, options) { 
                                      var queryString = $.param(formData);
                                        alert('About to submit: \n\n' + queryString); 
                                        
                                        for (var i=0; i < formData.length; i++) { 
                                            if (!formData[i].value) { 
                                              return false; 
                                        } 
                                        } 
                                    },
                                    success: function(data) {
                                            if(data && typeof data == 'object'){
                                                //...
                                            }
                                    },
                    url:       '/action', 
                    dataType:  'json',
                    clearForm: true
                  }; 
                  
                                $('#form').ajaxSubmit(options);
                              return false; 
    

    Symfony Backend:

    
            $json = json_encode($data);
            if(!$request->isXmlHttpRequest()){
              echo '<textarea>'.$json.'</textarea>';
              return sfView::NONE;
            }else{
            echo $json;
              return sfView::NONE;
            }
    

    You can check plugin document to know why you need 'echo "<textarea>"'. :-D

    Network Sharing Between Ubuntu And IPhone4(China Unicom Contract Version)

    Find a solution from Internet that can make network sharing between Ubuntu And IPhone4.

    sudo add-apt-repository ppa:pmcenery/ppa
    
    sudo apt-get update
    
    sudo apt-get install gvfs ipheth-dkms ipheth-utils
    

    If you can pass all these steps, and then restart your machine. Bingo!

    You can find more information here.

    Restore Lost Commits After Git Hard Reset

    Sometimes, you will make mistakes. But when someone told you there still is a hope that if you try xxxxx. What feeling will you have?

    The following might be one solution after 'git reset --hard' and realized you made a mistake:

      $ git reflog
      $ git merge 7c61179
    

    Check the blog post here.

    Replace MySql Table with NoSQL DB

    Backgroud:

    I am not nosql fans. I don't have the habit to replace whole mysql db with nosql db. But if a mysql table can be replaced with nosql db and expensive queries are generated againest this table, I will give a try of nosql DB.

    In our product, we have a unique user login log table to know how many users logined in a specify date. Here is the table structure:

      unique_login_logs: 
        _attributes:  {phpName: UniqueLoginLog}
        id:
        user_id: { type: integer }
        date: { type: date, index: true }
        created_at:
        _uniques:
          unique_date_type: [user_id, date]
    

    The expensive query looks like:

    select date, count(`user_id`) from unique_login_logs group by date order by date desc;'
    

    And the other related operations will be save an entry into this table or check the user entry for a specify date is in the table or not. Nothing more.

    Choose NoSQL DB from wiki page:

    ...
    Key-value cache in RAM
    
        Citrusleaf database
        memcached
        Oracle Coherence
        Redis
        Tuple space
        Velocity
    
    Key-value stores implementing the Paxos algorithm
    
        Keyspace
    
    Key-value stores on disk
    
        BigTable
        CDB
        Citrusleaf database
        Dynomite
        Keyspace
        membase
        Memcachedb
        Redis
        Tokyo Cabinet
        TreapDB
        Tuple space
        MongoDB
    ...
    

    For my feature request, I need a disk stored key-value system. I just spend 1 minute to take a look the MongoDB features. It meet my feature requirements. That's enough. If you know more about the other nosql DBs, of course you should choose the one which suit yours best.

    Try MongoDB

    Step 1: Install MongoDB(http://www.mongodb.org/display/DOCS/Quickstart+Unix);

    Step 2: Simple demo testing:

    create a new db:

    use mydb;
    

    Save an entry:

    db.uniqueLoginLog.save({date: '2011-06-09', user_id: 101});
    db.uniqueLoginLog.save({date: '2011-06-09', user_id: 111});
    db.uniqueLoginLog.save({date: '2011-06-09', user_id: 102});
    ..
    db.uniqueLoginLog.save({date: '2011-06-11', user_id: 100});
    db.uniqueLoginLog.save({date: '2011-06-11', user_id: 101});
    db.uniqueLoginLog.save({date: '2011-06-11', user_id: 102});
    

    Group entries by date:

    db.uniqueLoginLog.group(
               {key: { date:true },
                reduce: function(obj,prev) { prev.count ++; },
                initial: { count: 0 }
                });
                
    

    The output looks like:

    [
        {
            "date" : "2011-01-01",
            "count" : 3
        },
        {
            "date" : "2011-01-02",
            "count" : 3
        },
        {
            "date" : "2011-01-03",
            "count" : 1
        },
        {
            "date" : "2011-01-04",
            "count" : 1
        }
    ]
    

    With the output, I can do the order in client.

    Install PHP Mongo Extension

    You can install Mongo PHP extension with pecl command line or install it manually

    > pecl search mongo
    Retrieving data...0%
    Matched packages, channel pecl.php.net:
    =======================================
    Package Stable/(Latest) Local
    mongo   1.1.4 (stable)        MongoDB database driver
    
    > pecl install mongo
    

    Get stored items with php function

    PHP code:

    $m = new Mongo();
    
    $db = $m->mydb;
    
    $collection = $db->uniqueLoginLog;
    
    
    $obj = array('date'=> '2011-06-09', 'user_id'=> 101);
    $collection->insert($obj);
    
    
    
    $keys = array("date" => 1);
    $initial = array("count" => 0);
    $reduce = "function(obj,prev) { prev.count ++; }";
    $g = $collection->group($keys, $initial, $reduce);
    
    var_dump($g);
    
    

    Output looks like:

    array(4) {
      ["retval"]=>
      array(3) {
        [0]=>
        array(2) {
          ["date"]=>
          string(10) "2011-06-09"
          ["count"]=>
          float(17)
        }
        [1]=>
        array(2) {
          ["date"]=>
          string(10) "2011-06-11"
          ["count"]=>
          float(10)
        }
        [2]=>
        array(2) {
          ["date"]=>
          string(10) "2011-06-10"
          ["count"]=>
          float(7)
        }
      }
      ["count"]=>
      float(34)
      ["keys"]=>
      int(3)
      ["ok"]=>
      float(1)
    }
    

    Deploy to production?

    Until now, the demo is still a toy. If you want to deploy it to production, there are more work need to to be done. Take a look at the admin zone

    For my feature implemention, I will consider Replication next step.

    Summary:

    This is not a toturial about how to use MongoDB. It is just a example that can use nosql db in your production.

    Reference:

    When To Use Combine Index

    There are already a lot of posts which gave a introduction of what index merge is in MySQL. One of them is:

    Playing with MySQL's index merge

    Today I just noticed the combined index also can be affected by the single column index. For example:

    1. Let's say we have a table named table1 which have 100,000 items. The size of this table is 35MB.

    Table structure:

    CREATE TABLE `table1` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `status1` int(11) DEFAULT NULL,
      `status2` int(11) DEFAULT NULL,
      `status3` int(11) DEFAULT NULL,
      `status4` int(11) DEFAULT NULL,
      `status5` int(11) DEFAULT NULL,
      `status6` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `table1_name_unique` (`name`),
      KEY `table1_status1` (`status1`),
      KEY `table1_status2` (`status2`),
      KEY `table1_status3` (`status3`),
      KEY `table1_status4` (`status4`),
      KEY `table1_status5` (`status5`),
      KEY `table1_status6` (`status6`),
      KEY `table1_combine_index` (`status1`,`status2`,`status3`,`status4`,`status5`,`status6``)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    

    We have a query need to select items from table1 table with checking all the status columns in where clause. Let's do a explain:

    explain select * from table1 where status1=1 and status2=1 and status3=1 and status4=1 and status5=1 and status6=1  
    

    Explain result:

      select_type: SIMPLE
      table: table1
      type: index_merge
      possible_keys: PRIMARY,table1_status1,table1_status2,table1_status3,table1_status4,table1_status5,table1_status6,table1_combine_index
      key: table1_status1,table1_status2,table1_status3,table1_status4,table1_status5,table1_status6
      key_len: 1,1,4,4,4,5
      refs: NULL
      rows: 1955
      Extra: Using intersect(table1_status1,table1_status2,table1_status3,table1_status4,table1_status5,table1_status6); Using where
    

    The select query takes 3.5ms by intersecting indexes.

    What happened if we force index to use table1_combine_index:

    explain select * from table1 force index (table1_combine_index)  where status1=1 and status2=1 and status3=1 and status4=1 and status5=1 and status6=1  
    

    Explain result:

      select_type: SIMPLE
      table: table1
      type: ref
      possible_keys: table1_combine_index
      key: table1_combine_index
      key_len: 9
      refs: const,const
      rows: 62590
      Extra: Using where
    

    The select query takes 10.5ms by using combine index.

    In this example, intersecting index is more useful than combine index. There is an article give a more detailed explanation why it works like this:

    Multi Column indexes vs Index Merge

    1. Now it's time to make this example a little complicated.

    We have another table named table2, the structure looks like:

    CREATE TABLE `table2` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `table1_id` int(11) NOT NULL,
      `column1` datetime NOT NULL,
      `column2` varchar(255) NOT NULL,
      `column3` text,
      `column4` tinyint(4) NOT NULL,
      `column5 ` tinyint(4) NOT NULL,
      `column6 ` tinyint(4) NOT NULL,
      `column7` varchar(255) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `column7 ` (`column7 `),
      KEY `table2_FI_1` (`table1_id `)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    

    There are 20,000 rows in table2, with size 11MB. We have a feature request need to join table1 with table2 and sort by table2's id.

    SELECT sql_no_cache * FROM table2 LEFT JOIN table1 ON (table2.table1_id = table1.id) WHERE table1.status1=1 and table1.status2=1 and table1.status3=1 and table1.status4=1 and table1.status5=1 and table1.status6=1 ORDER BY table2.id DESC LIMIT 10;
    

    It takes 3s to finish the query. The explain tells you:

      select_type: SIMPLE
      table: table1
      type: index_merge
      possible_keys: PRIMARY,`table1_status1`,`table1_status2`,`table1_status3`,`table1_status4`,`table1_status5`,`table1_status6`,`table1_combine_index`
      key: `table1_status1`,`table1_status2`,`table1_status3`,`table1_status4`,`table1_status5`,`table1_status6`
      key_len: 1,1,4,4,4,5
      refs: NULL
      rows: 1955
      Extra: Using intersect(`table1_status1`,`table1_status2`,`table1_status3`,`table1_status4`,`table1_status5`,`table1_status6`); Using where
      
      ---
      select_type: SIMPLE
      table: table2
      type: ref
      possible_keys: table2_FI_1
      key: table2_FI_1
      key_len: 4
      refs: table1.id
      rows: 1
      Extra:  
    

    Now I want to remove these single column indexes in table1 to see what will happen:

      select_type: SIMPLE
      table: table2
      type: index
      possible_keys: table2_FI_1
      key: PRIMARY
      key_len: 4
      refs: 
      rows: 10
      Extra:
      
      ---
      
      select_type: SIMPLE
      table: table1
      type: eq_ref
      possible_keys: PRIMARY,table1_combine_index
      key: PRIMARY
      key_len: 4
      refs: table2.table1_id
      rows: 1
      Extra: Using where
    

    After removing the single column indexes in table 1, this select query only takes 2.4ms. To explain this, we can simply treat (status1,status2,status3,status4,status5,status6) as one column in table1. I think the single columns indexes affected the 'join' performance. MySQL need to use these single indexes to filter the table1 firstly and then make a join with table2.

    Summary:

    Combine index is not always better than index merge. To know which one is better, explain your query, analyze it and adjust the index.