起源
公司的项目中大量的用到了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日志模式,本文涉及该模式。
准备工作
测试计划
- 单线程单队列入队出队独立,在同一时间只有一个客户端连接到一个队列,把所有消息全部入队,再开始出队。
- 多线程单队列并发出入队,多个线程(多客户端)同事出队入队同一个队列。
- 多线程多队列并发出入队,多个线程(多客户端)同事出对入队多个队列。
环境
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>
ORA-25307 Work around: http://sungur.wordpress.com/2009/08/29/solution-of-ora-25307-enqueue-rate-too-high/
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)
-
- 无论是Enqueue还是Dequeue,性能最好的Redis,其次是Advance Queue Buffered模式,最差的是Persistent模式。
- 从Oracle Advance Queue两种内部两种模式之间的比较来看,Buffered模式是Persistent模式的4到5倍。
- 队列的数目基本上不会影响出入队的性能。
关于多线程之下的对比,敬请关注下一篇。