MySQL学习2–Linux下安装Mysql5.5数据库

Linux下安装Mysql5.5数据库
  1. 准备安装工具
    1. cmake:从Mysql5.5以后使用cmake编译,可以从www.cmake.org下载最新版本;
    2. GUN make:操作系统自带;
    3. gcc:操作系统自带;
    4. perl:操作系统自带;
    5. libncurses5-dev(ncurses-devel):运行cmake必须的包,如果没有安装会报错.
  2. 安装cmake:
    1. 解压压缩包:>tar -zxvf cmake-VERSION.tar.gz;
    2. 进入到cmake的解压缩目录,执行./configure命令生成makefile;
    3. 执行>make;make install;命令生成安装软件并安装cmake;
    4. 软件安装到了/usr/local/share/cmake-2.8/目录下,执行文件在/usr/local/bin目录下;
  3. 安装ncurses-devel插件
    1. 在Debian和Ubuntu上的包名是libncurses5-dev;
    2. 在RHEL和其它版本上是ncurses-devel,执行>rpm -ivh ncurses-devel-VERSION.rpm安装;
    3. 如果不安装会出现以下错误;                                                                           
  4. 创建mysql用户:>useradd mysql;                                 
  5. 解压缩mysql5.5的源码包:>tar -zxvf mysql-VERSION.tar.gz;
  6. 进入目录mysql-VERSION目录;
  7. 执行cmake命令生成makefile(MyISAM,MERGE,MEMBER和CSV四种引擎默认静态编译);
  8. 编译文件:>make;make install;mysql的安装目录下生成可执行文件,并自动创建了data文件(可以手动再创建一个logs目录,用来存放生成的日志文件,与数据目录不在同一块磁盘上,减小I/O并发),修改目录权限为mysql:>chown mysql:mysql data;                                      
  9. 拷贝配置文件:>cp ../mysql-VERSION/support-files/my-medium.cnf ./my.cnf                         
  10. 在配置文件下添加目录配置,指定数据文件的位置:                             
  11. 数据库的初始化,主要是数据库的创建,帮助文件的填充,用户文件的填充,执行:>./scripts/mysql_install_db –defaults-file=./my.cnf –user=mysql(在my.cnf配置文件中添加user参数,并且拷贝到/etc目录下就不用再加参数,执行>./scripts/mysql_install_db即可)           
  12. 启动服务器:>.bin/mysqld_safe –-user=mysql &;                         
  13. 修改MYSQL服务器root用户的密码:>./bin/mysqladmin –u root password ‘pwd’;            
  14. 登录:
    1. 如果没有设置root的密码,默认是空密码,使用>./bin/mysql就可以登录;
    2. 如果设置了root密码,则登录时要数据密码验证>./bin/mysql –uroot -p;
    3. 进入数据库后修改用户密码:update user set password=PASSWORD(‘123456′) where user=’root’;
  15. 把mysql添加到环境变量:
    1. 打开~root/.bash_profile文件;                                        
    2. 修改环境变量:>PATH=/usr/local/mysql/bin:$PATH,尽量把mysql的bin目录放在PATH的前面,使用mysql的工具的时候提高优先级,否则可能会使用系统预装的mysql的工具,造成版本不一致的错误,下图不准,修改时注意即可;                                                        
    3. 使环境变量立即生效:>. ./.bash_profile;                        
  16. 把配置文件放到默认读取的路径,并在配置文件中指定启动用户为mysql,添加开机启动:
    1. 拷贝文件到开机启动目录:cp /usr/local/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqld;
    2. 添加开机启动项:chkconfig –add mysqld;chkconfig mysqld on;
    3. 启动/关闭mysql服务:service mysqld start/stop;
— 编译参数;
/usr/local/bin/cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS:STRING=utf8,gbk \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_FEDERATED_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_READLINE=1 \
-DENABLED_LOCAL_INFILE=1 \
-DMYSQL_TCP_PORT=3306
如果要使用其它字符集,要在安装完成后改变字符集:mysql>set names ‘gb2312’;
charset:gb2312 — collation:gb2312_chinese_cs

MySQL学习1–Linux下安装Mysql5.1数据库

1.创建mysql用户:>useradd mysql
graphic
2.解压缩mysql的源码安装包:>tar –zxvf mysql-VERSION.tar.gz (如果要校验完整性使用md5sum mysql-VERSION.tar.gz 把得到的md5值比较)
graphic
3.创建安装mysql软件的目录:>mkdir /usr/local/mysql
4.进入mysql源码包目录,并编译源码生成makefile文件(查看gcc的版本,使用gcc –version, 查看configure的命令可以进入mysql-VERSION目录使用./configure –help | less命令):
./configure –prefix=/usr/local/mysql \   — 安装路径
–without-debug \                         — 使用非DEBUG方式编译
–enable-thread-safe-client \             — 允许以客户端线程安全方式编译
–enable-assembler \                      — 允许使用汇编字符串处理函数
–enable-profiling \
–with-mysqld-ldflags=-all-static \       — 静态编译,把函数放在执行程序中
–with-client-ldflags=-all-static \
–with-charset=latin1 \                    — 系统默认字符集
–with-extra-charsets=utf8,gbk \          — 编译安装字符集
–with-mysqld-user=mysql \                — 指定运行mysqld的用户
–without-embedded-server \
–with-server-suffix=snda \               — 个性化定制,在系统版本后的后缀
–with-plugins=innobase,partition         —存储引擎
graphic
5.编译文件:>make;make install;mysql的安装目录下生成可执行文件
graphic
6.创建数据文件,并修改目录权限为mysql:mysql:>mkdir data;chown mysql:mysql data
graphic
7.拷贝配置文件:>cp ../mysql-VERSION/support-files/my-medium.cnf ./my.cnf
graphic
8.在配置文件下添加目录配置,指定数据文件的位置
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
graphic
9./var/run文件夹下创建mysqld目录并属于mysql:mysql
>cd /var/run
>mkdir mysqld
>chown mysql:mysql mysqld
graphic
10.数据库的初始化,主要是数据库的创建,帮助文件的填充,用户文件的填充,执行:>./bin/mysql_install_db –defaults-file=./my.cnf –user=mysql
graphic
11.启动服务器:>.bin/mysqld_safe –-user=mysql &
graphic
12.修改MYSQL服务器root用户的密码:>./bin/mysqladmin –u root password ‘pwd’
graphic
13.登录:
    1.如果没有设置root的密码,默认是空密码,使用>./bin/mysql就可以登录
    2.如果设置了root密码,则登录时要数据密码验证>./bin/mysql –uroot -p
graphic
mysql的稳定级别
1.alpha:表明发行包含大量未被彻底测试的新代码
2.beta:意味着该版本功能是完整的,并且所有的新代码被测试了,没有增加重要的新特征,应该没有已知的缺陷.当appha版本至少一个月没有出现报导的致命漏洞,并且没有计划增加导致已经实施的功能不稳定的新功能时,版本从alpha变为beta版本.
3.re:是发布代表,是一个发行了一段时间的beta版本,看起来应该运行正常,只增加了很小的修复.
4.ga:如果没有后缀,这意味着该版本已经在很多地方运行一段时间了,而且没有非平台特性的缺陷报告.
Mysql源码目录,主要包括客户端代码,服务端代码,测试工具和其他库文件
1.BUILD:各种平台的编译脚本,可以用来制作各种平台的二进制版本
2.client:客户端目录(mysql.cc, mysqadmin.ccl)
3.docs:文档目录
4.storage:存储引擎目录,实现了handler抽象接口,主要包含一下目录:
     1.innobase
     2.myisam
     3.myisammrg
     4.heap
     5.cvs
     6.archive
     7.federated
     8.ndb
     9.blackhole
5.mysys:mysql为了实现跨平台对系统库封装
6.sql:数据库主程序目录(sql_insert.cc, sql_update.cc, ..),存储引擎接口(handler.cc, handler.h)

Redis学习10–Java Drivers

Java驱动实践
  1. Redis的客户端有主要有三种:JDBC-Redis, JRedis和Jedis,推荐Jedis的方式;
  2. Jedis API Online Help:http://www.jarvana.com/jarvana/view/redis/clients/jedis/2.0.0/jedis-2.0.0-javadoc.jar!/index.html;
  3. Jredis使用总结:
    1. pipeline:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you finish sending them;即pipeline适用于批处理,当有大量的操作需要一次性执行的时候,可以用管道;
    2. 分布式的id生成器:因为redis-server是单线程处理client端的请求的,所以可以使用jedis.incr(“id_key”)来生成序列;
    3. 分布式锁watch/multi:可以用来实现跨jvm的同步问题;
      1. 方法1:Jedis.setnx(key, value),推荐的方法;
      2. 方法2:事务multi;
      3. 方法3:事务+监听;
    4. redis分布式:jedis里面通过MD5,MURMUR Hash(默认)两种方式实现了分布式,也可以自己实现redis.clients.util.Hashing接口;
  4. Jedis中Pool的问题:It seems like server has closed the connection;
    1. 原因:redis-server关闭了此客户端的连接,server端设置了maxidletime(默认是5分钟),服务端会不断循环检测clinet的最后一次通信时间(lastinteraction),如果大于maxidletime,则关闭连接,并回收相关资源,client在向该连接中写数据后就会由于server端已经关闭而出现broken pipe的问题;
    2. 错误的配置:在spring初始化时获取一次实例化jedisCommands,而后每次的redis的调用时并未从pool中获取;
    3. 解决办法;

———————– Pipeline ———————–

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
 
public class Redis
{
     public static void main(String[] args)
     {
           // 连接Redis 服务器;
          Jedis jedis = new Jedis( “192.168.10.112”, 6379);
           // 生成Pipeline;
          Pipeline pipeline = jedis.pipelined();
           // 管道操作,会把所有的操作发送给服务器端,然后一次性执行;
           // ……
          pipeline.incr( “key”);
           // 获得所有的结果;
          pipeline.sync();
     }
}
———————– Pipeline ———————–
———————– Jedis.setnx(key, value) ———————–
import java.util.Random;
import org.apache.commons.pool.impl.GenericObjectPool.Config;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
 
public class RedisLock
{
     // 加锁标志
     public static final String    LOCKED                  = “TRUE”;
     public static final long      ONE_MILLI_NANOS         = 1000000L;
     
     // 默认超时时间(ms )
     public static final long      DEFAULT_TIME_OUT   = 3000;
     public static JedisPool       pool;
     public static final Random    r                       = new Random();
     
     // 锁的超时时间(s),过期删除
     public static final int       EXPIRE                  = 5 * 60;
     static
     {
           pool = new JedisPool( new Config(), “host”, 6379);
     }
     
     private Jedis                 jedis;
     private String                key;
     
     // 锁状态标志
     private boolean                    locked                  = false;
     
     public RedisLock(String key)
     {
           this. key = key;
           this. jedis = pool.getResource();
     }
     
     public boolean lock( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                    if ( jedis.setnx( key, LOCKED) == 1)
                   {
                         jedis.expire( key, EXPIRE);
                         this. locked = true ;
                         return locked;
                   }
                    // 短暂休眠,nano避免出现活锁
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
           return false;
     }
     
     public boolean lock()
     {
           return lock( DEFAULT_TIME_OUT);
     }
     
     // 无论是否加锁成功,必须调用
     public void unlock()
     {
           try
          {
               if ( locked)
              {
                    jedis.del( key);
              }
          }
           finally
          {
               pool.returnResource( jedis);
          }
     }
}
———————– Jedis.setnx(key, value) ———————–
———————– 事务multi ———————–
     public boolean lock_2( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                   Transaction t = jedis.multi();
                    // 开启事务,当server端收到 multi指令;
                    // 会将该client的命令放入一个队列,然后依次执行,直到收到 exec指令;
                   t.getSet( key, LOCKED);
                   t.expire( key, EXPIRE);
                   String ret = (String) t.exec().get(0);
                    if (ret == null || ret.equals(“UNLOCK”))
                   {
                         return true;
                   }
                    // 短暂休眠,nano避免出现活锁;
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
          }
           return false;
     }
———————– 事务multi ———————–
———————– 事务+监听 ———————–
     public boolean lock_3( long timeout)
     {
           long nano = System. nanoTime();
          timeout *= ONE_MILLI_NANOS;
           try
          {
               while ((System. nanoTime() – nano) < timeout)
              {
                    jedis.watch( key);
                    // 开启watch之后,如果key的值被修改,则事务失败, exec方法返回null;
                   String value = jedis.get( key);
                    if (value == null || value.equals(“UNLOCK”))
                   {
                        Transaction t = jedis.multi();
                        t.setex( key, EXPIRE, LOCKED);
                         if (t.exec() != null)
                        {
                              return true;
                        }
                   }
                    jedis.unwatch();
                    // 短暂休眠,nano避免出现活锁;
                   Thread. sleep(3, r.nextInt(500));
              }
          }
           catch (Exception e)
          {
          }
           return false;
     }
———————– 事务+监听 ———————–
———————– redis分布式 ———————–
List<JedisShardInfo> hosts = new ArrayList<JedisShardInfo>();
// server1
JedisShardInfo host1 = new JedisShardInfo( “”, 6380, 2000);
// server2
JedisShardInfo host2 = new JedisShardInfo( “”, 6381, 2000);
hosts.add(host1);
hosts.add(host2);
 
ShardedJedis jedis = new ShardedJedis(hosts);
jedis.set(“key”, “”);
———————– redis分布式 ———————–
———————– 错误的配置 ———————–
<bean id=”jedisPoolConfig” class=”redis.clients.jedis.JedisPoolConfig”>
<property name=”maxActive”  value=”20″ />
<property name=”maxIdle” value=”10″ />
<property name=”maxWait” value=”1000″ />
</bean>

<!– jedis shard信息配置 –>
<bean id=”jedis.shardInfo” class=”redis.clients.jedis.JedisShardInfo”>
<constructor-arg index=”0″ value=”*.*.*.*” />
<constructor-arg index=”1″ value=”6379″ />
</bean>

<!– jedis shard pool配置 –>
<bean id=”shardedJedisPool” class=”redis.clients.jedis.ShardedJedisPool”>
<constructor-arg index=”0″ ref=”jedisPoolConfig” />
<constructor-arg index=”1″>
<list>
<ref bean=”jedis.shardInfo” />
</list>
</constructor-arg>
</bean>

<bean id=”jedisCommands” factory-bean=”shardedJedisPool” factory-method=”getResource” />

———————– 错误的配置 ———————–
———————– 解决办法 ———————–
<!– POOL配置 –>
<bean id=”jedisPoolConfig” class=”redis.clients.jedis.JedisPoolConfig”>
<property name=”maxActive”  value=”20″ />
<property name=”maxIdle” value=”10″ />
<property name=”maxWait” value=”1000″ />
<property name=”testOnBorrow”  value=”true”/>
</bean>

<!– jedis shard信息配置 –>
<bean id=”jedis.shardInfo” class=”redis.clients.jedis.JedisShardInfo”>
<constructor-arg index=”0″ value=”*.*.*.*” />
<constructor-arg index=”1″ value=”6379″ />
</bean>

<!– jedis shard pool配置 –>
<bean id=”shardedJedisPool” class=”redis.clients.jedis.ShardedJedisPool”>
<constructor-arg index=”0″ ref=”jedisPoolConfig” />
<constructor-arg index=”1″>
<list>
<ref bean=”jedis.shardInfo” />
</list>
</constructor-arg>
</bean>

———————– 解决办法 ———————–

Redis学习09–虚拟内存

Redis中的虚拟内存
  1. Redis中虚拟内存的概念:
    1. redis的虚拟内存与os的虚拟内存不是同一个概念,但是实现的方法和目的是相同的,就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据;
    2. 对于redis这样的内存数据库,内存总是不够用的,除了可以将数据分割到多个redis server外,另外的能够提高数据库容量的办法就是使用vm把那些不经常访问的数据交换的磁盘上;
    3. 总是有少部分数据被经常访问,大部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃,当少量数据被经常访问时,使用vm不但能提高单台redis server数据库的容量,而且也不会对性能造成太多影响;
  2. Redis没有使用os提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内存机制,主要原因为:
    1. os的虚拟内存是以4k页面为最小单位进行交换的,而redis的大多数对象都远小于4k,所以一个os页面上可能有多个redis对象;
    2. 另外redis的集合对象类型如list,set可能存在与多个os页面上,最终可能造成只有10%的key被经常访问,但是所有os页面都会被os认为是活跃的,这样只有内存真正耗尽时os才会交换页面;
    3. 相比于os的交换方式,redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息,一般压缩后的对象会比内存中的对象小10倍,这样redis的vm会比osvm能少做很多io操作;
  3. VM的配置:
    1. vm-enabled yes                     # 开启vm功能;
    2. vm-swap-file /tmp/redis.swap       # 交换出来的value保存的文件路径/tmp/redis.swap;
    3. vm-max-memory 1000000              # redis使用的最大内存上限,超过上限后redis开始交换value到磁盘文件中;
    4. vm-page-size 32                    # 每个页面的大小32个字节;
    5. vm-pages 134217728                 # 最多使用在文件中使用多少页面,交换文件的大小 = vm-page-size * vm-pages
    6. vm-max-threads 4                   # 用于执行value对象换入换出的工作线程数量,推荐设置为cpu的核心数,0表示不使用工作线程;
  4. 参数的解释:
    1. redis的vm在设计上为了保证key的查找速度,只会将value交换到swap文件中,所以如果是内存问题是由于太多value很小的key造成的,那么vm并不能解决;
    2. 和os一样redis也是按页面来交换对象的,redis规定同一个页面只能保存一个对象,但是一个对象可以保存在多个页面中;
    3. 在redis使用的内存没超过vm-max-memory之前是不会交换任何value的,当超过最大内存限制后,redis会选择较老的对象,如果两个,对象一样老会优先交换比较大的对象,精确的公式swappability = age log(size_in_memory);
    4. 对于vm-page-size的设置应该根据自己的应用将页面的大小设置为可以容纳大多数对象的大小,太大了会浪费磁盘空间,太小了会造成交换文件出现碎片;
    5. 对于交换文件中的每个页面,redis会在内存中对应一个1bit值来记录页面的空闲状态,所以像上面配置中页面数量(vm-pages 134217728)会占用16M(134217728/8/1024/1024)内存用来记录页面空闲状态;
    6. vm-max-threads表示用做交换任务的线程数量,如果大于0推荐设为服务器的cpu core的数量,如果是0则交换过程在主线程进行;
  5. vm的工作原理:
    1. 当vm-max-threads设为0时(Blocking VM):
      1. 换出:主线程定期检查发现内存超出最大上限后,会直接已阻塞的方式,将选中的对象保存到swap文件中,并释放对象占用的内存,此过程会一直重复直到下面条件满足
        1. 内存使用降到最大限制以下;
        2. swap文件满了;
        3. 几乎全部的对象都被交换到磁盘了;
      2. 换入:当有client请求value被换出的key时,主线程会以阻塞的方式从文件中加载对应的value对象,加载时此时会阻塞所有的client,然后处理client的请求;
    2. 当vm-max-threads大于0(Threaded VM):
      1. 换出:当主线程检测到使用内存超过最大上限,会将选中的要交换的对象信息放到一个队列中交由工作线程后台处理,主线程会继续处理client请求;
      2. 换入:如果有client请求的key被换出了,主线程先阻塞发出命令的client,然后将加载对象的信息放到一个队列中,让工作线程去加载,加载完毕后工作线程通知主线程,主线程再执行client的命令,这种方式只阻塞请求value被换出key的client;
    3. 总的来说blocking vm的方式总的性能会好一些,因为不需要线程同步,创建线程和恢复被阻塞的client等开销,但是也相应的牺牲了响应性;threaded vm的方式主线程不会阻塞在磁盘io上,所以响应性更好;如果应用不太经常发生换入换出,而且也不太在意有点延迟的话则推荐使用blocking vm的方式;
  6. 相关链接:
    1. http://antirez.com/post/redis-virtual-memory-story.html;
    2. http://redis.io/topics/internals-vm;

Redis学习08–Master-Slave架构

Redis的主从架构
  1. redis主从复制的配置和使用都非常简单,通过主从复制可以允许多个slave servers和master server具有相同的数据库副本;
  2. Master-Salve的特点:
    1. master可以有多个slaves;
    2. 除了多个slave连到相同的master外,slave也可以连接其他slave形成图状结构;
    3. 主从复制不会阻塞master,也就是说当一个或多个slave与master进行初次同步数据时,master可以继续处理client发来的请求,相反slave在初次同步数据时则会阻塞不能处理client的请求;
    4. 从复制可以用来提高系统的可伸缩性,我们可以用多个slave专门用于client的读请求,比如sort操作可以使用slave来处理,也可以用来做简单的数据冗余;
    5. 可以在master禁用数据持久化,只需要注释掉master配置文件中的所有save配置,然后只在slave上配置数据持久化;
  3. Master-Slave的过程:
    1. 当设置好slave服务器后,slave会建立和master的连接,然后发送sync命令;
    2. 无论是第一次同步建立的连接还是连接断开后的重新连接,master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存起来;
    3. 后台进程完成写文件后,master就发送文件给slave,slave将文件保存到磁盘上,然后加载到内存恢复数据库快照到slave上;
    4. 接着master就会把缓存的命令转发给slave,而且后续master收到的写命令都会通过开始建立的连接发送给slave;
    5. 从master到slave的同步数据的命令和从client发送的命令使用相同的协议格式;
    6. 当master和slave的连接断开时slave可以自动重新建立连接,如果master同时收到多个slave发来的同步连接命令,只会使用启动一个进程来写数据库镜像,然后发送给所有slave;
  4. 配置slave服务器很简单,只需要在配置文件中加入如下配置:slaveof 192.168.1.1 6379  #指定master的ip和端口;

Redis学习07–持久化

Redis的数据持久化
  1. redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化,redis支持两种持久化方式:
    1. Snapshotting(快照)也是默认方式;
    2. Append-only file(缩写aof)的方式;
  2. Snapshotting:
    1. 快照是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb;
    2. 可以通过配置设置自动做快照持久化的方式,我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:
      1. save 900 1  #900s内如果超过1个key被修改,则发起快照保存;
      2. save 300 10 #300s内如果超过10个key被修改,则发起快照保存;
      3. save 60 10000 # 10s内,如果超过10000个key被修改,则发起快照保存;
    3. 快照保存过程:
      1. redis调用fork,现在有了子进程和父进程;
      2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件,由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面,所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照;
      3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出;
    4. 快照持久化的注意事项:
      1. client也可以使用save或者bgsave命令通知redis做一次快照持久化,save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有client的请求,这种方式会阻塞所有client请求,所以不推荐使用;
      2. 需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据,如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能;
      3. 另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,如果应用要求不能丢失任何修改的话,可以采用aof持久化方式;
  3. Append-only file:
    1. aof比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof),当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容,当然由于os会在内核中缓存write做的修改,所以可能不是立即写到磁盘上,这样aof方式的持久化也还是有可能会丢失部分修改,不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机:
      1. appendonly yes:启用aof持久化方式;
      2. appendfsync always:每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用;
      3. appendfsync everysec:每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐,也是默认方式;
      4. appendfsync no:完全依赖os,性能最好,持久化没保证;
    2. aof的方式也同时带来了另一个问题,持久化文件会变的越来越大;例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的,因为要恢复数据库的状态其实文件中保存一条set test 100就够了;为了压缩aof的持久化文件,redis提供了bgrewriteaof命令,收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件;
    3. bgrewriteaof命令原理:
      1. redis调用fork,现在有父子两个进程;
      2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令;
      3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中,同时把收到的写命令缓存起来,这样就能保证如果子进程重写失败的话并不会出问题;
      4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程,然后父进程把缓存的写命令也写入到临时文件;
      5. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加;
    4. 需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似;

Redis学习06–发布及订阅

Redis的发布及订阅

  1. 发布订阅(pub/sub)是一种消息通信模式,主要的目的是解决消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似;pub/sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合;
  2. redis作为一个pub/sub server,在订阅者和发布者之间起到了消息路由的功能,订阅者可以通过subscribe和psubscribe命令向redis server订阅自己感兴趣的消息类型,redis将消息类型称为通道(channel);
  3. 当发布者通过publish命令向redis server发送特定类型的消息时,订阅该消息类型的全部client都会收到此消息;这里消息的传递是多对多的,一个client可以订阅多个channel,也可以向多个channel发送消息;
  4. 发布/订阅的命令:
    1. SUBSCRIBE channel [channel …]:Subscribes the client to the specified channels,Once the client enters the subscribed state it is not supposed to issue any other commands, except for additional SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE and PUNSUBSCRIBE commands;
    2. PSUBSCRIBE pattern [pattern …]:Subscribes the client to the given patterns;
    3. UNSUBSCRIBE [channel [channel …]]:Unsubscribes the client from the given channels, or from all of them if none is given,When no channels are specified, the client is unsubscribed from all the previously subscribed channels. In this case, a message for every unsubscribed channel will be sent to the client;
    4. PUNSUBSCRIBE [pattern [pattern …]]:Unsubscribes the client from the given patterns;
    5. PUBLISH channel message:Posts a message to the given channel; Integer reply:the number of clients that received the message;
  5. 开启三个redis-cli测试发布订阅的例子;
  6. redis的协议是文本类型的,具体链接为:http://redis.io/topics/protocol;
  7. 使用java客户端来订阅消息;
———————— 发布/订阅测试 ————————
— 1.打开第一个redis-cli的客户端订阅news:snda和news:taobao两个channels,收到redis-server返回订阅成功的消息;
redis 127.0.0.1:6379> subscribe news:snda news:taobao
Reading messages… (press Ctrl-C to quit)
1) “subscribe”
2) “news:snda”
3) (integer) 1
1) “subscribe”
2) “news:taobao”
3) (integer) 2
— 2.打开第二个redis-cli客户端,使用psubscribe订阅news:*模式(*表示任意字符串)的channels;
redis 127.0.0.1:6379> psubscribe news:*
Reading messages… (press Ctrl-C to quit)
1) “psubscribe”
2) “news:*”
3) (integer) 1
— 3.打开第三个redis-cli客户端,通过publish命令发布消息,返回值表示接收到此订阅的个数,通过订阅的窗口查看消息;
redis 127.0.0.1:6379> publish news:snda “www.snda.com”
(integer) 2
redis 127.0.0.1:6379> publish news:taobao “www.taobao.com”
(integer) 2
———————— 发布/订阅测试 ————————
———————— 使用java客户端来订阅消息 ————————
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
public class PubSubTest
{
     private static Socket    socket;
     
     public static void main(String[] args)
     {
           // String cmd = args[0] + “\r\n”;
          String cmd = “subscribe news:snda news:taobao \r\n”;
           try
          {
               socket = new Socket( “192.168.10.112”, 6379);
              InputStream in = socket.getInputStream();
              OutputStream out = socket.getOutputStream();
              out.write(cmd.getBytes());
               // 发送订阅命令         
               byte[] buffer = new byte[1024];
               while ( true)
              {
                    int readCount = in.read(buffer);
                   System. out.write(buffer, 0, readCount);
                   System. out.println( “————————————–“ );
              }
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
}
 
———————— 使用java客户端来订阅消息 ————————

Redis学习05–Pipeline

Redis中的Pipeline
  1. Pipeline是打包多条命令发送给服务端,服务端处理完多条命令后将结果打包一起返回的方式;
  2. redis是一个cs模式的tcp server,使用和http类似的请求响应协议,一个client可以通过一个socket连接发起多个请求命令;每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完后请求命令后会将结果通过响应报文返回给client;
  3. 模拟的通信过程;                                                
  4. 基本上四个命令需要8个tcp报文才能完成,由于通信会有网络延迟,假如从client和server之间的包传输时间需要0.125秒,那么上面的四个命令8个报文至少会需要1秒才能完成,这样即使redis每秒能处理100个命令,而我们的client也只能一秒钟发出四个命令,这显示没有充分利用redis的处理能力;
  5. 可以利用mget,mset之类的单条命令处理多个key的命令外,还可以利用pipeline的方式从client打包多条命令一起发出,不需要等待单条命令的响应返回,而redis服务端会处理完多条命令后会将多条命令的处理结果打包到一起返回给客户端;
  6. 假设不会因为tcp报文过长而被拆分,可能两个tcp报文就能完成四条命令,client可以将四个incr命令放到一个tcp报文一起发送,server则可以将四条命令的处理结果放到一个tcp报文返回;
    通过pipeline方式当有大批量的操作时候,我们可以节省很多原来浪费在网络延迟的时间,需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果,打包的命令越多,缓存消耗内存也越多;所以并是不是打包的命令越多越好;
  7. 使用Jedis测试Pipeline的过程,发现使用pipeline节省很多时间;                        
package com.snda.study.redis;
 
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
 
public class PipeLineTest
{
     public static void main(String[] args)
     {
           // 不使用pipeline的时间消耗;
           long start = System. currentTimeMillis();
           withoutPipeline();
           long end = System. currentTimeMillis();
          System. out.println( “WithoutPipeline:” + (end – start));
          
           // 使用pipeline的时间消耗;
          start = System. currentTimeMillis();
           usePipeline();
          end = System. currentTimeMillis();
          System. out.println( “UsePipeline:” + (end – start));
     }
     
     /**
      * 不使用pipeline的方式,循环N次,需要与服务器端交互2N次;
      */
     private static void withoutPipeline()
     {
           try
          {
              Jedis jedis = new Jedis( “192.168.10.112”, 6379);
               for ( int i = 0; i < 100000; i++)
              {
                   jedis.incr( “withoutPipeline”);
              }
              jedis.quit();
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
     
     /**
      * 使用pipeline,循环N次,会把操作打包发送给服务端,然后一次性返回;
      */
     private static void usePipeline()
     {
           try
          {
              Jedis jedis = new Jedis( “192.168.10.112”, 6379);
              Pipeline pipeline = jedis.pipelined();
               for ( int i = 0; i < 100000; i++)
              {
                   pipeline.incr( “usePipeline”);
              }
              pipeline.sync();
              jedis.quit();
          }
           catch (Exception e)
          {
              e.printStackTrace();
          }
     }
}

Redis学习04–对事务的处理

Redis中的事务
  1. redis对事务的支持目前还比较简单,redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令,由于redis是单线程来处理所有client的请求的所以做到这点是很容易的;
  2. 一般情况下redis在接受到一个client发来的命令后会立即处理并返回处理结果,但是当一个client在一个连接中发出multi命令时,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中,当此连接收到exec命令后,redis会顺序的执行队列中的所有命令,并将所有命令的运行结果打包到一起返回给client,然后此连接就结束事务上下文;
  3. 命令解释:
    1. MULTI:Marks the start of a transaction block. Subsequent commands will be queued for atomic execution using EXEC,开启一个事务,相当于begin transaction;
    2. EXEC:Executes all previously queued commands in a transaction and restores the connection state to normal,When using WATCH, EXEC will execute commands only if the watched keys were not modified,执行事务队列里面的命令,相当于commit;
    3. DISCARD:Flushes all previously queued commands in a transaction and restores the connection state to normal,If WATCH was used, DISCARD unwatches all keys,取消事务队列里面的命令,相当于rollback;
    4. WATCH:Marks the given keys to be watched for conditional execution of a transaction,命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败;也可以调用watch多次监视多个key,这样就可以对指定的key加乐观锁了;
    5. 注意watch的key是对整个连接有效的,事务也一样,如果连接断开,监视和事务都会被自动清除,当然了exec, discard, unwatch命令都会清除连接中的所有监视;
  4. 使用multi命令的例子;
  5. 使用discard命令的例子;
  6. 使用watch命令的例子;
  7. Redis使用事务的bug:
    1. redis只能保证事务的每个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,命令类型不匹配的例子;
    2. 当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;当然如果我们使用的append-only file方式持久化,redis会用单个write操作写入整个事务内容,即使是这种方式还是有可能只部分写入了事务到磁盘;发生部分写入事务的情况下,redis重启时会检测到这种情况,然后失败退出,可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容,修复完后就能够重新启动了;

————————- multi命令 ————————-

— 1.可以看到incr a ,incr b命令发出后并没执行而是被放到了队列中;调用exec后两个命令被连续的执行,最后返回的是两条命令执行后的结果;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

————————- multi命令 ————————-
————————- discard命令 ————————-
— 1.可以发现incr a incr b都没被执行;discard命令其实就是清空事务的命令队列并退出事务上下文;
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr b
QUEUED
redis 127.0.0.1:6379> discard
OK
redis 127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> get b
“1”
————————- discard命令 ————————-
————————- watch命令 ————————-
— 1.常规情况,设置a的值;如果是一个incr a的操作,使用以下命令去实现,由于get a和set a两个命令并不能保证是连续执行的(get操作不在事务上下文中),很可能有两个client同时做这个操作,结果期望是加两次a从原来的1变成3,但是很有可能两个client的get a,取到都是1,造成最终加两次结果却是2,主要问题我们没有对共享资源a的访问进行任何的同步;
也就是说redis没提供任何的加锁机制来同步对a的访问;
redis 127.0.0.1:6379> get a
“1”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 2
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“2”
— 2.使用watch命令对变量加锁;
redis 127.0.0.1:6379> watch a
OK
redis 127.0.0.1:6379> get a
“2”
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> set a 3
QUEUED
redis 127.0.0.1:6379> exec
1) OK
redis 127.0.0.1:6379> get a
“3”
— 3.被watch的key如果在事务在exec之前被修改了,则返回错误;
————————- watch命令 ————————-
————————- Redis中事务的bugs ————————-
— 1.数据类型不一致导致事务失败;
redis 127.0.0.1:6379> set a 1
OK
redis 127.0.0.1:6379> lpush list1 5
(integer) 1
redis 127.0.0.1:6379> set c 1
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> incr a
QUEUED
redis 127.0.0.1:6379> incr list1
QUEUED
redis 127.0.0.1:6379> incr c
QUEUED
redis 127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR Operation against a key holding the wrong kind of value
3) (integer) 2
— 2.当事务的执行过程中,如果redis服务宕机了,只有部分命令执行了,后面的也就被丢弃了;
————————- Redis中事务的bugs ————————-

Redis学习03–排序操作

Redis的排序操作

  1. redis支持对list, set和sorted set元素的排序;
  2. 排序命令SORT的语法:SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC|DESC] [ALPHA] [STORE destination];
  3. sort key [ASC|DESC] [ALPHA]:
    1. sort key:不加任何选项时就是简单的对列表自身元素排序,并返回排序结果,列表中的值只能是数字;
    2. ASC:sort默认的排序方式,即升序排列;
    3. DESC:是逆序排列;
    4. ALPHA:如果列表中是字符串或者数字的话,需要使用alpha选项按照字母顺序排列;ASC|DESC可以和ALPHA连用;
  4. [LIMIT offset count]:用于分页显示,表示从offset的位置(从0开始),显示count个记录;
  5. [BY nosort]:不排序,直接返回list, set或者sorted set中元素的物理顺序;
  6. [BY pattern]:使用外部的keys作为权重来对list, set, sorted set中的元素排序,但是权重的后缀必须与几何中的元素一一对应;
  7. [GET pattern …]:以通过get选项去获取指定pattern作为新key对应的值;
  8. [STORE destination]:如果对集合经常按照固定的模式去排序,那么把排序结果缓存起来会减少cpu的开销,使用store选项可以将排序内容保存到指定key中,保存的类型是list;
  9. 关于排序的两个问题及解决方案:
    1. 如果我们有多个redis server的话,不同的key可能存在于不同的server上,比如weight_3,weight_9, weight_12, weight_22很有可能分别在四个不同的server上存放着,这种情况会对排序性能造成很大的影响;
      1. 可以通过key tag将需要排序的key都放到同一个server上,由于具体决定哪个key存在哪个服务器上一般都是在client端hash的办法来做的,我们可以通过只对key的部分进行hash;
      2. 举个例子假如我们的client如果发现key中包含[],那么只对key中[]包含的内容进行hash,我们将weight_四个相关的key,[weight_]3,[weight_]9, [weight_]12, [weight_]22都这样命名,于是client程序就会把他们都放到同一server上;
    2. 如果要sort的集合非常大的话排序就会消耗很长时间,由于redis单线程的,所以长时间的排序操作会阻塞其他client的请求;
      1. 通过主从复制机制将数据复制到多个slave上,然后我们只在slave上做排序操作,并进可能的对排序结果缓存;
      2. 或者是采用sorted set对需要按某个顺序访问的集合建立索引;
  10. 实际使用的场景及例子;
——————————— sort key [ASC|DESC] [ALPHA] ———————————
— 1.sort key;
redis 127.0.0.1:6379> lpush l1 12
(integer) 1
redis 127.0.0.1:6379> lpush l1 3
(integer) 2
redis 127.0.0.1:6379> lpush l1 22
(integer) 3
redis 127.0.0.1:6379> lpush l1 9
(integer) 4
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 desc
1) “22”
2) “12”
3) “9”
4) “3”
— 2.alpha;
1.对数字按照字母表顺序排序;
2.字符串按照字母表顺序排序;
redis 127.0.0.1:6379> lpush l2 snda
(integer) 1
redis 127.0.0.1:6379> lpush l2 tencent
(integer) 2
redis 127.0.0.1:6379> lpush l2 baidu
(integer) 3
redis 127.0.0.1:6379> lpush l2 taobao
(integer) 4
redis 127.0.0.1:6379> sort l2 alpha
1) “baidu”
2) “snda”
3) “taobao”
4) “tencent”
redis 127.0.0.1:6379> sort l2 desc alpha
1) “tencent”
2) “taobao”
3) “snda”
4) “baidu”
redis 127.0.0.1:6379> sort l2
(error) ERR One or more scores can’t be converted into double
——————————— sort key [ASC|DESC] [ALPHA] ———————————
——————————— [LIMIT offset count] ———————————
获得l1集合中第二个字符开始的后两个值;
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 limit 1 2
1) “9”
2) “12”
redis 127.0.0.1:6379> sort l1 limit 1 2 desc
1) “12”
2) “9”
——————————— [LIMIT offset count] ———————————
——————————— [BY nosort] ———————————
li列表插入的时候是按照12, 3, 22, 9的顺序插入的,但是实际的物理顺序是反序的,即后插入的元素在前面;
redis 127.0.0.1:6379> sort l1 by nosort
1) “9”
2) “22”
3) “3”
4) “12”
——————————— [BY nosort] ———————————
——————————— [BY pattern] ———————————
redis 127.0.0.1:6379> sort l1 by nosort
1) “9”
2) “22”
3) “3”
4) “12”
redis 127.0.0.1:6379> set weight_3 1
OK
redis 127.0.0.1:6379> set weight_12 2
OK
redis 127.0.0.1:6379> set weight_22 3
OK
redis 127.0.0.1:6379> set weight_9 4
OK
redis 127.0.0.1:6379> sort l1 by weight_*
1) “3”
2) “12”
3) “22”
4) “9”
redis 127.0.0.1:6379> sort l1 by weight_* desc
1) “9”
2) “22”
3) “12”
4) “3”
redis 127.0.0.1:6379> set weight_22 5
OK
redis 127.0.0.1:6379> sort l1 by weight_*
1) “3”
2) “12”
3) “9”
4) “22”
——————————— [BY pattern] ———————————
——————————— [GET pattern …] ———————————
— 1.返回pattern的值,按照pattern的顺序;
redis 127.0.0.1:6379> sort l1 by weight_*  get weight_*
1) “1”
2) “2”
3) “4”
4) “5”
— 2.通过#(#特殊符号引用的是原始集合)返回原来列表中的值;
redis 127.0.0.1:6379> sort l1 by weight_* get weight_* get #
1) “1”
2) “3”
3) “2”
4) “12”
5) “4”
6) “9”
7) “5”
8) “22”
— 3.引用hash类型字段的话需要使用->符号,当值不存在就返回nil;
redis 127.0.0.1:6379> hset h_3 id 1
(integer) 1
redis 127.0.0.1:6379> hset h_22 id 2
(integer) 1
redis 127.0.0.1:6379> hset h_9 id 3
(integer) 1
redis 127.0.0.1:6379> hset h_10 id 4
(integer) 1
redis 127.0.0.1:6379> sort l1 get h_*->id
1) “1”
2) “3”
3) (nil)
4) “2”
——————————— [GET pattern …] ———————————
——————————— [STORE destination] ———————————
把排序的结果保存在一个list中;
redis 127.0.0.1:6379> sort l1
1) “3”
2) “9”
3) “12”
4) “22”
redis 127.0.0.1:6379> sort l1 limit 1 2 store sl1
(integer) 2
redis 127.0.0.1:6379> type sl1
list
redis 127.0.0.1:6379> lrange sl1 0 -1
1) “9”
2) “12”
——————————— [STORE destination] ———————————
——————————— 实际使用的场景及例子 ———————————
— 1.kobe的队友列表,存放队友的号码uid;
redis 127.0.0.1:6379> sadd kobe:team:list 10
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 12
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 15
(integer) 1
redis 127.0.0.1:6379> sadd kobe:team:list 16
(integer) 1
— 2.球员的排序规则,存放球员的得分;
redis 127.0.0.1:6379> set uid:sort:10 1000
OK
redis 127.0.0.1:6379> set uid:sort:12 2000
OK
redis 127.0.0.1:6379> set uid:sort:15 300
OK
redis 127.0.0.1:6379> set uid:sort:16 2500
OK
— 3.队友的信息;
redis 127.0.0.1:6379> set uid:10 “{‘uid’:10,’name’:’Steve Nash’}”
OK
redis 127.0.0.1:6379> set uid:12 “{‘uid’:12,’name’:’Dwight Howard’}”
OK
redis 127.0.0.1:6379> set uid:15 “{‘uid’:15,’name’:’Ron Artest’}”
OK
redis 127.0.0.1:6379> set uid:16 “{‘uid’:16,’name’:’Pau Gasol’}”
OK
— 4.按照得分对球员排序;
redis 127.0.0.1:6379> sort kobe:team:list by uid:sort:* get uid:*
1) “{‘uid’:15,’name’:’Ron Artest’}”
2) “{‘uid’:10,’name’:’Steve Nash’}”
3) “{‘uid’:12,’name’:’Dwight Howard’}”
4) “{‘uid’:16,’name’:’Pau Gasol’}”
redis 127.0.0.1:6379> sort kobe:team:list by uid:sort:* get uid:* get uid:sort:*
1) “{‘uid’:15,’name’:’Ron Artest’}”
2) “300”
3) “{‘uid’:10,’name’:’Steve Nash’}”
4) “1000”
5) “{‘uid’:12,’name’:’Dwight Howard’}”
6) “2000”
7) “{‘uid’:16,’name’:’Pau Gasol’}”
8) “2500”
——————————— 实际使用的场景及例子 ———————————

Redis学习02–数据类型介绍

Redis中的数据类型

  1. Redis中支持的数据类型:
    1. string;
    2. list;
    3. set;
    4. sorted set;
    5. hash;
  2. Redis中的Key:
    1. redis本质上一个key-value数据库,它的key是字符串类型,但是key中不能包括边界字符,由于key不是binary safe的字符串,所以像”my key”和”mykey\n”这样包含空格和换行的key是不允许的(之后的版本是可以包含任字符的);
    2. 在redis内部并不限制使用binary字符,这是redis协议限制的,”\r\n”在协议格式中会作为特殊字符;redis 1.2以后的协议中部分命令已经开始使用新的协议格式了(比如MSET),推荐把包含边界字符当成非法的key,免得被bug纠缠;
    3. key的一个格式约定,object-type:id:field;比如user:1000:password,blog:xxidxx:title;key的长度最好不要太长,首先占内存啊,而且查找时候相对短key也更慢,不过也不推荐过短的key,可读性不好;
    4. 与key相关的命令:
      1. exists key:测试指定key是否存在,返回1表示存在,0不存在;
      2. del key1 key2 ….keyN:删除给定key,返回删除key的数目,0表示给定key都不存在;
      3. type key:返回给定key的value类型;返回none表示不存在key,string字符类型,list链表类型,set无序集合类型;
      4. keys pattern:返回匹配指定模式的所有key;
        1. keys *:获得所有的keys;
        2. key t*:获得t开头的keys;
        3. key t[ab]x:获得以t开头x结尾中间是a或者b的keys;
        4. key t?x:获得以t开头x结尾中间之后一个字符的keys;
      5. randomkey:返回从当前数据库中随机选择的一个key,如果当前数据库是空的,返回空串;
      6. rename oldkey newkey:原子的重命名一个key,如果newkey存在,将会被覆盖,返回1表示成功,0失败;可能是oldkey不存在或者和newkey相同;
      7. renamenx oldkey newkey:同上,但是如果newkey存在返回失败;
      8. dbsize:返回当前数据库的key数量;
      9. expire key seconds:为key指定过期时间,单位是秒;返回1成功,0表示key已经设置过过期时间或者不存在;
      10. ttl key:返回设置过过期时间的key的剩余过期秒数,-1表示key不存在或者没有设置过过期时间;
      11. select db-index:通过索引选择数据库,默认连接的数据库所有是0,默认数据库数是16个;返回1表示成功,0失败;
      12. move key db-index:将key从当前数据库移动到指定数据库,返回1成功,0如果key不存在,或者已经在指定数据库中;
      13. flushdb:删除当前数据库中所有key,此方法不会失败,慎用;
      14. flushall:删除所有数据库中的所有key,此方法不会失败,更加慎用;
  3. string类型:
    1. string是redis最基本的类型,而且string类型是二进制安全的,即redis的string可以包含任何数据;比如jpg图片或者序列化的对象;
    2. 从内部实现来看其实string可以看作byte数组,最大上限是1G字节,string类型的定义为:struct sdshdr {long len; long free; char buf[]; };
      1. buf是个char数组用于存贮实际的字符串内容,其实char和高级语言中的byte是等价的,都是一个字节;
      2. len是buf数组的长度;
      3. free是数组中剩余可用字节数;
    3. string类型可以被部分命令按int处理,比如incr等命令;
    4. redis的其它类型像list, set, sorted set, hash它们包含的元素与都只能是string类型;
    5. 如果只用string类型,redis就可以被看作加上持久化特性的memcached,当然redis对string类型的操作比memcached多很多;
    6. 与string相关的操作:
      1. set key value:设置key对应的值为string类型的value,返回1表示成功,0失败;
      2. setnx key value:同上,如果key已经存在,返回0;nx是not exist的意思;
      3. get key:获取key对应的string值,如果key不存在返回nil;
      4. getset key value:原子的设置key的值,并返回key的旧值;如果key不存在返回nil;
      5. mget key1 key2 … keyN:一次获取多个key的值,如果对应key不存在,则对应返回nil;
      6. mset key1 value1 … keyN valueN:一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置;
      7. msetnx key1 value1 … keyN valueN:同上,但是不会覆盖已经存在的key;
      8. incr key:对key的值做加加操作,并返回新的值;注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key为1;
      9. decr key:同上,但是做的是减减操作,decr一个不存在key,则设置key为-1;
      10. incrby key integer:同incr,加指定值,key不存在时候会设置key,并认为原来的value是0;
      11. decrby key integer:同decr,减指定值;decrby完全是为了可读性,我们完全可以通过incrby一个负值来实现同样效果,反之一样;
      12. substr key start end:返回截取过的key的字符串值,注意并不修改key的值,下标是从0开始的(redis在2.0版本以后不包括2.0,使用的方法是getrange参数相同);
      13. append key value:给指定key的字符串值追加value,返回新字符串值的长度;
  4. list类型:
    1. redis的list类型其实就是一个每个子元素都是string类型的双向链表,所以[lr]push和[lr]pop命令的算法时间复杂度都是O(1),另外list会记录链表的长度,所以llen操作也是O(1);
    2. list插入的元素,默认是按照时间的逆序排列的,最新的数据都是插入到头部,可以通过lrange list 0 -1/sort list by nosort查看;
    3. 链表的最大长度是(2的32次方-1),我们可以通过push,pop操作从链表的头部或者尾部添加删除元素;这使得list既可以用作栈,也可以用作队列;
    4. list的pop操作还有阻塞版本的,当我们[lr]pop一个list对象时,如果list是空,或者不存在,会立即返回nil,但是阻塞版本的b[lr]pop则可以阻塞,当然可以加超时时间,超时后也会返回nil;
    5. 为什么要阻塞版本的pop呢:主要是为了避免轮询;举个简单的例子如果我们用list来实现一个工作队列,执行任务的thread可以调用阻塞版本的pop去获取任务这样就可以避免轮询去检查是否有任务存在,当任务来时候工作线程可以立即返回,也可以避免轮询带来的延迟;
    6. 与list相关的操作:
      1. lpush key string:在key对应list的头部添加字符串元素,返回1表示成功,0表示key存在且不是list类型;
      2. rpush key string:同上,在尾部添加;
      3. llen key:返回key对应list的长度,key不存在返回0,如果key对应类型不是list返回错误;
      4. lrange key start end:返回指定区间内的元素,下标从0开始,负值表示从后面计算,-1表示倒数第一个元素,key不存在返回空列表;
      5. ltrim key start end:截取list,保留指定区间内元素,成功返回1,key不存在返回错误;
      6. lset key index value:设置list中指定下标的元素值,成功返回1,key或者下标不存在返回错误;
      7. lrem key count value:从key对应list中删除count个和value相同的元素,count为0时候删除全部;
      8. lpop key:从list的头部删除元素,并返回删除元素,如果key对应list不存在或者是空返回nil,如果key对应值不是list返回错误;
      9. rpop:同上,但是从尾部删除;
      10. blpop key1…keyN timeout:从左到右扫描返回对第一个非空list进行lpop操作并返回;
        1. 比如blpop list1 list2 list3 0,如果list1不存在list2,list3都是非空则对list2做lpop并返回从list2中删除的元素;如果所有的list都是空或不存在,则会阻塞timeout秒,timeout为0表示一直阻塞;
        2. 当阻塞时,如果有client对key1…keyN中的任意key进行push操作,则第一在这个key上被阻塞的client会立即返回;如果超时发生,则返回nil;有点像unix的select或者poll
      11. brpop:同blpop,一个是从头部删除一个是从尾部删除;
      12. rpoplpush srckey destkey:从srckey对应list的尾部移除元素并添加到destkey对应list的头部,最后返回被移除的元素值,整个操作是原子的.如果srckey是空或者不存在返回nil;
  5. set类型:
    1. redis的set是string类型的无序集合,set元素最大可以包含(2的32次方-1)个元素;
    2. set的是通过hash table实现的,所以添加,删除,查找的复杂度都是O(1);hash table会随着添加或者删除自动的调整大小,需要注意的是调整hash table大小时候需要同步(获取写锁)会阻塞其他读写操作,可能不久后就会改用跳表(skip list)来实现;
    3. 跳表已经在sorted set中使用了,关于set集合类型除了基本的添加删除操作,其它有用的操作还包含集合的取并集(union),交集(intersection),差集(difference);通过这些操作可以很容易的实现sns中的好友推荐和blog的tag功能;
    4. 与set相关的操作:
      1. sadd key member:添加一个string元素到key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误;
      2. srem key member:从key对应set中移除给定元素,成功返回1,如果member在集合中不存在或者key不存在返回0,如果key对应的不是set类型的值返回错误;
      3. spop key:删除并返回key对应set中随机的一个元素,如果set是空或者key不存在返回nil;
      4. srandmember key:同spop,随机取set中的一个元素,但是不删除元素;
      5. smove srckey dstkey member:从srckey对应set中移除member并添加到dstkey对应set中,整个操作是原子的;成功返回1,如果member在srckey中不存在返回0,如果key不是set类型返回错误;
      6. scard key:返回set的元素个数,如果set是空或者key不存在返回0;
      7. sismember key member:判断member是否在set中,存在返回1,0表示不存在或者key不存在;
      8. sinter key1 key2…keyN:返回所有给定key的交集;
      9. sinterstore dstkey key1…keyN:同sinter,但是会同时将交集存到dstkey下;
      10. sunion key1 key2…keyN:返回所有给定key的并集;
      11. sunionstore dstkey key1…keyN:同sunion,并同时保存并集到dstkey下;
      12. sdiff key1 key2…keyN:返回所有给定key的差集;
      13. sdiffstore dstkey key1…keyN:同sdiff,并同时保存差集到dstkey下;
      14. smembers key:返回key对应set的所有元素,结果是无序的;
  6. sorted set类型:
    1. 和set一样sorted set也是string类型元素的集合,不同的是每个元素都会关联一个double类型的score;
    2. sorted set的实现是skip list和hash table的混合体当元素被添加到集合中时,一个元素到score的映射被添加到hash table中,所以给定一个元素获取score的开销是O(1),另一个score到元素的映射被添加到skip list并按照score排序,所以就可以有序的获取集合中的元素;添加,删除操作开销都是O(log(N))和skip list的开销一致,redis的skip list实现用的是双向链表,这样就可以逆序从尾部取元素;
    3. sorted set最经常的使用方式应该是作为索引来使用,我们可以把要排序的字段作为score存储,对象的id当元素存储;
    4. 与sorted set相关的操作:
      1. zadd key score member:添加元素到集合,元素在集合中存在则更新对应score;
      2. zrem key member:删除指定元素,1表示成功,如果元素不存在返回0;
      3. zincrby key incr member:增加对应member的score值,然后移动元素并保持skip list保持有序,返回更新后的score值;
      4. zrank key member:返回指定元素在集合中的排名(下标),集合中元素是按score从小到大排序的;
      5. zrevrank key member:同上,但是集合中元素是按score从大到小排序;
      6. zrange key start end:类似lrange操作从集合中去指定区间的元素;返回的是有序结果;
      7. zrevrange key start end:同上,返回结果是按score逆序的;
      8. zrangebyscore key min max:返回集合中score在给定区间的元素;
      9. zcount key min max:返回集合中score在给定区间的数量;
      10. zcard key:返回集合中元素个数;
      11. zscore key element:返回给定元素对应的score;
      12. zremrangebyrank key min max:删除集合中排名在给定区间的元素;
      13. zremrangebyscore key min max:删除集合中score在给定区间的元素;
  7. hash类型:
    1. redis的hash是一个string类型的field和value的映射表,它的添加,删除操作平均都是O(1),hash特别适合用于存储对象;
    2. 相较于将对象的每个字段存成单个string类型,将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象,省内存的原因是新建一个hash对象时开始是用zipmap(又称为small hash)来存储的;
    3. zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销,尽管zipmap的添加,删除,查找都是O(n),但是由于一般对象的field数量都不太多,所以使用zipmap也是很快的,也就是说添加删除平均还是O(1);
    4. 如果field或者value的大小超出一定限制后,redis会在内部自动将zipmap替换成正常的hash实现,这个限制可以在配置文件中指定;
      1. hash-max-zipmap-entries 64 #配置字段最多64个
      2. hash-max-zipmap-value 512 #配置value最大为512字节
    5. 与hash相关的操作:
      1. hset key field value:设置hash field为指定值,如果key不存在,则先创建;
      2. hget key field:获取指定的hash field;
      3. hmget key filed1….fieldN:获取全部指定的hash filed;
      4. hmset key filed1 value1 … filedN valueN:同时设置hash的多个field;
      5. hincrby key field integer:将指定的hash filed 加上给定值;
      6. hexists key field:测试指定field是否存在;
      7. hdel key field:删除指定的hash field;
      8. hlen key:返回指定hash的field数量;
      9. hkeys key:返回hash的所有field;
      10. hvals key:返回hash的所有value;
      11. hgetall:返回hash的所有filed和value;

Redis学习01–在Linux下安装Redis v2.6.13

安装Redis
  1. Redis的介绍:
    1. redis是一个开源的key-value数据库,它又经常被认为是一个数据结构服务器,因为它的value不仅包括基本的string类型还有list,set,sorted set和hash类型,当然这些类型的元素也都是string类型,也就是说list,set这些集合类型也只能包含string类型;
    2. 可以在这些类型上做很多原子性的操作,比如对一个字符value追加字符串(APPEND命令),加加或者减减一个数字字符串(INCR命令,当然是按整数处理的),可以对list类型进行push,或者pop元素操作(可以模拟栈和队列),对于set类型可以进行一些集合相关操作(intersection,union,difference);
    3. memcache也有类似与++,–的命令,不过memcache的value只包括string类型,远没有redis的value类型丰富;
    4. 和memcahe一样为了性能,redis的数据通常都是放到内存中的,当然redis可以每间隔一定时间将内存中数据写入到磁盘以防止数据丢失;
    5. redis也支持主从复制机制(master-slave replication),redis的其它特性包括简单的事务支持和发布订阅(pub/sub)通道功能,而且redis配置管理非常简单,还有各种语言版本的开源客户端类库;
  2. key-list类型内存数据引擎:
    1. 互联网数据目前基本使用两种方式来存储,关系数据库或者key value,但是这些互联网业务本身并不属于这两种数据类型,比如用户在社会化平台中的关系,它是一个list,如果要用关系数据库存储就需要转换成一种多行记录的形式,这种形式存在很多冗余数据,每一行需要存储一些重复信息;如果用key value存储则修改和删除比较麻烦,需要将全部数据读出再写入; — by timyang@weibo.com
    2. 如果用key-value中的value存储list,只能实现最简单的列表功能(按照id或时间先后排序,例如使用memcache的append或prepend协议),其他list操作只能靠客户端操作,性能很差,如果数据量较大,操作时间是无法接受的,并发也会遇到巨大挑战);
    3. key-list系统key对应的”value”是一个list(eg.set list),可以对list中的单个item进行操作,理想的key-list需要如下特点:
      1. list可以是海量的,且操作性能高效;
      2. list是可以是有序的,且可动态调整顺序;
    4. 使用场景:
      1. 论坛中的主题列表,回复列表;
      2. 微博中的用户关注列表,用户feed列表,用户关注feed列表;
      3. 最近访问列表;
      4. 集合操作:求交集,并集,差集(sdiff/sinter/sunion);
      5. 好友推荐;
      6. 排行榜;
  3. 下载:
    1. 官方地址: http://redis.io/;
    2. 下载地址: http://redis.io/download;
    3. 文档地址: http://redis.io/documentation;
    4. 作者blog: http://oldblog.antirez.com/;
  4. 安装:
    1. 解压缩到/usr/local目录下:tar -zxvf /tools/redis-2.6.13.tar.gz -C /usr/local/;                            
    2. 编译:cd /usr/local/redis-2.6.13/;make;                 
    3. 安装时报错:undefined reference to `__sync_add_and_fetch_4′;  
    4. 是因为CPU信息判断失误引起的,所以需要确定CPU的信息,然后把CPU的信息设置到环境变量中,清除旧的编译文件,最后重新编译即可:uname -a(uname -m);export CFLAGS=-march=i686;cd /usr/local/redis-2.6.13/;make distclean;make;              
    5. 如果需要运行make-test的话需要安装8.5版本以上的tcl;
    6. 此时就会在src目录下出现redis-server的服务端和redis-cli客户端,为了方便使用可以分别创建软连接:ln -s /usr/local/redis-2.6.13/src/redis-server /usr/local/redis-2.6.13/src/redisd;ln -s /usr/local/redis-2.6.13/src/redis-cli /usr/local/redis-2.6.13/src/redis;                                 
    7. 添加PATH变量:PATH=$PATH:/usr/local/redis-2.6.13/src;                                  
    8. 启动服务端:
      1. 默认配置启动:redisd;
      2. 使用自己的配置文件启动:redisd /etc/redis.conf;
      3. 在后台运行:redisd /etc/redis.conf &;                                     
    9. 启动客户端:redis;                                        
  5. 驱动下载:
    1. 官网提供的支持语言的下载地址: http://redis.io/clients;
    2. Jedis的github地址:https://github.com/xetorthio/jedis;
    3. jar包下载地址:https://github.com/xetorthio/jedis/downloads;
    4. 提供的方法:
      1. Sorting
      2. Connection handling
      3. Commands operating on any kind of values
      4. Commands operating on string values
      5. Commands operating on hashes
      6. Commands operating on lists
      7. Commands operating on sets
      8. Commands operating on sorted sets
      9. Transactions
      10. Pipelining
      11. Publish/Subscribe
      12. Persistence control commands
      13. Remote server control commands
      14. Connection pooling
      15. Sharding (MD5, MurmureHash)
      16. Key-tags for sharding
      17. Sharding with pipelining
  6. 配置文件;
# Redis configuration file example
 
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
 
# By default Redis does not run as a daemon. Use ‘yes’ if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes
 
# When running daemonized, Redis writes a pid file in /var/run/redis.pid by
# default. You can specify a custom pid file location here.
pidfile /var/run/redis.pid
 
# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379
 
# If you want you can bind a single interface, if the bind option is not
# specified all the interfaces will listen for incoming connections.
#
# bind 127.0.0.1
 
# Specify the path for the unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocketperm 755
 
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0
 
# TCP keepalive.
#
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Take the connection alive from the point of view of network
#    equipment in the middle.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
# On other kernels the period depends on the kernel configuration.
#
# A reasonable value for this option is 60 seconds.
tcp-keepalive 0
 
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
 
# Specify the log file name. Also ‘stdout’ can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile stdout
 
# To enable logging to the system logger, just set ‘syslog-enabled’ to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no
 
# Specify the syslog identity.
# syslog-ident redis
 
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
 
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and ‘databases’-1
databases 16
 
################################ SNAPSHOTTING  #################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving at all commenting all the “save” lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save “”
 
#save 900 1
#save 300 10
#save 60 10000
 
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in an hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# distater will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usually even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes
 
# Compress string objects using LZF when dump .rdb databases?
# For default that’s set to ‘yes’ as it’s almost always a win.
# If you want to save some CPU in the saving child set it to ‘no’ but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
 
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes
 
# The filename where to dump the DB
dbfilename dump.rdb
 
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the ‘dbfilename’ configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
 
################################# REPLICATION #################################
 
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. Note that the configuration is local to the slave
# so for example it is possible to configure the slave to save the DB with a
# different interval, or to listen to another port, and so on.
#
# slaveof <masterip> <masterport>
 
# If the master is password protected (using the “requirepass” configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
# masterauth <master-password>
 
# When a slave loses its connection with the master, or when the replication
# is still in progress, the slave can act in two different ways:
#
# 1) if slave-serve-stale-data is set to ‘yes’ (the default) the slave will
#    still reply to client requests, possibly with out of date data, or the
#    data set may just be empty if this is the first synchronization.
#
# 2) if slave-serve-stale-data is set to ‘no’ the slave will reply with
#    an error “SYNC with master in progress” to all the kind of commands
#    but to INFO and SLAVEOF.
#
slave-serve-stale-data yes
 
# You can configure a slave instance to accept writes or not. Writing against
# a slave instance may be useful to store some ephemeral data (because data
# written on a slave will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a
# misconfiguration.
#
# Since Redis 2.6 by default slaves are read-only.
#
# Note: read only slaves are not designed to be exposed to untrusted clients
# on the internet. It’s just a protection layer against misuse of the instance.
# Still a read only slave exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extend you can improve
# security of read only slaves using ‘rename-command’ to shadow all the
# administrative / dangerous commands.
slave-read-only yes
 
# Slaves send PINGs to server in a predefined interval. It’s possible to change
# this interval with the repl_ping_slave_period option. The default value is 10
# seconds.
#
# repl-ping-slave-period 10
 
# The following option sets a timeout for both Bulk transfer I/O timeout and
# master data or ping response timeout. The default value is 60 seconds.
#
# It is important to make sure that this value is greater than the value
# specified for repl-ping-slave-period otherwise a timeout will be detected
# every time there is low traffic between the master and the slave.
#
# repl-timeout 60
 
# Disable TCP_NODELAY on the slave socket after SYNC?
#
# If you select “yes” Redis will use a smaller number of TCP packets and
# less bandwidth to send data to slaves. But this can add a delay for
# the data to appear on the slave side, up to 40 milliseconds with
# Linux kernels using a default configuration.
#
# If you select “no” the delay for data to appear on the slave side will
# be reduced but more bandwidth will be used for replication.
#
# By default we optimize for low latency, but in very high traffic conditions
# or when the master and slaves are many hops away, turning this to “yes” may
# be a good idea.
repl-disable-tcp-nodelay no
 
# The slave priority is an integer number published by Redis in the INFO output.
# It is used by Redis Sentinel in order to select a slave to promote into a
# master if the master is no longer working correctly.
#
# A slave with a low priority number is considered better for promotion, so
# for instance if there are three slaves with priority 10, 100, 25 Sentinel will
# pick the one wtih priority 10, that is the lowest.
#
# However a special priority of 0 marks the slave as not able to perform the
# role of master, so a slave with priority of 0 will never be selected by
# Redis Sentinel for promotion.
#
# By default the priority is 100.
slave-priority 100
 
################################## SECURITY ###################################
 
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands.  This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
# requirepass foobared
 
# Command renaming.
#
# It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# hard to guess so that it will still be available for internal-use tools
# but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG “”
#
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to slaves may cause problems.
 
################################### LIMITS ####################################
 
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error ‘max number of clients reached’.
#
# maxclients 10000
 
# Don’t use more memory than the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# accordingly to the eviction policy selected (see maxmemmory-policy).
#
# If Redis can’t remove keys according to the policy, or if the policy is
# set to ‘noeviction’, Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#
# This option is usually useful when using Redis as an LRU cache, or to set
# an hard memory limit for an instance (using the ‘noeviction’ policy).
#
# WARNING: If you have slaves attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the slaves are subtracted
# from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output
# buffer of slaves is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied.
#
# In short… if you have slaves attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for slave
# output buffers (but this is not needed if the policy is ‘noeviction’).
#
# maxmemory <bytes>
 
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don’t expire at all, just return an error on write operations
#
# Note: with any of the above policies, Redis will return an error on write
#       operations, when there are not suitable keys for eviction.
#
#       At the date of writing this commands are: set setnx setex append
#       incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
#       sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
#       zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
#       getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy volatile-lru
 
# LRU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can select as well the sample
# size to check. For instance for default Redis will check three keys and
# pick the one that was used less recently, you can change the sample size
# using the following configuration directive.
#
# maxmemory-samples 3
 
############################## APPEND ONLY MODE ###############################
 
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
 
appendonly yes
 
# The name of the append only file (default: “appendonly.aof”)
# appendfilename appendonly.aof
 
# The fsync() call tells the Operating System to actually write data on disk
# instead to wait for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don’t fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log . Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is “everysec”, as that’s usually the right compromise between
# speed and data safety. It’s up to you to understand if you can relax this to
# “no” that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that’s snapshotting),
# or on the contrary, use “always” that’s very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use “everysec”.
 
# appendfsync always
appendfsync everysec
# appendfsync no
 
# When the AOF fsync policy is set to always or everysec, and a background
# saving process (a background save or AOF log background rewriting) is
# performing a lot of I/O against the disk, in some Linux configurations
# Redis may block too long on the fsync() call. Note that there is no fix for
# this currently, as even performing fsync in a different thread will block
# our synchronous write(2) call.
#
# In order to mitigate this problem it’s possible to use the following option
# that will prevent fsync() from being called in the main process while a
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving, the durability of Redis is
# the same as “appendfsync none”. In practical terms, this means that it is
# possible to lose up to 30 seconds of log in the worst scenario (with the
# default Linux settings).
#
# If you have latency problems turn this to “yes”. Otherwise leave it as
# “no” that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
 
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
 
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
 
################################ LUA SCRIPTING  ###############################
 
# Max execution time of a Lua script in milliseconds.
#
# If the maximum execution time is reached Redis will log that a script is
# still in execution after the maximum allowed time and will start to
# reply to queries with an error.
#
# When a long running script exceed the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# is the only way to shut down the server in the case a write commands was
# already issue by the script but the user don’t want to wait for the natural
# termination of the script.
#
# Set it to 0 or a negative value for unlimited execution without warnings.
lua-time-limit 5000
 
################################## SLOW LOG ###################################
 
# The Redis Slow Log is a system to log queries that exceeded a specified
# execution time. The execution time does not include the I/O operations
# like talking with the client, sending the reply and so forth,
# but just the time needed to actually execute the command (this is the only
# stage of command execution where the thread is blocked and can not serve
# other requests in the meantime).
#
# You can configure the slow log with two parameters: one tells Redis
# what is the execution time, in microseconds, to exceed in order for the
# command to get logged, and the other parameter is the length of the
# slow log. When a new command is logged the oldest one is removed from the
# queue of logged commands.
 
# The following time is expressed in microseconds, so 1000000 is equivalent
# to one second. Note that a negative number disables the slow log, while
# a value of zero forces the logging of every command.
slowlog-log-slower-than 10000
 
# There is no limit to this length. Just be aware that it will consume memory.
# You can reclaim memory used by the slow log with SLOWLOG RESET.
slowlog-max-len 128
 
############################### ADVANCED CONFIG ###############################
 
# Hashes are encoded using a memory efficient data structure when they have a
# small number of entries, and the biggest entry does not exceed a given
# threshold. These thresholds can be configured using the following directives.
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
 
# Similarly to hashes, small lists are also encoded in a special way in order
# to save a lot of space. The special representation is only used when
# you are under the following limits:
list-max-ziplist-entries 512
list-max-ziplist-value 64
 
# Sets have a special encoding in just one case: when a set is composed
# of just strings that happens to be integers in radix 10 in the range
# of 64 bit signed integers.
# The following configuration setting sets the limit in the size of the
# set in order to use this special memory saving encoding.
set-max-intset-entries 512
 
# Similarly to hashes and lists, sorted sets are also specially encoded in
# order to save a lot of space. This encoding is only used when the length and
# elements of a sorted set are below the following limits:
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
 
# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
# order to help rehashing the main Redis hash table (the one mapping top-level
# keys to values). The hash table implementation Redis uses (see dict.c)
# performs a lazy rehashing: the more operation you run into an hash table
# that is rehashing, the more rehashing “steps” are performed, so if the
# server is idle the rehashing is never complete and some more memory is used
# by the hash table.
#
# The default is to use this millisecond 10 times every second in order to
# active rehashing the main dictionaries, freeing memory when possible.
#
# If unsure:
# use “activerehashing no” if you have hard latency requirements and it is
# not a good thing in your environment that Redis can reply form time to time
# to queries with 2 milliseconds delay.
#
# use “activerehashing yes” if you don’t have such hard requirements but
# want to free memory asap when possible.
activerehashing yes
 
# The client output buffer limits can be used to force disconnection of clients
# that are not reading data from the server fast enough for some reason (a
# common reason is that a Pub/Sub client can’t consume messages as fast as the
# publisher can produce them).
#
# The limit can be set differently for the three different classes of clients:
#
# normal -> normal clients
# slave  -> slave clients and MONITOR clients
# pubsub -> clients subcribed to at least one pubsub channel or pattern
#
# The syntax of every client-output-buffer-limit directive is the following:
#
# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
#
# A client is immediately disconnected once the hard limit is reached, or if
# the soft limit is reached and remains reached for the specified number of
# seconds (continuously).
# So for instance if the hard limit is 32 megabytes and the soft limit is
# 16 megabytes / 10 seconds, the client will get disconnected immediately
# if the size of the output buffers reach 32 megabytes, but will also get
# disconnected if the client reaches 16 megabytes and continuously overcomes
# the limit for 10 seconds.
#
# By default normal clients are not limited because they don’t receive data
# without asking (in a push way), but just after a request, so only
# asynchronous clients may create a scenario where data is requested faster
# than it can read.
#
# Instead there is a default limit for pubsub and slave clients, since
# subscribers and slaves receive data in a push fashion.
#
# Both the hard or the soft limit can be disabled by setting them to zero.
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
 
# Redis calls an internal function to perform many background tasks, like
# closing connections of clients in timeot, purging expired keys that are
# never requested, and so forth.
#
# Not all tasks are perforemd with the same frequency, but Redis checks for
# tasks to perform accordingly to the specified “hz” value.
#
# By default “hz” is set to 10. Raising the value will use more CPU when
# Redis is idle, but at the same time will make Redis more responsive when
# there are many keys expiring at the same time, and timeouts may be
# handled with more precision.
#
# The range is between 1 and 500, however a value over 100 is usually not
# a good idea. Most users should use the default of 10 and raise this up to
# 100 only in environments where very low latency is required.
hz 10
 
# When a child rewrites the AOF file, if the following option is enabled
# the file will be fsync-ed every 32 MB of data generated. This is useful
# in order to commit the file to the disk more incrementally and avoid
# big latency spikes.
aof-rewrite-incremental-fsync yes
 
################################## INCLUDES ###################################
 
# Include one or more other config files here.  This is useful if you
# have a standard template that goes to all Redis server but also need
# to customize a few per-server settings.  Include files can include
# other files, so use this wisely.
#
# include /path/to/local.conf
# include /path/to/other.conf

MongoDB学习08–驱动实践

驱动实践

  1. 目前驱动有两种:
    1. 官方驱动:
      1. 官方页面:https://github.com/mongodb;
      2. java驱动下载:https://github.com/mongodb/mongo-java-driver/downloads;
      3. mongodb主页:http://docs.mongodb.org/ecosystem/drivers/;
    2. samus驱动:
      1. 官方页面:https://github.com/samus;
      2. c#驱动下载: https://github.com/samus/mongodb-csharp/downloads;
  2. 有时间使用java写一下CRUD的例子;

MongoDB学习07–运维技术

运维技术

  1. 常见的运维技术:
    1. 安装部署;
    2. 状态监控;
    3. 安装认证;
    4. 备份与恢复;
  2. 安装部署:
    1. mongod进程总是停留在命令行窗口下,很容易误操作给结束掉;而且日志信息总是在命令行下打印,不便于之后的查看,可以把mongod作为后台进程运行,并把日志输出到文件中;
    2. Linux中:mongod –dbpath=/mongo/data –logpath=/mongo/logs/mongod.log –logappend –port=27017 –fork;                         
    3. Windows中:mongod –dbpath=D:\mongo\data –logpath=D:\mongo\logs\mongod.log –logappend –port=27017 –install;之后就可以通过[net start/stop MongoDB]命令来开启和关闭mongodb数据库了;
    4. Linux下开机启动,可以编辑/etc/rc.d/rc.local文件,把mongod启动的脚本添加进去即可;
    5. Linux下关闭mongod服务器:
      1. 如果没有使用–fork命令,则直接退出终端即可,mongodb会自动清理;
      2. 如果使用了–fork命令,在admin数据库下执行:db.shutdownServer();
      3. 如果在Master-Slave集群中,如果备机和主机时间相差超过10s的话不会关闭mongod,可以通过配置timeoutSecs来完成mongod的数据更新,时间差在10s以内的话就可以关闭了:db.shutdownServer({force : true, timeoutsec : 5});
  3. 监控状态:
    1. http监控器,通过页面打开,网页使用端口是mongod服务端口加1000:http://mongod-host:port;
    2. 打开客户端调用db.serverStatus()获得服务器的状态,包括服务器基本信息,锁,索引,内存,连接,游标,网络和用户操作行为等信息;
    3. 使用客户端工具mongostat来实时查看统计信息(具体的字段含义可以通过mongostat -h查看),每秒刷新一次:mongostat –port=27017 –all;                           
  4. 安全认证:
    1. mongodb身份验证的机制:
      1. 当启动mongod服务的时候不加–auth参数,默认是没有权限验证的,可以直接登陆数据库做任何操作而且可以远程访问数据库,相当于都是超级用户的权限;
      2. 当启动mongod服务的时候添加–auth参数,如果没有在admin数据库中添加过用户(即admin.system.users不存在或者为空),那么还是可以做任何操作,直到在admin.system.users中添加了一个用户(如果没有在admin.system.users中添加用户,而在其它数据库下添加了用户,它们依然是超级用户的权限,因为只有admin.system.users中有用户后mongod的认证授权服务才生效);
      3. admin.system.users中保存的用户都是超级用户,可以对其它的数据库进行操作,而其它数据库中的用户只能对自己数据库中的集合(不论是哪个用户创建的集合)进行操作;
      4. mongodb中,数据库是由超级用户来创建的,一个数据库可以包含多个用户,但是一个用户只能在一个数据库中操作,不同的数据库中的用户可以同名;
    2. 创建用户的语法:db.addUser(username, password[, readOnly=false]);
      1. username:用户名;
      2. password:访问的密码;
      3. readOnly:默认是false,即有读写的权限,如果设置为true的话,则只有读的权限;
    3. 创建超级用户:mongo –port=27017 admin;db.addUser(“root”, “mongo”);                                           
    4. 验证用户:
      1. 可以在登陆时指定用户名密码:mongo 127.0.0.1:27017/admin -uroot -pmongo;                                
      2. 登陆之后使用db.auth()函数验证用户:db.auth(“root”, “mongo”),返回1表示成功,返回0表示失败;                      
    5. 查看用户:db.getCollection(‘system.users’).find();                               
    6. 删除用户:db.removeUser(“root”),也可以使用db.system.users.remove()操作,如果删除所有admin.system.users中的用户,则就没有安全验证了;                                    
    7. 其它数据库中创建用户的做法相同;
  5. 备份与恢复:
    1. 冷备份:在关闭服务器的情况下直接拷贝mongodb的数据目录,不推荐;
    2. mongodump和mongorestore:mongodb提供的内置工具,提供了热备功能,备份的父目录为/mongo/backup;
      1. mongodump:mongodump -h 127.0.0.1 –port 27017 -d test -o /mongo/backup;                      
      2. mongorestore:mongorestore -h 127.0.0.1 –port 27017 -d test -drop /mongo/backup/test;(–drop选项是指定导入数据之前先删除原来的数据)                             
      3. 在导出的时候可能数据的实时性不能保证,因为导出的时候可能数据还在内存中,可以使用mongodb提供的fsync+lock机制把缓冲区数据暴力刷入硬盘,然后给数据库一个写入锁,所有的写入都会被阻塞,直到fsync+lock释放锁为止;
        1. 加锁:db.runCommand({“fsync”:1, “lock”:1});
        2. 释放锁:db.$cmd.unlock.findOne();
    3. Master-Slave架构;

MongoDB学习06–分片技术

分片技术

  1. 当数据量达到T级别的时候,对CPU,内存和磁盘的压力都很大,这个时候就需要采用分片技术,将MongoDB中的集合进行拆分,分担到多个片(即多台MongoDB服务器)上;
  2. 跨服务器的数据拆分中,Sharding是一个有效的方法;MongoDB中支持自动化Sharding,但是对数据库性能会造成很大影响;因此建议用户尽早进行Sharding,使用MMS:Munin (+ Mongo plugin)和CloudWatch等工具对MongoDB进行监控,确保系统资源使用达到80%之前就完成Sharding工作;
  3. 分片技术中各个组件的职能及拓扑图:
    1. clients:客户端,即发出数据请求,并得到返回结果;
    2. config:保存数据和片的对应关系以及相应的配置信息,配置节点推荐使用使用主备,防止单点故障;
    3. mongos:即路由服务器,根据设置的片键(即集合拆分的依据)将数据分摊到不同的mongod集群;
    4. mongod:普通的MongoDB服务器,就是所谓的片;
    5. 拓扑图;                                                                                                 
  4. 使用不同的目录和端口来模拟不同的服务器:
    1. config:mongod –dbpath=/mongo/config –port=20001;
    2. mongos:mongos –port=20002 –configdb=127.0.0.1:20001;
    3. mongod1:mongod –dbpath=/mongo/mongod1 –port=30001;
    4. mongod2:mongod –dbpath=/mongo/mongod2 –port=30002;
    5. mongod3:mongod –dbpath=/mongo/mongod3 –port=30003;
  5. 开启config服务器:mongod –dbpath=/mongo/config –port=20001;                             
  6. 启动mongos服务器(不需要指定数据目录,因为它只是起到路由器的功能),同时要指定config服务器,因为要写入配置信息:mongos –port=20002 –configdb=127.0.0.1:20001;                               
  7. 分别启动三台mongod服务器:mongod –dbpath=/mongo/mongodX –port=3000X;
  8. 配置mongos服务:
    1. 连接mongos服务并添加分片节点:mongo 127.0.0.1:20002/admin;                                
    2. 开启数据库的分片功能,以test数据库为例:db.runCommand({“enablesharding”:”test”});                                  
    3. 指定集合的分片键,以user集合的name列为例:                                             
  9. 查看效果:
    1. 通过mongos插入10w条测试数据;                                         
    2. 查看数据的分布情况:db.printShardingStatus();                                
    3. 结果分析:
      1. shards:查看到已经分成了shard0000,shard0001,shard0002三个片;
      2. databases:看到test的partitioned属性为true;
      3. chundks:被分成了$minKey(无穷小)->user0, user0->user9999, user9999->$maxKey(无穷大)三段;
———————— 配置mongos服务器 ————————
— 1.连接mongos服务器;
mongo 127.0.0.1:20002/admin
— 2.添加分片;
db.runCommand({addshard:”127.0.0.1:30001″, allowLocal:true})
db.runCommand({addshard:”127.0.0.1:30002″, allowLocal:true})
db.runCommand({addshard:”127.0.0.1:30003″, allowLocal:true})
— 3.开启数据库的分片功能;
db.runCommand({“enablesharding”:”test”})
— 4.指定片键;
db.runCommand({“shardcollection”:”test.user”, “key”:{“name”:1}})
———————— 配置mongos服务器 ————————
———————— 插入测试数据并查看结果 ————————
— 1.连接mongos服务器的test数据库;
mongo 127.0.0.1:20002/test
— 2.插入10w条测试数据;
for(var i = 0; i< 100000; i++){ db.user.insert({name:”user”+i, age:i}) }
— 3.查看数据分布情况;
db.printShardingStatus()
———————— 插入测试数据并查看结果 ————————

MongoDB学习05–Master-Slave架构及副本集

Master-Slave架构及副本集

  1. Master-Slave架构的优点及拓扑图:
    1. 主要的优点:
      1. 解决单点故障问题;
      2. 实现数据的备份和恢复;
      3. 实现数据的读写分离;
    2. 拓扑图;                                                                                                              
    3. 使用不同的目录和端口来模拟不同的服务器:
      1. master:mongod –dbpath=/mongo/master –port=27017 –master;
      2. slave1:mongod –dbpath=/mongo/slave1 –port=30001 –slave –source=127.0.0.1:27017;
      3. slave2:mongod –dbpath=/mongo/slave2 –port=30002 –slave;
  2. 启动Master数据库:mongod –dbpath=/mongo/master –port=27017 –master,在master和slave的服务器上都会有一个local的集合,用来存放内部的复制信息;                      
  3. 配置Salve1数据库,并在启动时就与Master数据库保持同步:mongod –dbpath=/mongo/slave1 –port=30001 –slave –source=127.0.0.1:27017,会看到有一个rep1的进程从Master服务器同步数据;           
  4. 把原来的Slave2服务器添加到Master-Slave架构中:
    1. 首先启动一台Slave2服务器,并不指定Master源:mongod –dbpath=/mongo/slave2 –port=30002 –slave;                               
    2. 连接到Slave2服务器,在local数据库下添加Master服务器信息,然后查看,同步完成后会出现同步的时间戳;                                    
    3. 可以通过观察系统日志或者查看集合的记录数来查看同步的状况;
  5. 查看Master, Slave的状态:
    1. 查看master服务器的状态:db.printReplicationInfo();                     
    2. 查看slave服务器的状态:db.printReplicationInfo();                           
  6. 读写分离:
    1. 默认情况下Salve服务器不支持数据的读取,可以通过驱动中的slaveOkay来显示读取Slave数据库,从而减轻Master服务器的压力;
    2. 也可以让前端程序直接连接到Slave服务器读取数据;
  7. 副本集:
    1. 与Master-Slave不同的是,它没有特定的主数据库,如果哪个主数据库宕机了,集群中就会推选出一个从属的数据库作为主数据库,具有自动故障恢复功能,所以一定要保证副本集为奇数个,否则会发生主节点宕机,其它节点为只读的情况;
    2. 创建一个名为snda的集群;
    3. 使用不同的服务器和端口来模拟不同的服务器:
      1. rep1:mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003;
      2. rep2:mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003;
      3. rep3:mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
      4. arbriter:mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;
  8. 打开第一个副本服务器rep1:mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003,replSet参数表示让服务器知道副本集snda下还有其它的服务器;     
  9. 打开第二个副本服务器rep2:mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003,会与其它的副本进行通信,并读取副本集的配置;             
  10. 同样的方法打开第三个副本集rep3:mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
  11. 添加副本集的配置信息,在任意一个副本上的admin数据库进行配置,这里选择rep1:db.runCommand({replSetInitiate:{_id:”snda”, members:[{_id:1, host:”127.0.0.1:30001″}, {_id:2, host:”127.0.0.1:30002″}, {_id:3, host:”127.0.0.1:30003″}]}});                      
  12. 此时可以从日志看出哪一个副本已经成为了Primary服务器,哪一个副本是Secondary服务器,可以看到rep2和rep3都投票给rep1作为primary服务器;当然也可以通过查看状态查询:rs.status();               
  13. 启动仲裁服务器:mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;                            
  14. 添加仲裁服务器配置,可以通过snda副本集中任意一个副本的admin数据库添加,这里以rep1为例:rs.addArb(“127.0.0.1:30000”);                         
  15. 查看副本集群中各个服务器的状态:rs.status();                                          
  16. 测试自动故障恢复功能,关掉当前的主服务器,之后通过rs.status()查看状态,发现自动投票选择了新的主服务器;                                        
  17. 当rep1重新修复后加入集群,自动成为了Secondary服务器;
—————————– 已开启的Slave服务器加入到Master-Slave架构中 —————————–
mongo 127.0.0.1:30002/local
db.sources.insert({host:”127.0.0.1:27017″})
db.sources.find()
db.sources.find()
—————————– 已开启的Slave服务器加入到Master-Slave架构中 —————————–
—————————– MongoDB副本集的配置 —————————–
— 1.添加三个副本,并互相告知剩余的成员;
mongod –dbpath=/mongo/rep1 –port=30001 –replSet=snda/127.0.0.1:30002,127.0.0.1:30003;
mongod –dbpath=/mongo/rep2 –port=30002 –replSet=snda/127.0.0.1:30001,127.0.0.1:30003;
mongod –dbpath=/mongo/rep3 –port=30003 –replSet=snda/127.0.0.1:30001,127.0.0.1:30002;
— 2.添加副本集的配置信息;
mongo 127.0.0.1:30001/admin
db.runCommand({replSetInitiate:{_id:”snda”, members:[{_id:1, host:”127.0.0.1:30001″}, {_id:2, host:”127.0.0.1:30002″}, {_id:3, host:”127.0.0.1:30003″}]}})
— 3.启动仲裁服务器;
mongod –dbpath=/mongo/arbiter –port=30000 –replSet=snda/127.0.0.1:30001;
— 4.添加仲裁服务器的配置信息;
mongo 127.0.0.1:30001/admin
rs.addArb(“127.0.0.1:30000”)
— 5.查看副本集各个成员的状态;
rs.status()
—————————– MongoDB副本集的配置 —————————–

MongoDB学习04–性能分析及索引操作

MongoDB中的性能分析及索引操作

  1. 性能分析函数explain();
    1. 语法:cursor.explain(verbose),verbose为true或者1的时候会输出所有的执行计划和旧的执行计划;
    2. 一般是跟在find()操作后使用(eg:db.mycoll.find().explain()),它的执行速度决定于实际查询的速度,尽管explain()会额外生成一系列候选的执行计划,但是跟实际查询的性能相差不大;
    3. explain函数的输出结果;                                                          
    4. explain()函数输出结果含义:
      1. cursor:查找使用的cursor类型和索引名称;
      2. isMultiKey:是否是联合索引;
      3. n:返回记录的个数;
      4. nscannedObjects:遍历的文档对象的个数;
      5. nscanned:遍历的文档的个数;
      6. nscannedObjectsAllPlans: <num>;
      7. nscannedAllPlans: <num>;
      8. scanAndOrder: <boolean>;
      9. indexOnly: <boolean>;
      10. nYields: <num>;
      11. nChunkSkips: <num>;
      12. millis:消耗的时间,单位是ms;
      13. indexBounds:索引的范围,如果为空表示没有用到索引;
      14. llPlans:产生的所有的候选的执行计划;
      15. oldPlan:旧的执行计划;
      16. server:主机名和端口号<host:port>;
  2. 创建/删除索引,及前后效率的对比;
    1. 创建索引语法:db.mycoll.ensureIndex(keypattern[,options]);
      1. keypattern:是索引的键;
      2. options:可以取name,unique,dropDups,sparse等值;
      3. 只有在某个列上没有索引才会创建,如果存在索引的话就不再创建;
      4. 默认创建的是b-tree索引;
      5. 可以在子文档上创建索引:db.mycoll.ensureIndex({“object.attritude”:1});
    2. 没有创建索引时,查找name=index10000的效率:db.index.find({“name”:”index10000″}).explain();使用的是BasicCursor即没有索引,扫描了10w条记录后返回1条记录,消耗了192ms;              
    3. 在name列创建一个名为idx_index_name的索引:db.index.ensureIndex({name:1}, {name:”idx_index_name”}),如果不指定第二个参数中的name的话,则系统会自动生成一个索引的名称;          
    4. 查看创建索引后的效率,发现使用了Btree索引,一共扫描一条记录,返回1条记录,并且瞬间返回;                                            
    5. 删除索引语法:db.mycoll.dropIndex(index),可以根据名称删除索引,或者根据创建时指定的键值;
      1. db.mycoll.dropIndex(“indexName”):根据索引名称删除;
      2. db.mycoll.dropIndex({ “indexKey” : 1 }):根据key值删除索引;
      3. 如果要删除某个集合下所有的索引,则使用db.mycoll.dropIndexes(),但是无法删除系统自动创建的基于_id列的索引,它由数据库自己维护;
    6. 重建索引:db.mycoll.reIndex(),是把原来的索引删掉后重建;                                                        
  3. 唯一索引:
    1. 语法:db.mycoll.ensureIndex(keypattern, {unique:true});
    2. 创建一个基于name列的唯一索引unq_index_name:db.index.ensureIndex({name:1}, {name:”unq_index_name”, unique:true});
    3. 如果某一个列上已经存在的重复值,在这样的列上创建唯一索引的话可以使用dropDups选项,这样系统会保留第一条记录,删除剩下重复的记录:db.mycoll.ensureIndex(keypattern, {unique:true, dropDups:true});
    4. 如果某一个列是唯一列,插入数据时没有插入此列的话会保存一个null值,一个唯一列中只能有一个null值;(这点与关系型数据库不同,关系型数据库中唯一列中可以保存null值,但是在mongodb中null值作为了一个值来比较)
  4. 组合索引:
    1. 语法与普通索引相同,只是需要多指定几个列:db.mycoll.ensureIndex({key1:value1, key2:value2, …});
    2. 键后面的数字表示索引的存储顺序,其中1表示升序,-1表示降序.在随机访问的时候意义不大,但是在排序或者是范围查询时很重要;
    3. 如果在a,b,c三列上创建了这索引,则以a,ab,abc开头的条件都可以使用到此索引;
  5. 稀疏索引:
    1. Sparse indexes only contain entries for documents that have the indexes field, any document that is missing the field is not indexes;
    2. By contrast, non-sparse indexes contrain all documents in collection, and store null values for documents that do not contrain the indexes field;
    3. 语法:db.mycoll.ensureIndex(keypattern, {sparse:true});
    4. 稀疏索引与非稀疏索引的对比;
  6. hint:
    1. 查询优化器会选择最优的执行计划,而且第一次执行后的执行计划将会被保存;如果需要使用自己指定的查询方案的话可以使用hint;
    2. 语法:cursor.hint(index);
      1. db.mycoll.find().hint(“index_name”):通过指定索引名称来实现hint;
      2. db.mycoll.find().hint({key:value}):通过指定创建索引时的key的顺序来实现hint,可以之前通过db.mycoll.getIndexes()查看;
    3. 查看hint的性能:db.mycoll.find().hint(index).explain();
  7. 注意事项:
    1. MongoDB中的索引是大小写敏感的;
    2. 当更新对象时,只有在索引上的这些key发生变化才更新,所以提高了性能;当对象增长了或者移动时,所有的索引都必须更新,会很慢;
    3. 索引的信息会保存在system.indexes集合中,运行db.system.indexes.find()可以查看这些数据;
    4. 索引的字段的大小限制目前是800bytes,大于之后可以在这个字段上创建索引,但是该字段不会被索引;
    5. 如果数据集合比较小(4M),可以联合使用limit()和sort()函数而不需要创建索引来查询数据;
———————- 10w条测试数据 ———————-
db.index.remove()
for(var i = 0; i < 100000; i++){
     db.index.insert({name:”index”+i, age:i})
}
———————- 10w条测试数据 ———————-
———————- 稀疏索引与非稀疏索引的对比 ———————-
— 1.测试数据;
db.sparse.insert({name:”sparse1″, age:20})
db.sparse.insert({name:”sparse2″})
db.sparse.find()
— 2.在age列创建一个稀疏索引;
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”, sparse:true})
db.sparse.getIndexes()
— 3.查看数据,稀疏索引中不包含列为null的文档,只有稀疏索引中的文档才会被返回;
db.sparse.find()
db.sparse.find().sort({age:1})
db.sparse.find({name:{$ne:0}})
— 4.删除稀疏索引,并创建一般的索引;
db.sparse.dropIndex(“idx_sparse_age”)
db.sparse.ensureIndex({age:1}, {name:”idx_sparse_age”})
db.sparse.getIndexes()
— 查看数据,非稀疏索引中包含了列为null的文档;
———————- 稀疏索引与非稀疏索引的对比 ———————-

MongoDB学习03–聚合操作,游标的使用及排序分页操作

聚合操作,游标的使用及排序分页操作

  1. 聚合操作:
    1. count:查看符合某些条件的集合中记录的个数;
      1. 查看集合user的记录数:db.user.count();                                        
      2. 查看集合user中年龄大于20的记录的个数:db.user.count({“age”:{$gt:20}});                                  
    2. distinct:查看集合中某个属性的独立存在的个数;
      1. 获得不同年龄的数组:db.user.distinct(“age”);                               
      2. 获得不同年龄值的个数:db.user.distinct(“age”).length;                           
    3. group:对集合按照某一属性分组,形成一个K-V模型;
      1. 语法:db.mycoll.group( { key : …, initial: …, reduce : … [, finalize: …] [, condition: …] } );
        1. key:分组的键;
        2. initial:每个分组使用的初始化函数;
        3. reduce:此函数的第一个参数是当前的文档对象,第二个参数是上次函数(即initial函数)操作的累计对象,有多少个文档,$reduce就会调用多少次;
        4. finalize:每一组文档执行完之后,会触发执行的一个函数,参数是上次函数(即initial函数)操作的累计对象;
        5. condtion:过滤条件;
      2. 按照年龄分组的例子:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}}),push函数是把一个值加入到数组中;       
      3. 按照年龄分组,过滤掉年龄大于25的记录,并且添加一个用户数量的属性count:db.user.group({key:{age:true}, initial:{user:[]}, reduce:function(cur, prev){prev.user.push(cur.name);}, finalize:function(prev){prev.count=prev.user.length;}, condition:{age:{$lte:25}}});                               
    4. mapReduce:其实是一种编程模型,用在分布式计算中.可以把问题划分为多个不同的部分并分发到不同的服务器并行处理,每台服务器都把分配给自己的一部分处理完成后把结果集返回给主服务器,主服务器汇总结果后完成问题的处理;
      1. 语法:db.mycoll.mapReduce( mapFunction , reduceFunction , <optional params> );
        1. mapFunction:映射函数,里面会调用emit(key, value),集合会按照指定的key进行映射分组;
        2. reduceFunction:;简化函数,会对map分组后的数据进行分组简化.其中reduce(key, value)中的key就是emit中的key,value为emit分组后的emit(value)的集合,它有一个或者多个对应于键的文档组成;
        3. 可选参数;
      2. 原理:map首先将文档映射到集合并操作文档,这一步可能产生多个键和多个值或者什么也没有(文档中要处理的值为空).而后按照键分组,并将产生的值组成列表放到对应的键中,reduce则把列表中的值化简为一个值,这个值被返回,而后继续按键分组,进行化简,直到每个键在列表中只有一个值为止,这个值也就是最终结果;
      3. 例子;
  2. 游标的使用:
    1. 使用while循环遍历输出:var cursor=db.user.find(); while(cursor.hasNext()) { printjson(cursor.next()); };(其中hasNext()函数判断是否有记录,next()函数返回下一条记录)             
    2. pringjson()函数是内置函数,能够将结果输出为json格式;
    3. 得到数据集集合,遍历完之后游标便销毁;
    4. 使用forEach()函数遍历:db.user.find().forEach(printjson);                          
    5. 像访问数组一样使用游标:var cursor=db.user.find();printjson(cursor[0]);(会把访问的数据都加载ram中,如果数据量很大的话非常消耗内存)                
    6. 直接转换成数组访问:var array=db.user.find().toArray();array[0];                            
  3. 排序操作:
    1. 语法:db.mycoll.find().sort({col:value, …});
      1. col:表示按照排序的列;
      2. value:可以取1和-1,1表示升序,-1表示降序;
      3. 查找所有记录,并按照年龄升序,名称降序显示:db.user.find().sort({“age”:1, “name”:-1}),select name, age from user order by age desc, name asc;              
    2. 还可以通过其它方法实现,db.mycoll.find([query], [fields])操作是根据query过滤记录,先后显示fields里面指定的列,如果query给默认的话({}就是默认的参数),只需要指定列名及排序方式即可:select name, age from user order by age desc, name asc;                                 
    3. 先按年龄升序,然后按照名称升序排序:select name from user order by age asc, name asc;                      
  4. 分页操作:
    1. 使用db.mycoll.find().skip(n)操作和db.mycoll.find().limit(n)实现,前者是跳过之前多少条记录,后者是显示多少条记录:skip((pageIndex-1) * pageSize).limit(pageSize);
    2. 排序后的结果,每页两条记录,显示第二页:db.user.find().sort({“age”:1, “name”:-1}).skip(2).limit(2);                 
— 测试数据;
db.user.drop()
db.user.insert({“name”:”jack”, “age”:20})
db.user.insert({“name”:”joe”, “age”:22})
db.user.insert({“name”:”mary”, “age”:26})
db.user.insert({“name”:”kobe”, “age”:20})
db.user.insert({“name”:”wade”, “age”:22})
db.user.insert({“name”:”yi”, “age”:22})
db.user.find()
— mapReduce操作;
1.创建map函数;
function map(){ emit(this.age, {count:1}); }
2.创建reduce函数;
function reduce(key, value){
     var result = {count:0};
     for (var i = 0; i < value.length; i++){
          result.count += value[i].count;
     }
     return result;
}
3.执行mapReduce操作,并把结果集存放在集合collection中;
db.user.mapReduce(map, reduce, {out:”collection”})
result:存放集合名;
timeMillis:执行的时间,单位是毫秒;
input:传入文档的个数;
emit:emit函数被调用的次数;
reduce:reduce函数被调用的次数;
output:最后返回文档的个数;
4.查看collection中的结果;
db.collection.find()

MongoDB学习02–增删改查操作

MongoDB的增删改查操作

  1. INSERT操作:
    1. 单条插入(可以使用js的语法);                                          
    2. 批量插入:mongodb中并没有提供批量插入的语法,但是高级语言中提供了与mongodb批量插入的接口,也可以通过for循环来模拟;
    3. insert和save的区别:
      1. 如果不使用_id列,两个函数没有区别都是插入数据;
      2. 如果使用了_id列,insert插入_id重复的话会报错;save插入_id重复的话会调用upsert,即存在就更新,不存在就插入;
  2. FIND操作:
    1. 根据条件查询:[>, >=, <, <=, !=, =]分别对应[$gt, $gte, $lt, $lte, $ne, 无]关键字;                    
    2. 多条件查询:[and, or, in, notin]分别对应[无, $or, $in, $nin]关键字;                      
    3. 正则表达式查询;                                                       
    4. 使用$where子句查询;                                                   
    5. findOne()函数返回满足条件的第一条记录或者是null:printjson(db.user.findOne({name:”joe”}));                                 
  3. UPDATE操作:
    1. 整体更新;                                                            
    2. 局部更新,使用修改器$inc和$set来更新某一个字段;
      1. $inc:increase的缩写,即在原来值的基础上增加$inc指定的值,可以用于自增的主键;                                
      2. $set:即设置某个列的值为$set指定的值;                                         
    3. upsert操作:update操作的第一个参数是查询条件,第二个参数是要更新的值,如果设置第三个参数为true的话,就表示如果存在记录就更新,如果不存在就插入;                           
    4. multi操作:批量更新,如果update操作匹配到了多条记录,默认情况下只更新第一条,如果需要全部更新的话,需要把update操作中第四个参数设置为true;                           
  4. REMOVE操作:
    1. 根据条件删除:db.mycoll.remove(query);                                       
    2. 全部删除:db.mycoll.remove();                                                        
— insert操作;
var user={“name”:”jack”, “password”:”12345″, “age”:20, “address”:{“province”:”shanghai”, “city”:”shanghai”}, “favourite”:[“apple”, “banana”]}
db.user.insert(user);
user.name=”joe”
user.age=21
user.address={“province”:”beijing”, “city”:”beijing”}
user.favourite=[“sanguosha”, “dota”]
db.user.insert(user)
db.user.find()
— find操作;
1.查找年龄大于等于21岁的集合;
db.user.find({“age”:{$gte:21}})
2.查找年龄等于20岁的集合;
db.user.find({“age”:20})
3.查找名称为jack而且省份为shanghai的集合:name=’jack’ and province=’shanghai’;
db.user.find({“name”:”jack”, “address.province”:”shanghai”})
4.查找省份为上海或者北京的集合:province=’shanghai’ or province=’beijing’;
db.user.find({$or:[{“address.province”:”shanghai”}, {“address.province”: “beijing”}]})
5.查找省份在上海或者北京的集合:province in (‘shanghai’, ‘beijing’);
db.user.find({“address.province”:{$in:[“shanghai”, “beijing”]}})
6.查找省份不在上海的集合:province not in (‘shanghai’);
db.user.find({“address.province”:{$nin:[“shanghai”]}})
7.查找姓名以’j’开头并且以’e’结尾的集合:name like ‘j%e’;
db.user.find({“name”:/^j/, “name”:/e$/})
8.条件复杂的时候,可以使用$where子句,其实就是js的函数,查找名称为’jack’的集合:name=’jack’;
db.user.find({$where:function(){return this.name == ‘jack’}})
— update操作;
1.整体更新一条记录;
db.user.find({“name”:”jack”})
var model=db.user.findOne({“name”:”jack”})
model.age=30
db.user.update({“name”:”jack”}, model)
db.user.find({“name”:”jack”})
2.使用$inc修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$inc:{“age”:-5}})
db.user.find({“name”:”jack”})
3.使用$set修改器更新一个字段;
db.user.find({“name”:”jack”})
db.user.update({“name”:”jack”}, {$set:{“age”:20}})
db.user.find({“name”:”jack”})
4.upsert操作:查找到就更新,没有就插入;
db.user.find({“name”:”kobe”})
db.user.update({“name”:”kobe”}, {$set:{“age”:20}}, true)
db.user.find({“name”:”kobe”})
5.multi操作:批量更新记录;
db.user.find()
db.user.update({“age”:{$gte:20}}, {$inc:{“age”:5}}, false, true)
db.user.find()
— remove操作;
1.根据条件删除;
db.user.find()
db.user.remove({“name”:”kobe”})
db.user.find()
2.全部删除;
db.user.remove()
db.user.find()
db.user.count()

MongoDB学习01–Linux下安装MongoDB v2.2

Linux下安装MangoDB

  1. 官网地址:http://www.mongodb.org;
  2. 下载文件:从http://www.mongodb.org/downloads页面下载相应的版本,这里选择linux下32bit的2.2.4版本;
  3. 解压文件:tar -zxvf /tools/mongodb-linux-i686-2.2.4.tgz;
  4. 修改文件名,并移动到/usr/local目录下:mv mongodb-linux-i686-2.2.4 mongodb; cp -R  /tools/mongodb /usr/local/;
  5. 把mangodb的路径添加到path中:PATH=$PATH:/usr/local/mongodb/bin;
  6. 启动mongod服务并修改默认数据目录:
    1. mongodb默认的数据文件的路径是/data/db,需要预先创建,如果使用的是mongo用户的话,需要修改此目录的权限:chown mongo /data/db;
    2. 修改数据文件的路径:mkdir -p /mongo/data/journal;mongod –journal –dbpath /mongo/data/;(默认是不开启journal功能的,可以指定参数打开)                         
    3. 访问相应的端口查看信息:http://192.168.10.112:28017/;                                
  7. 使用配置文件方式启动mongod服务,每次启动都需要输入很长的参数比较麻烦,可以把参数写在/etc/mongod.conf文件中,然后采用:mongod -f /etc/mongod.conf的方式启动;             
  8. 登陆客户端,使用mongo命令,同时也是js的编辑器(可以使用一切js的语法),默认连接test数据库;(使用SecureCRT的话,命令总是会重复一次,推荐使用xshell)                                                   
  9. 获得帮助,使用help命令;                                                   
  10. 关闭mongod服务:mongod –dbpath /mongo/data –shutdown;                                           
  11. 如果非法关闭后再次打开时报错:mongod –journal –dbpath=/mongo/data,此时需要删除rm -rf /mongo/data/mongod.lock文件,然后再打开;                                        
  12. 对MongoDB的基本CRUD操作:
    1. 插入一个集合,是以bson(json的扩展)的形式插入的;                              
    2. find操作,_id字段是数据库默认加的guid,目的是保证数据的唯一性;                              
    3. update操作,第一个参数为查找的条件,第二个参数为更新的值;                                 
    4. remove操作,如果不加参数就会删除所有的数据,而且不能回滚;                                
  13. 官方给定的使用mongodb的一些建议:
    1. MongoDB分成32位版本和64位版本,由于MongoDB使用内存映射文件,所以32位版本只能存储2GB左右的数据;建议存储更多数据的用户使用64位版本;
    2. MongoDB是文档型数据库,数据以BSON形式存储在文档中;最新版本的MongoDB能够支持最大16MB的文档大小;建议用户尽量不要存储大型对象,将文档控制在1 MB以内;
    3. MongoDB的写入和更新速度非常快,所以错误提示并不明确;要确保写入正确,建议用户使用getLastError或者使用安全写入;
    4. 关系型数据库往往会有预定义的schema,你想添加额外的列就需要在整个表上添加;MongoDB没有这个约束,这使得开发和管理变得更简单;但这并不意味着你就可以完全忽视MongoDB的schema设计,一个设计良好的schema能够让MongoDB的性能达到最佳;
    5. MongoDB的更新在默认情况下会使用类似于传统数据库的LIMIT语句,即LIMIT 1.因此更新不会影响到所有的文档,如果你想要一次更新许多文档,那么请把multi设为true;
    6. MongoDB默认情况下是区分大小写的,例如db.people.find({name: ‘Russell’}) 和db.people.find({name: ‘russell’})就是不一样的;所以用户需要知道MongoDB的大小写限制;
    7. 传统数据库中,如果插入错误的数据类型,通常会提示错误或者强制转换成预定义的数据值;MongoDB中没有这种限制,所以输入错误数据类型不会出现提示;建议用户确保输入正确的数据类型;
    8. 全局锁是一直被MongoDB用户诟病的特性,MongoDB 2.2中增加了数据库级锁,这是一个很大的改进;建议用户使用稳定版的MongoDB 2.2数据库,避免全局锁限制;
    9. 过期版本MongoDB用户在下载程序包时会出问题,建议用户使用10gen最新版本的官方程序包;
    10. Replica Set是MongoDB中受关注最多的功能,它能为MongoDB集群增加冗余并提供良好的读性能;但由于Replica Set的选举机制,必须保证Replica Set成员数目为奇数;如果是偶数的话,主节点宕机就会导致其他节点变为只读;解决方法也可以使用一个仲裁节点(arbiter),它也是一个Replica Set的成员,但并不存储用户数据;所以请记住设置Replica Set成员时要定为奇数;
    11. MongoDB中不存在join,你要针对多个集合进行数据检索的时候,必须使用多个查询;所以当你遇到这个问题时,可以考虑重新设计MongoDB的schema;
    12. Journaling日志是MongoDB中非常好的功能,能够增强节点的可用性;在2.0版本之后,MongoDB默认是开启Journaling日志功能的;虽然Journaling日志会对数据库性能造成一定的影响,但这部分影响是可以忽略的;因此建议用户开启Journaling功能,特别是对于可用性要求较高的用户;
    13. MongoDB默认情况下是没有认证功能的,因此建议用户使用防火墙对MongoDB进行保护;
    14. Replica Set的工作是通过传送oplog来完成的,主节点发生故障后,新的数据将会存放在数据目录下的一个特定文件夹内,即rollback文件夹;你可以用来手动完成数据恢复;所以在每次故障发生之后,你一定要看看这个文件夹,MongoDB自带的工具就能够帮助你轻松地完成手动数据恢复;
    15. 跨服务器的数据拆分中,Sharding是一个有效的方法;MongoDB中支持自动化Sharding,但是对数据库性能会造成很大影响;因此建议用户尽早进行Sharding,使用MMS:Munin (+ Mongo plugin)和CloudWatch等工具对MongoDB进行监控,确保系统资源使用达到80%之前就完成Sharding工作;
    16. MongoDB使用shard key来决定特定的文档在哪个分片上,当插入一个文档之后,你是无法更新shard key的;这里建议用户删除文档并重新插入,这样就能够将其分配到合适的分片上;
    17. MongoDB对分片的限制还包括集合的大小,当超过256 GB的时候,MongoDB将不允许进行分片;相信10gen公司会在未来放弃这一限制,但在此之前用户需要留意;
    18. MongoDB中跨分片并没有强制要求唯一性,MongoDB只针对独立的分片进行强制而非全局性;当然除shard key之外;
    19. 进行拆分的时候,MongoDB会要求你选择一个键;用户需要注意选择正确的键,否则会造成不必要的麻烦;如何进行选择并无定式,主要取决于你的应用,比如针对news feed使用时间戳就是错的;在下一版本中,MongoDB将对此进行改进;
    20. MongoDB连接默认情况下是不加密的,也就是说你的数据是能够被第三方记录和使用的;所以你在公共网中访问MongoDB的话,就一定要进行加密;
    21. MongoDB只支持单一文档的原子性,这一点与传统的数据库有所不同,如MySQL.因此MongoDB中跨多个文档是不提供内置的transaction支持的;
    22. 当MongoDB显示ready的时候,其实内部还在进行journal的配置;因此针对速度较慢的文件系统,MongoDB的journal配置也会很慢;
    23. 不建议尝试NUMA + Linux + MongoDB的组合,如果你的MongoDB跑在NUMA服务器上,建议将它关掉;
    24. 在Linux上运行MongoDB遭遇segfault错误时,这主要是因为open files / process限制过低;建议用户将限制设定为4K+;
————————– 基本的增删改查操作 ————————–
— insert操作;
db.person.insert({“name”:”kobe”,”age”:32})
db.person.insert({“name”:”james”,”age”:28})
— find()操作;
db.person.find()
— update()操作;
db.person.update({“name”:”kobe”},{“name”:”kobe”,”age”:33})
db.person.find({“name”:”kobe”})
— delete()操作;
db.person.remove({“name”:”james”})
db.person.find()
db.person.count()
————————– 基本的增删改查操作 ————————–
————————– mongodb的配置文件/etc/mongod.conf ————————–
vi /etc/mongod.conf
port=27017
dbpath=/mongo/data
directoryperdb=true
logpath=/mongo/logs/mongod.log
logappend=true
auth=true
journal=true
fork=true
————————– mongodb的配置文件/etc/mongod.conf ————————–