老峰的博客
=技术 ?? new 技术()

导航

 

起源

公司的项目中大量的用到了Oracle Advance Queue来作为集成队列。直觉告诉我,Oracle作为关系型数据库的老大,但作为队列这种不需要ACID的场景不一定适合。再者最近NOSQL的解决方案非常火热,Redis是其中的新贵。所以拿他们两个来作个性能对比(Benchmark)。相信很多兄弟在是否选择NoSQL的方案时会考虑性能,希望这样的对比,能提供一点点基本的第一手资料。

 

这个性能对比准备以两个文章的形式来发布,第一部分只包括了简单的单线程的比较,第二篇文章将包括多线程部分。

源代码

 

https://files.cnblogs.com/ivenxu/RedisBenchmark_src.zip

各种模式及其优缺点

  • 存储的AQ (Persistence AQ),这个是Oracle所擅长的,好处是由于每一个入队(Enqueue)出队(Dequeue)都是一次行插入和删除,都是磁盘操作,就算数据库掉电,这些message还是存在的。缺点就是性能还会产生大量的数据库负载,在大型应用中数据库负载基本上是永恒的瓶颈,如果有可能的换我们应该减少数据库的负载。
  • 缓存的AQ(Buffered AQ),性能大大优于存储但是容错移转能力不行。另一个缺点是,默认最大队列容量5000。超过此值发生“ORA-25307: Enqueue rate too high”错误。
  • Redis 快照模式(Snapshot),性能强劲,可以通过Slave这样的机制来增强容错移转能力。
  • Redis日志模式,本文涉及该模式。

准备工作

  测试计划

  1. 单线程单队列入队出队独立,在同一时间只有一个客户端连接到一个队列,把所有消息全部入队,再开始出队。
  2. 多线程单队列并发出入队,多个线程(多客户端)同事出队入队同一个队列。
  3. 多线程多队列并发出入队,多个线程(多客户端)同事出对入队多个队列。

  环境

  Oralce数据库和Redis Server将部署在同一台Linux机器上,这个机器是Amazon EC2云计算的C1.Large,8 Cores,7GB内存。
  客户机(测试机器):m1.large,E5430 @ 2.66 GHz 2 Cores, 8.5 GB内存 Wiindows2008 64-bit
  客户机和服务在同一个局域网内,在同一个Domain Server下面。

  Oracle software is 11.2.0.1.0 - 64bit.

 

database

db_block_size                 

8192

db_recovery_file_dest_size

300G

fast_start_mttr_target

3600

memory_target

3584M

open_cursors

300

processes

1000

  Redis 的版本是2.0.4.

  Redis Client

  Oracle Advance Queue创建

  
  1. 创建Schema, User, 分配权限
DROP USER AQ_BENCHMARK CASCADE;

-- Create Schema

CREATE USER AQ_BENCHMARK
  IDENTIFIED BY AQ_BENCHMARK
  DEFAULT TABLESPACE DATAUSR
  TEMPORARY TABLESPACE TEMP
  PROFILE DEFAULT
  ACCOUNT UNLOCK;

  -- 4 Roles for AQ_BENCHMARK

  GRANT AQ_USER_ROLE TO AQ_BENCHMARK;

  GRANT RESOURCE TO AQ_BENCHMARK;

  GRANT AQ_ADMINISTRATOR_ROLE TO AQ_BENCHMARK;

  GRANT CONNECT TO AQ_BENCHMARK;

  ALTER USER AQ_BENCHMARK DEFAULT ROLE ALL;

  -- 2 System Privileges for AQ_BENCHMARK

  GRANT UNLIMITED TABLESPACE TO AQ_BENCHMARK;

  GRANT CREATE VIEW TO AQ_BENCHMARK;

  GRANT EXECUTE ON DBMS_AQ TO AQ_BENCHMARK;

  GRANT EXECUTE ON DBMS_AQADM TO AQ_BENCHMARK;


/
  2. 创建表,队列,并使enable
connect AQ_BENCHMARK/AQ_BENCHMARK

BEGIN
   DBMS_AQADM.create_queue_table (
      queue_table          => 'queue_table1',
      queue_payload_type   => 'raw',
      storage_clause       => 'tablespace DATAUSR',
      multiple_consumers   => FALSE,
      comment              => 'Queue Table for queue1',
      compatible           => '11.0',
      SECURE                =>         FALSE
   );
END;
/

BEGIN
   DBMS_AQADM.create_queue (queue_name    => 'queue1',
                            queue_table   => 'queue_table1');
END;
/

BEGIN
   DBMS_AQADM.start_queue (queue_name   => 'queue1',
                           enqueue      => TRUE,
                           dequeue      => TRUE);
END;
/
  3. 分别为Buffered Queue和Persistent Queue创建入队出队的存储过程

CREATE OR REPLACE PROCEDURE buffered_enqueue (queue_name    varchar2,
                                             message          varchar2)
IS
   enqueue_options      DBMS_AQ.enqueue_options_t;
   message_properties   DBMS_AQ.message_properties_t;
   message_handle       RAW (16);
BEGIN
   enqueue_options.visibility := DBMS_AQ.immediate;
   enqueue_options.delivery_mode := DBMS_AQ.buffered;
   DBMS_AQ.enqueue (queue_name           => queue_name,
                    enqueue_options      => enqueue_options,
                    message_properties   => message_properties,
                    payload              => utl_raw.cast_to_raw(message),
                    msgid                => message_handle);
   COMMIT;
END;
/

CREATE OR REPLACE PROCEDURE buffered_dequeue (queue_name varchar2, message OUT VARCHAR2)
IS
   dequeue_options      DBMS_AQ.dequeue_options_t;
   message_properties   DBMS_AQ.message_properties_t;
   message_handle       RAW (16);
   MSG              varchar2(9000);
   no_messages exception;
   PRAGMA EXCEPTION_INIT (no_messages, -25228);
BEGIN
   dequeue_options.wait := DBMS_AQ.FOREVER;
   dequeue_options.navigation := DBMS_AQ.next_message;
   dequeue_options.visibility := DBMS_AQ.immediate;
   dequeue_options.delivery_mode := DBMS_AQ.buffered;

         DBMS_AQ.dequeue (queue_name           => queue_name,
                          dequeue_options      => dequeue_options,
                          message_properties   => message_properties,
                          payload              => MSG,
                          msgid                => message_handle);
         DBMS_OUTPUT.put_line ('Message: ' || MSG);
         
         message := UTL_RAW.CAST_TO_VARCHAR2(MSG); 


EXCEPTION
   WHEN no_messages
   THEN
      DBMS_OUTPUT.put_line ('No more messages');

      COMMIT;
END;
/


CREATE OR REPLACE PROCEDURE persistent_enqueue (queue_name    varchar2,
                                             message          varchar2)
IS
   enqueue_options      DBMS_AQ.enqueue_options_t;
   message_properties   DBMS_AQ.message_properties_t;
   message_handle       RAW (16);
BEGIN
   enqueue_options.visibility := DBMS_AQ.immediate;
   enqueue_options.delivery_mode := DBMS_AQ.persistent;
   DBMS_AQ.enqueue (queue_name           => queue_name,
                    enqueue_options      => enqueue_options,
                    message_properties   => message_properties,
                    payload              => utl_raw.cast_to_raw(message),
                    msgid                => message_handle);
   COMMIT;
END;
/

CREATE OR REPLACE PROCEDURE persistent_dequeue (queue_name varchar2, message OUT VARCHAR2)
IS
   dequeue_options      DBMS_AQ.dequeue_options_t;
   message_properties   DBMS_AQ.message_properties_t;
   message_handle       RAW (16);
   MSG              varchar2(9000);
   no_messages exception;
   PRAGMA EXCEPTION_INIT (no_messages, -25228);
BEGIN
   dequeue_options.wait := DBMS_AQ.FOREVER;
   dequeue_options.navigation := DBMS_AQ.next_message;
   dequeue_options.visibility := DBMS_AQ.immediate;
   dequeue_options.delivery_mode := DBMS_AQ.persistent;

         DBMS_AQ.dequeue (queue_name           => queue_name,
                          dequeue_options      => dequeue_options,
                          message_properties   => message_properties,
                          payload              => MSG,
                          msgid                => message_handle);
         DBMS_OUTPUT.put_line ('Message: ' || MSG);
         
         message := UTL_RAW.CAST_TO_VARCHAR2(MSG); 


EXCEPTION
   WHEN no_messages
   THEN
      DBMS_OUTPUT.put_line ('No more messages');

      COMMIT;
END;
/
  
  4. 为Buffered AQ消除ORA-25307
SQL> alter system set “_buffered_publisher_flow_control_threshold”=500000 scope=both; 

System altered

SQL> show parameter buffered;

NAME TYPE VALUE
———————————— ———– ——————————
_buffered_publisher_flow_control_threshold integer 500000

SQL> 

                 http://www.orafaq.com/parms/parm168.htm

Test Case的设计

在单线程的情况下,单向操作(Enqueue/Dequeue不会同时发生),通过观察不同的队列的长度对性能的影响。选择的数列是以1000为基数,5000为增量。
 static void SingleThreadBenchMark()
        {
            for (int i = 1; i < 50; i+=5)
            {
                List<string> messages = BuildMessages(1000*i);
                DoBulkMessagesInSingleThread(AQAccessor.CreatePersistentQueue(1), messages, "AQ Persistent Single Thread");
                DoBulkMessagesInSingleThread(AQAccessor.CreateBufferedQueue(1), messages, "AQ Buffered Single Thread");


                DoBulkMessagesInSingleThread(RedisAccessor.CreateSharedServerQueue(1), messages, "Redis Single Thread");
            }
        }

测试结果及结论

  入队(Enqueue)

  

  出队 (Dequeue)

  
  1.  
    1.   无论是Enqueue还是Dequeue,性能最好的Redis,其次是Advance Queue Buffered模式,最差的是Persistent模式。
    2.   从Oracle Advance Queue两种内部两种模式之间的比较来看,Buffered模式是Persistent模式的4到5倍。
    3.   队列的数目基本上不会影响出入队的性能。
关于多线程之下的对比,敬请关注下一篇。
posted on 2011-01-22 20:47  iVen Xu  阅读(5232)  评论(0编辑  收藏  举报