1. 程式人生 > >redis(單機讀取資料效能測試)

redis(單機讀取資料效能測試)

redis 測試之–讀取效能

目的

  • 熟悉jedis操作redis
  • 對redis效能指標有個大概認知

測試環境

  • ubuntu
  • 機器雙核4G記憶體普通機
  • 外網流量4M
  • redis版本: 3.2.6
  • redis 和測試服務程式在一臺伺服器上

redis 配置

新增密碼和註釋了bind:127.0.0.1,其他均為預設配置

測試思路

  1. redis 儲存一個測試key( 測試key是672位元組)
  2. 開啟jedis執行緒池,為個工作執行緒提供一個執行緒
  3. 工作執行緒不斷讀取key,獲取到key後直接計數器記錄
  4. 開啟一個定時列印定時器,將結果輸出

測試結果

日誌記錄

=======================================================
---------開始時間--------------結束時間-------------獲取條數-----每秒吞吐量-----分鐘吞吐量-----小時吞吐量-----測試執行執行緒數量----每個訊息的大小
-2016-12-10 15:28:42---2016-12-10 17:01:44------240670794------43115---------2587857---------240670792-----------2-----------672byte-----
-------
--開始時間--------------結束時間-------------獲取條數-----每秒吞吐量-----分鐘吞吐量-----小時吞吐量-----測試執行執行緒數量----每個訊息的大小 -2016-12-10 10:40:25---2016-12-10 12:55:12------457891036------56620---------3417097---------228945517-----------4-----------672byte----- ---------開始時間--------------結束時間-------------獲取條數-----每秒吞吐量-----分鐘吞吐量-----
小時吞吐量-----測試執行執行緒數量----每個訊息的大小 -2016-12-10 14:10:26---2016-12-10 15:11:28------231494306------63215---------3794988---------231494306-----------6-----------672byte----- [framework] 2016-12-10 15:11:28,466 - com.jiazq.jizq.redis.mq.RedisReadTaskSchuder -3662056 [pool-1-thread-1] ERROR com.jiazq.jizq.redis.mq.RedisReadTaskSch --------開始時間--------------結束時間-------------獲取條數-----每秒吞吐量-----分鐘吞吐量-----小時吞吐量-----測試執行執行緒數量----每個訊息的大小 -2016-12-10 13:00:49---2016-12-10 14:09:06------275406638------67221---------4050097---------275406638-----------8-----------672byte-----

cpu記錄

// 2 個執行緒
top - 17:02:56 up 16 days, 18:35,  2 users,  load average: 1.15, 1.00, 1.02
Tasks: 116 total,   4 running, 112 sleeping,   0 stopped,   0 zombie
%Cpu(s): 18.2 us, 24.7 sy,  0.0 ni, 39.3 id,  0.0 wa,  0.0 hi, 14.6 si,  3.3 st
KiB Mem:   4046856 total,  3113916 used,   932940 free,   233184 buffers
KiB Swap:        0 total,        0 used,        0 free.  1501364 cached Mem

// 4 執行緒
top - 12:57:07 up 16 days, 14:29,  2 users,  load average: 1.64, 1.85, 1.92
Tasks: 117 total,   3 running, 114 sleeping,   0 stopped,   0 zombie
%Cpu(s): 19.9 us, 30.6 sy,  0.0 ni, 21.7 id,  0.0 wa,  0.0 hi, 26.4 si,  1.4 st
KiB Mem:   4046856 total,  3125252 used,   921604 free,   233180 buffers
KiB Swap:        0 total,        0 used,        0 free.  1499612 cached Mem

// 6 執行緒
top - 14:30:01 up 16 days, 16:02,  2 users,  load average: 2.21, 2.73, 2.95
Tasks: 117 total,   3 running, 114 sleeping,   0 stopped,   0 zombie
%Cpu(s): 19.8 us, 39.0 sy,  0.0 ni, 14.4 id,  0.0 wa,  0.0 hi, 26.0 si,  0.8 st
KiB Mem:   4046856 total,  3115260 used,   931596 free,   233184 buffers
KiB Swap:        0 total,        0 used,        0 free.  1500320 cached Mem

// 8 執行緒
top - 15:21:23 up 16 days, 16:53,  2 users,  load average: 4.17, 3.42, 3.14
Tasks: 117 total,   3 running, 114 sleeping,   0 stopped,   0 zombie
%Cpu(s): 21.0 us, 39.1 sy,  0.0 ni, 13.2 id,  0.0 wa,  0.0 hi, 26.1 si,  0.6 st
KiB Mem:   4046856 total,  3099824 used,   947032 free,   233184 buffers
KiB Swap:        0 total,        0 used,        0 free.  1500688 cached Mem

結果分析

  • 讀取執行緒數量2 的吞吐量為 4.3/S 平均每個執行緒讀取能力2.1W/S 負載1.0
  • 讀取執行緒數量4(cpu核心2倍)的吞吐量為 5.6W/S 平均每個執行緒讀取能力 1.4W/S 負載 1.85
  • 讀取執行緒數量6 (cpu核心3倍) 的吞吐量為 6.3W/S 平均每個執行緒讀取能力 1W/S 負載 2.73
  • 讀取執行緒數量8 (cpu核心4倍) 的吞吐量為 6.7W/S 平均每個執行緒讀取能力 8300/S 負載3.42

隨著處理執行緒增加整體吞吐量也在增加,但增帳率下降,單執行緒處理能力下降,CPU使用率浮動較小,負載情況成倍增加。

結論

  • 單機redis最高讀取效能,還可以更高,吞吐量還應在 7W/S以上
  • 客戶端執行緒處理能力,應該在IO執行緒數量上,合理的客戶端連線數量可提高整體處理能力.
  • 客戶端讀取執行緒數量 為核心個數為佳.

核心處理程式碼

工作執行緒組(RedisReadTaskSchuder)

package com.jiazq.jizq.redis.mq;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;

public class RedisReadTaskSchuder {

    private static Logger logger = Logger.getLogger(RedisReadTaskSchuder.class);

    // 訊息計數器
    AtomicLong counter = new AtomicLong(0);

    // 定時器
    ScheduledExecutorService executorService =  Executors.newScheduledThreadPool(1);

    volatile boolean runState = true;
    // 開啟時間
    long startTime = 0;

    // 工作執行緒
    Thread[] workers = null;

    // 執行緒數量
    int threadNumber = 0;

    public RedisReadTaskSchuder(int threadNumber) {
        // 預設執行緒數量為硬體核心數的2倍
        this.threadNumber = threadNumber;

        workers = new Thread[threadNumber];

        for (int i = 0; i < threadNumber; i++) {
            workers[i] = new Thread(new RedisReadTask(JedisManager.instance().getJedis()));
            workers[i].setDaemon(true);
            workers[i].setName(ConfigManager.read_thread_name + "i");
        }

        executorService.scheduleAtFixedRate(new PrintTimer(), 2, 15, TimeUnit.SECONDS);

    }

    /**
     *  啟動工作執行緒
     */
    public void start() {

        for (int i = 0; i < threadNumber; i++) {
            workers[i].start();
        }

        startTime = System.currentTimeMillis();
    }

    /**
     * 計數器重置
     * 
     */
    public void resetCount() {
        this.counter.set(0);
        startTime = System.currentTimeMillis();
    }

    public long getCount() {
        return this.counter.get();
    }

    /**
     * 關閉執行緒
     */
    public void shutdown() {
        runState = false;
        executorService.shutdown();
    }

    public void printReuslt() {
        logger.info("---獲取到資料數量:--" + counter.get());
    }

    class RedisReadTask implements Runnable {

        private Jedis jedis = null;

        RedisReadTask(Jedis jedis) {

            this.jedis = jedis;
        }

        @Override
        public void run() {

            while (runState) {

                try {

                    byte[] str = jedis.get(Main.testKey.getBytes());
                    if (null != str) {
                        counter.incrementAndGet();
                    }

                } catch (Throwable t) {

                    logger.error("",t);

                    // 連線失敗
                    if (!jedis.isConnected()) {

                        //返回連線池裡面
                        jedis.close();
                        // 重新獲取連線
                        jedis = JedisManager.instance().getJedis();
                    }
                }
            }

            // 返回去jedis pool 裡面
            jedis.close();
        }
    }


    class PrintTimer implements Runnable {

        @Override
        public void run() {

            try {
                StringBuilder sb = new StringBuilder();

                SimpleDateFormat format = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
                long _count = counter.get();
                long _endTime = System.currentTimeMillis();

                long throughput_s  = ( _count  * 1000) / (_endTime - startTime);
                long minTime = (_endTime - startTime) /(1000 * 60);
                long hourTime = (_endTime - startTime) /(1000 * 60 * 60);

                long throughput_m = (minTime != 0) ? _count /minTime : 0;
                long throughput_h = (hourTime != 0) ? _count /hourTime : 0;

                sb.append("\n======================================================\n");
                sb.append("---------開始時間--------------結束時間-------------獲取條數-----每秒吞吐量-----分鐘吞吐量-----小時吞吐量-----測試執行執行緒數量----每個訊息的大小\n");

                sb.append("-");
                sb.append(format.format(new Date(startTime)));
                sb.append("---");
                sb.append(format.format(new Date()));
                sb.append("------");
                sb.append(counter.get());
                sb.append("------");
                sb.append(throughput_s);
                sb.append("---------");
                sb.append(throughput_m);
                sb.append("---------");
                sb.append(throughput_h);
                sb.append("-----------");
                sb.append(threadNumber);
                sb.append("-----------");
                sb.append("672byte");
                sb.append("-----");

                logger.error(sb.toString());
                logger.error("\n");

            } catch (Throwable t) {

                logger.error("",t);
            }

        }

    }
}

jedis執行緒池

package com.jiazq.jizq.redis.mq;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class JedisManager {

    private static Logger logger = Logger.getLogger(JedisManager.class);

    private static JedisManager instance = null;

    private JedisPool jedisPool = null;

    private JedisManager () {

    }


    public static synchronized JedisManager instance() {

        if (null == instance) {

            instance = new JedisManager();
            instance.init();
        }
        return instance;
    }

    public void init() {

        logger.info("------init redis start------");


        GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();

        jedisPoolConfig.setMaxTotal(ConfigManager.redis_pool_maxTotal);
        jedisPoolConfig.setMaxIdle(ConfigManager.redis_pool_maxIdle);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setMaxWaitMillis(ConfigManager.redis_pool_maxWait);

        jedisPool = new JedisPool(jedisPoolConfig,
                ConfigManager.redis_host,
                ConfigManager.redis_port,
                ConfigManager.redis_pool_timeOut,
                ConfigManager.redis_auth,
                ConfigManager.redis_pool_database);

        logger.info("------init redis end------");


    }

    public Jedis getJedis() {

        return jedisPool.getResource();
    }

    public void rebackPool(Jedis jedis) {

        jedis.close();
    }

    public String getString(String key) {
        Jedis jedis = getJedis();
        String result = jedis.get(key);

        jedis.close();

        return result;
    }

    public void set(String key, String value) {

        Jedis jedis = getJedis();

        jedis.set(key, value);

        jedis.close();

    }

    public void shutdown() {
        logger.info("---redis close--" + jedisPool.getNumActive());
        jedisPool.close();
    }
}

主函式Main

package com.jiazq.jizq.redis.mq;

import org.apache.log4j.Logger;

public class Main {

    private static Logger logger = Logger.getLogger(Main.class);

    static String testStr = "{.....}";
    static String testKey = "testkey676K";

    // 執行時間
    static int exetime = 1; 

    public static void main(String[] args) throws InterruptedException {

        logger.info("----start-testServer----start--");

        // 初始化redis連線池
        JedisManager.instance();
        // 建立執行緒排程
        RedisReadTaskSchuder schuder = new RedisReadTaskSchuder(ConfigManager.read_thread_number);
        // 執行緒啟動
        schuder.start();

        logger.info("----start-testServer----end--");

    }

    public void testNative() throws InterruptedException {
        // 初始化redis連線池
        JedisManager.instance();
        // 建立執行緒排程
        RedisReadTaskSchuder schuder = new RedisReadTaskSchuder(ConfigManager.read_thread_number);
        // 執行緒啟動
        schuder.start();
        //預熱一秒
        Thread.sleep(1000);
        // 計數器重置
        schuder.resetCount();
        // 執行五秒
        Thread.sleep(60000 * exetime);
        schuder.shutdown();
        JedisManager.instance().shutdown();

        schuder.printReuslt();

        logger.info(schuder.getCount() / (60 * exetime));


    }

} 

測試程式碼下載地址