zoukankan      html  css  js  c++  java
  • 关于Redis持久化

    Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件)

    • RDB持久化方式是在一个特定的间隔保存某个时间点的一个数据快照。

    • AOF(Append only file)持久化方式则会记录每一个服务器收到的写操作。数据回复时,这些记录的操作会逐条执行从而重建出原来的数据。写操作命令  记录的格式跟Redis协议一致,以追加的方式进行保存。

    Redis的持久化是可以禁用的,两种方式的持久化是可以同时存在的,但是当Redis重启时,AOF文件会被优先用于重建数据。

    一、RDB

    RDB就是Snapshot存储,是默认的持久化方式。按照一定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。Redis支持将当前数据的快照存成一个数据文件实现持久化。而一个持续写入的数据库如何生成快照呢。Redis借助了fork命令的copy on write机制。在生成快照时,将当前进程fork出一个子进程,然后在子进程中循环所有的数据,将数据写成为RDB文件。

    Client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求,所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。 

    Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的。当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件。这样在任何时候出现故障,Redis的RDB文件都总是可用的。并且Redis的RDB文件也是Redis主从同步内部实现中的一环

    主从同步

    第一次Slave向Master同步的实现是:

    Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。

    第二次以及以后的同步实现是:

    Master将变量的快照直接实时依次发送给各个Slave。但不管什么原因导致Slave和Master断开重连都会重复以上两个步骤的过程。

    Redis的主从复制是建立在内存快照的持久化基础上的,只要有Slave就一定会有内存快照发生。

    工作原理

    • Redis调用fork(),产生一个子进程。

    • 父进程继续处理client请求,子进程把内存数据写到一个临时的RDB文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。

    • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出

    优点

    • RDB文件是一个很简洁的单文件,它保存了某个时间点的Redis数据,很适合用于做备份。你可以设定一个时间点对RDB文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本。

    • RDB很适合用于灾备。单文件很方便就能传输到远程的服务器上。

    • RDB的性能很好,需要进行持久化时,主进程会fork一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的I/O操作。

    • 比起AOF,在数据量比较大的情况下,RDB的启动速度更快。

    缺点

    • RDB容易造成数据的丢失。假设每5分钟保存一次快照,如果Redis因为某些原因不能正常工作,那么从上次产生快照到Redis出现问题这段时间的数据就会丢失了。

    • RDB使用fork()产生子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成Redis停止服务几毫秒。如果数据量很大且CPU性能不是很好的时候,停止服务的时间甚至会到1秒。

    文件路径和名称

    默认Redis会把快照文件存储为当前目录下一个名为dump.rdb的文件。要修改文件的存储路径和名称,可以通过修改配置文件redis.conf实现:

    # RDB文件名,默认为dump.rdb。
    dbfilename dump.rdb
    
    # 文件存放的目录,AOF文件同样存放在此目录下。默认为当前工作目录。
    dir ./

    保存点(RDB的启用和禁用)

    你可以配置保存点,使Redis如果在每N秒后数据发生了M次改变就保存快照文件。例如下面这个保存点配置表示每60秒,如果数据发生了1000次以上的变动,Redis就会自动保存快照文件:

    save 60 1000

    保存点可以设置多个,Redis的配置文件就默认设置了3个保存点:

    # 格式为:save <seconds> <changes>
    # 可以设置多个。
    save 900 1 #900秒后至少1个key有变动
    save 300 10 #300秒后至少10个key有变动
    save 60 10000 #60秒后至少10000个key有变动

    如果想禁用快照保存的功能,可以通过注释掉所有"save"配置达到,或者在最后一条"save"配置后添加如下的配置:

    save ""

    错误处理

    默认情况下,如果Redis在后台生成快照的时候失败,那么就会停止接收数据,目的是让用户能知道数据没有持久化成功。但是如果你有其他的方式可以监控到Redis及其持久化的状态,那么可以把这个功能禁止掉。

    stop-writes-on-bgsave-error yes

    数据压缩

    默认Redis会采用LZF对数据进行压缩。如果你想节省点CPU的性能,你可以把压缩功能禁用掉,但是数据集就会比没压缩的时候要打。

    rdbcompression yes

    数据校验

    从版本5的RDB的开始,一个CRC64的校验码会放在文件的末尾。这样更能保证文件的完整性,但是在保存或者加载文件时会损失一定的性能(大概10%)。如果想追求更高的性能,可以把它禁用掉,这样文件在写入校验码时会用0替代,加载的时候看到0就会直接跳过校验

    rdbchecksum yes

    手动生成快照

    Redis提供了两个命令用于手动生成快照。

    SAVE

    SAVE命令会使用同步的方式生成RDB快照文件,这意味着在这个过程中会阻塞所有其他客户端的请求。因此不建议在生产环境使用这个命令,除非因为某种原因需要去阻止Redis使用子进程进行后台生成快照(例如调用fork(2)出错)。

    BGSAVE

    BGSAVE命令使用后台的方式保存RDB文件,调用此命令后,会立刻返回OK返回码。Redis会产生一个子进程进行处理并立刻恢复对客户端的服务。在客户端我们可以使用LASTSAVE命令查看操作是否成功。

    127.0.0.1:6379> BGSAVE
    Background saving started
    127.0.0.1:6379> LASTSAVE
    (integer) 1433936394

    配置文件里禁用了快照生成功能不影响SAVEBGSAVE命令的效果。

    二、AOF

    快照并不是很可靠。如果服务器突然Crash了,那么最新的数据就会丢失。而AOF文件则提供了一种更为可靠的持久化方式。每当Redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF里的命令会被重新执行一次,重建数据

    原理

    • redis调用fork ,现在有父子两个进程

    • 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令

    • 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题

    • 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件

    • 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加

    优点

    • 比RDB可靠。你可以制定不同的fsync策略:不进行fsync、每秒fsync一次和每次查询进行fsync。默认是每秒fsync一次。这意味着你最多丢失一秒钟的数据。

    • AOF日志文件是一个纯追加的文件。就算服务器突然Crash,也不会出现日志的定位或者损坏问题。甚至如果因为某些原因(例如磁盘满了)命令只写了一半到日志文件里,我们也可以用redis-check-aof这个工具很简单的进行修复。

    • 当AOF文件太大时,Redis会自动在后台进行重写。重写很安全,因为重写是在一个新的文件上进行,同时Redis会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操作命令的集合。当新文件重写完,Redis会把新旧文件进行切换,然后开始把数据写到新文件上。

    • AOF把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如我们不小心用FLUSHALL命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来。

    缺点

    • 在相同的数据集下,AOF文件的大小一般会比RDB文件大。

    • 在某些fsync策略下,AOF的速度会比RDB慢。通常fsync设置为每秒一次就能获得比较高的性能,而在禁止fsync的情况下速度可以达到RDB的水平。

    • 在过去曾经发现一些很罕见的BUG导致使用AOF重建的数据跟原数据不一致的问题。

    启用AOF

    把配置项appendonly设为yes

    appendonly yes

    文件路径和名称

    # 文件存放目录,与RDB共用。默认为当前工作目录。
    dir ./
    
    # 默认文件名为appendonly.aof
    appendfilename "appendonly.aof"

    可靠性

    你可以配置Redis调用fsync的频率,有三个选项:

    • 每当有新命令追加到AOF的时候调用fsync。速度最慢,但是最安全。

    • 每秒fsync一次。速度快(2.4版本跟快照方式速度差不多),安全性不错(最多丢失1秒的数据)。

    • 从不fsync,交由系统去处理。这个方式速度最快,但是安全性没有保证

    推荐使用每秒fsync一次的方式(默认的方式),因为它速度快,安全性也不错。相关配置如下:

    # appendfsync always
    appendfsync everysec
    # appendfsync no

    日志重写

    随着写操作的不断增加,AOF文件会越来越大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。

    工作原理如下:

    • Redis调用fork(),产生一个子进程。

    • 子进程把新的AOF写到一个临时文件里。

    • 主进程持续把新的变动写到内存里的buffer,同时也会把这些新的变动写到旧的AOF里,这样即使重写失败也能保证数据的安全。

    • 当子进程完成文件的重写后,主进程会获得一个信号,然后把内存里的buffer追加到子进程生成的那个新AOF里。

    我们可以通过配置设置日志重写的条件:

    #在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。
    #设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes
    no-appendfsync-on-rewrite yes 
    
    # Redis会记住自从上一次重写后AOF文件的大小(如果自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。
    # 如果当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
    # 同时需要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,但是已经达到百分比的情况。
    
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

    要禁用自动的日志重写功能,我们可以把百分比设置为0:

    auto-aof-rewrite-percentage 0

    Redis 2.4以上才可以自动进行日志重写,之前的版本需要手动运行BGREWRITEAOF这个命令。

    数据损坏修复

    如果因为某些原因(例如服务器崩溃)AOF文件损坏了,导致Redis加载不了,可以通过以下方式进行修复:

    • 备份AOF文件。

    • 使用redis-check-aof命令修复原始的AOF文件:

      $ redis-check-aof --fix
    • 可以使用diff -u命令看下两个文件的差异。

    • 使用修复过的文件重启Redis服务。

    从RDB切换到AOF

    这里只说Redis >= 2.2版本的方式:

    • 备份一个最新的dump.rdb的文件,并把备份文件放在一个安全的地方。

    • 运行以下两条命令:

      $ redis-cli config set appendonly yes
      $ redis-cli config set save ""
    • 确保数据跟切换前一致。

    • 确保数据正确的写到AOF文件里。

    第二条命令是用来禁用RDB的持久化方式,但是这不是必须的,因为你可以同时启用两种持久化方式。

    记得对配置文件redis.conf进行编辑启用AOF,因为命令行方式修改配置在重启Redis后就会失效。

    从上面看出,RDB和AOF操作都是顺序IO操作,性能都很高。而同时在通过RDB文件或者AOF日志进行数据库恢复的时候,也是顺序的读取数据加载到内存中。所以也不会造成磁盘的随机读。

    到底选择什么呢?下面是来自官方的建议:

    通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用RDB。很多用户仅使用了AOF,但是我们建议,既然RDB可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用RDB。

    在数据恢复方面:RDB的启动时间会更短,原因有两个

    • 一是RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。

    • 另一个原因是RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不需要再进行数据编码工作,所以在CPU消耗上要远小于AOF日志的加载。 

    注意:

    上面说了RDB快照的持久化,需要注意:在进行快照的时候(save),fork出来进行dump操作的子进程会占用与父进程一样的内存,真正的copy-on-write,对性能的影响和内存的耗用都是比较大的。比如机器8G内存,Redis已经使用了6G内存,这时save的话会再生成6G,变成12G,大于系统的8G。这时候会发生交换;要是虚拟内存不够则会崩溃,导致数据丢失。所以在用redis的时候一定对系统内存做好容量规划。

    目前,通常的设计思路是利用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化。即Master上Snapshot和AOF都不做,来保证Master的读写性能,而Slave上则同时开启Snapshot和AOF来进行持久化,保证数据的安全性。

    三、对Redis持久化的测试

    通过上面的理论对snapshot和aof有了一定的理解,下面开始进行一些测试

    1、redis.conf 开启snapshot 关闭aof

    save 900 1
    save 300 10
    save 60 10000
    
    rdbcompression no
    rdbchecksum no
    dbfilename redis.rdb
    dir /home/backup/redis
    
    appendonly  no

    测试

    [root@localhost redis]# ./src/redis-cli 
    127.0.0.1:6379> keys *
    1) "a"
    127.0.0.1:6379> set b 2
    OK
    127.0.0.1:6379> set c 3
    OK
    127.0.0.1:6379> set d 4
    OK
    127.0.0.1:6379> keys *
    1) "c"
    2) "a"
    3) "aa"
    4) "b"
    5) "d"
    127.0.0.1:6379> save
    OK
    #保存,进行持久化,每执行一次save,会在日至里面记录一条:" * DB saved on disk "
    
    127.0.0.1:6379> lpush aa 1
    (integer) 1
    127.0.0.1:6379> lpush aa 2
    (integer) 2

    持久化验证,重启redis

    127.0.0.1:6379> keys *
    1) "c"
    2) "a"
    3) "aa"
    4) "b"
    5) "d"
    

    lpush 操作在 save之后,但是重启之后仍然有这个数据

    什么原因呢,我们可以查看一下日志 

    6720:signal-handler (1453738444) Received SIGTERM scheduling shutdown...
    6720:M 26 Jan 00:14:04.896 # User requested shutdown...
    6720:M 26 Jan 00:14:04.896 * Saving the final RDB snapshot before exiting.
    6720:M 26 Jan 00:14:04.932 * DB saved on disk
    6720:M 26 Jan 00:14:04.932 * Removing the pid file.
    6720:M 26 Jan 00:14:04.932 # Redis is now ready to exit, bye bye...
    

    从日志里面可以看到,正常关闭redis,在关闭前执行save命令。 用kill的效果和上面一样,属于正常关闭

    那异常关闭呢?当以kill -9 的形式发送信号

    127.0.0.1:6379> set ss 1
    Could not connect to Redis at 127.0.0.1:6379: Connection refused
    not connected> get ss
    Could not connect to Redis at 127.0.0.1:6379: Connection refused
    not connected> get ss
    (nil)

    通过测试,开启RDB持久化,在满足save条件、手动save、正常关闭的时候数据都会被持久化;而异常关闭终止的时候数据会丢失

    2、redis.conf 关闭snapshot,关闭aof

    #save 900 1
    #save 300 10
    #save 60 10000
    rdbcompression no
    rdbchecksum no
    dbfilename redis.rdb
    dir ./
    
    appendonly  no

    操作

    redis 127.0.0.1:6379> keys * 
    (empty list or set)
    redis 127.0.0.1:6379> set name test
    OK
    redis 127.0.0.1:6379> save
    OK
    redis 127.0.0.1:6379> set aa 1
    OK
    redis 127.0.0.1:6379> 
    
    #重启redis
    
    redis 127.0.0.1:6379> keys *                          #发现刚才没有被保存的key丢失了
    1) "name"

    从上面的结果看出,关闭持久化,只有在手动save的时候数据都会被持久化,正常关闭的时候数据丢失。如果从一开始到关闭写入数据的期间没有手动save,则数据全部丢失,既然能手动save间接的说明了快照一直都存在,所以不能说是禁止snapshot,应该是禁止自动snapshot功能。

    3、redis.conf 关闭snapshot,开启aof

    appendonly  yes
    appendfilename redis.aof
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-min-size 64mb

     操作

    redis 127.0.0.1:6379> keys *
    1) "name"
    
    #修改开启AOF参数,重启数据库:
    
    redis 127.0.0.1:6379> keys *
    (empty list or set)
    redis 127.0.0.1:6379> 
    
    #数据库里面没有记录
    #查看日志:
    #* DB loaded from append only file: 0.000 seconds
    #发现是从0字节的aof文件里面同步数据,为什么不同步rdb的数据?原来redis代码里面写好了优先级,AOF>RDB
    

    查看源代码 redis.c   grep 'DB loaded from' ./ -R

    void loadDataFromDisk(void) {
        long long start = ustime();
        if (server.aof_state == REDIS_AOF_ON) {
            if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
                redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
        } else {
            if (rdbLoad(server.rdb_filename) == REDIS_OK) {
                redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
                    (float)(ustime()-start)/1000000);
            } else if (errno != ENOENT) {
                redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
                exit(1);
            }    
        }
    }

    这里需要注意的是:当中途开启AOF,重启让生效的时候,不能第2次正常重启了

    因为第一次重启让aof生效的时候,启动redis已经读取这个文件了,导致此时的redis数据为空的(优先级)。第二次重启,则会把这个空的数据save到RDB文件,这样导致RDB原有的数据被替换,导致数据丢失。所以一定要小心,为了避免悲剧的发生,当要重启redis的时候最好都备份下RDB文件

    redis 127.0.0.1:6379> keys *
    (empty list or set)
    redis 127.0.0.1:6379> set name tt
    OK
    redis 127.0.0.1:6379> save
    OK
    
    #开启aof参数
    #第一次重启
    redis 127.0.0.1:6379> keys *   #如上面所说的优先级原因:aof > rdb,结果为空
    (empty list or set)
    
    
    #第2次正常重启,把上面空的结果save到了RDB,数据丢失。此时的db是空的,日志记录 "* DB saved on disk"
    redis 127.0.0.1:6379> keys *
    (empty list or set)
    
    #数据已经被初始化了,数据丢失

    这里就有一个问题,比如在用redis的时候,刚开始只开启RDB的持久方式,AOF没有开启,在跑一段时间之后想开启AOF,那如何把RDB的数据直接写到AOF文件呢?有2种方法

    a、在开启AOF之前,先执行bgrewriteaof,再重启

    redis 127.0.0.1:6379> keys *                   #查看是否有数据
    (empty list or set)
    redis 127.0.0.1:6379> set name ttd
    OK
    redis 127.0.0.1:6379> keys *
    1) "name"
    
    redis 127.0.0.1:6379> bgsave                   #保存数据
    Background saving started
    redis 127.0.0.1:6379> keys *
    1) "name"
    
    #只有一个RDB文件,没有AOF文件
    
    redis 127.0.0.1:6379> bgrewriteaof             #执行合并重写功能,生成AOF文件
    Background append only file rewriting started
    
    #这时候去打开redis.conf 文件中的aof参数(appendonly yes),重启生效。
    #日志里面出现:* DB loaded from append only file: 0.000 seconds
    
    redis 127.0.0.1:6379> keys *                    #数据还在
    1) "name"
    
    
    #查看文件
    [root@localhost data]# od -c redis.aof 
    0000000   *   2  
      
       $   6  
      
       S   E   L   E   C   T  
      
    
    0000020   $   1  
      
       0  
      
       *   3  
      
       $   3  
      
       S
    0000040   E   T  
      
       $   4  
      
       n   a   m   e  
      
       $   4
    0000060  
      
       j   a   c   k  
      
    
    0000070 

    b、利用CONFIG GET/SET 的方法动态修改配置文件

    redis 127.0.0.1:6379> BGSAVE
    Background saving started
    #此时,只有rdb文件
    
    #动态修改参数,把aof功能开启:appendonly yes
    redis 127.0.0.1:6379> CONFIG SET appendonly yes        #动态修改参数
    OK
    redis 127.0.0.1:6379> CONFIG GET append*
    1) "appendonly"
    2) "yes"
    3) "appendfsync"
    4) "everysec"
    redis 127.0.0.1:6379>
    
    #aof文件已经生成,并且有数据(同步rdb)
    
    #日志里面的信息:* Background append only file rewriting started by pid 3165
    #因为参数是动态修改的,在重启之后会失效,所以在维护的时候修改redis.conf文件的参数即可

    从上面的结果看出,redis重启载入数据的时候,读取aof的文件要先于rdb文件,所以尽量一开始开启aof选项,不要在中途开启。

    通过日志可以很清楚的知道redis通过那个文件来取数据的:

    RDB:   * DB loaded from disk: 0.000 seconds
    AOF: * DB loaded from append only file: 0.000 seconds

    保存数据则是

    RDB:* DB saved on disk
    AOF:  * Calling fsync() on the AOF file

    4、redis.conf 开启snapshot,开启aof

    save 900 1
    save 300 10
    save 60 10000
    
    appendonly  yes
    appendfilename zhoujy.aof
    # appendfsync always
    appendfsync everysec
    # appendfsync no
    
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-min-size 64mb

    通过上面的这些测试,已经说明RDB和AOF他们的操作方式,以及如重启时的载入,重启时将按照以下优先级恢复数据到内存:

    • 如果只配置AOF,重启时加载AOF文件恢复数据

    • 如果同时 配置了RBD和AOF,启动是只加载AOF文件恢复数据

    • 如果只配置RDB,启动时候加载dump文件恢复数据

    四、Redis数据备份

    备份很简单,只需要把RDB,AOF的文件复制备份起来就可以了

    #redisA: A上生成测试数据
    redis 127.0.0.1:6379> set name test
    7.0.0.1:6379> set age 17
    OK
    redis 127.0.0.1:6379> keys *
    1) "age"
    
    redis 127.0.0.1:6379> bgsave
    Background saving started
    
    #redisB: B上没有数据
    redis 127.0.0.1:6380> keys *
    (empty list or set)
    
    #复制A的文件到B(rdb和aof文件)
    cp redis/* redis2/  
    #修改权限
    chown -R redis.redis *
    #重启B 还原
    redis 127.0.0.1:6380> keys *
    1) "sex"

    参考

    http://redis.io/topics/persistence
    http://www.cnblogs.com/zhoujinyi/archive/2013/05/26/3098508.html
    http://heylinux.com/archives/1932.html
    http://database.51cto.com/art/201203/322144.htm

  • 相关阅读:
    c语言结构体数组引用
    c语言结构体数组定义的三种方式
    如何为SAP WebIDE开发扩展(Extension),并部署到SAP云平台上
    SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
    使用SAP API portal进行SAP SuccessFactors的API测试
    SAP UI5应用里的页面路由处理
    在SAP WebIDE Database Explorer里操作hdi实例
    如何使用SAP事务码SAT进行UI应用的性能分析
    使用SAP WebIDE进行SAP Cloud Platform Business Application开发
    SAP CRM WebClient UI ON_NEW_FOCUS的用途
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/5158791.html
Copyright © 2011-2022 走看看