1. 程式人生 > >redis 寫操作效能測試 (單機)

redis 寫操作效能測試 (單機)

redis 寫操作效能測試 (單機版)

測試目的

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

測試環境

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

redis 配置

  • 新增密碼
  • 註釋了bind:127.0.0.1,
  • maxmemory 3gb

遇到問題

問題一

再插入300萬資料的時候,報錯

// redis 日誌檔案
2670:M 13 Dec 17:30:53.061 * 10 changes in 300 seconds. Saving...
2670
:M 13 Dec 17:30:53.061 # Can't save in background: fork: Cannot allocate memory // redis-cli 127.0.0.1:6379> 127.0.0.1:6379> flushdb (error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for
details about the error. 127.0.0.1:6379

解決方式

// 引用 http://www.cnblogs.com/qq78292959/p/3994341.html

在小記憶體的程序上做一個fork,不需要太多資源,但當這個程序的記憶體空間以G為單位時,fork就成為一件很恐怖的操作。何況在16G記憶體的主機上fork 14G記憶體的程序呢?肯定會報記憶體無法分配的。更可氣的是,越是改動頻繁的主機上fork也越頻繁,fork操作本身的代價恐怕也不會比假死好多少。

找到原因之後,直接修改核心引數vm.overcommit_memory = 1

Linux核心會根據引數vm.overcommit
_memory引數的設定決定是否放行。 1. 如果 vm.overcommit_memory = 1,直接放行 2.vm.overcommit_memory = 0:則比較 此次請求分配的虛擬記憶體大小和系統當前空閒的實體記憶體加上swap,決定是否放行。 3.vm.overcommit_memory = 2:則會比較 程序所有已分配的虛擬記憶體加上此次請求分配的虛擬記憶體和系統當前的空閒實體記憶體加上swap,決定是否放行。 Arch linux設定vm.overcommit_memory 方法 永久性修改核心引數 在/etc/sysctl.conf檔案裡面加入或者直接刪除也可以,因為它預設值就是0 vm.overcommit_memory = 0 執行使之生效 #sysctl -p

測試思路

分別對redis 進行100萬 200 萬 300萬資料量的插入,分別統計花費的時間

測試結果

日誌資訊

====================================================
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 16:56:22---2016-12-13 16:56:44------1000000------21791------45890-----------4-----------11472--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 17:15:23---2016-12-13 17:16:07------2000000------43802------45660-----------4-----------11415--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:23:47---2016-12-13 18:24:57------3000000------70256------42700-----------4-----------10675--------748byte-----


--------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:33:53---2016-12-13 18:34:21------1000000------28133------35545-----------2-----------17772--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:31:34---2016-12-13 18:32:32------2000000------57970------34500-----------2-----------17250--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:27:37---2016-12-13 18:29:02------3000000------85097------35253-----------2-----------17626--------748byte-----



---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:36:01---2016-12-13 18:36:21------1000000------20085------49788-----------6-----------8298--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:37:43---2016-12-13 18:38:25------2000000------41571------48110-----------6-----------8018--------748byte-----
---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小
-2016-12-13 18:39:25---2016-12-13 18:40:30------3000000------65840------45565-----------6-----------7594--------748byte-----


結果分析

  • 100萬條資料(700M)插入redis只需最少時間20s, 200萬條資料最少41秒 300萬條資料最少65秒
  • 200萬資料一下,資料插入效率相當,300萬條效率執行效率下降
  • 最高redis插入吞吐量是 4.9W/S
  • 單執行緒客戶端最高吞吐 1.7W/S

結論

300萬條執行的效率下降,跟RDB預設策略有關係.

save 900 1
save 300 10
save 60 10000
  • 本次測試遠沒有達到redis效能峰值,但保守插入操作在5W/S的併發吞吐。單客戶端執行緒最低1.7W/S.
  • 結合之前redis讀取效能測試對比,redis讀取效率是寫入的1.4倍。

測試程式碼

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.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.log4j.Logger;


import redis.clients.jedis.Jedis;

public class RedisWriteTaskSchudler {

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

    volatile boolean runState = true;

    // 開啟時間
    long startTime = 0;

    long endTime = 0;

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

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

    AtomicLong writeCount = new AtomicLong(ConfigManager.redis_write_count);

    // 測試完成判斷
    public Semaphore semaphore = new Semaphore(1);

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



    RedisWriteTaskSchudler (int threadNumber) {

        // 預設執行緒數量為硬體核心數的2倍
        this.threadNumber = threadNumber;

        workers = new Thread[threadNumber];

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

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

    }

    class PrintTimer implements Runnable {

        @Override
        public void run() {

            // 結束
            if (runState == false) {

                JedisManager.instance().shutdown();

                executorService.shutdown();
            }
        }

    }

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

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

        startTime = System.currentTimeMillis();

    }


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

    public synchronized void log() {

        if (endTime == 0) {

            endTime = System.currentTimeMillis();

        } else {
            return;
        }

        StringBuilder sb = new StringBuilder();

        SimpleDateFormat format = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        long _count = ConfigManager.redis_write_count;

        long useTime = endTime - startTime;
        long throughput = ((_count * 1000) /useTime);
        int strLength = ConfigManager.test_string_value.getBytes().length;

        sb.append("\n======================================================\n");
        sb.append("---------開始時間--------------結束時間-------------插入條數-----使用時間(毫秒)-----吞吐量--------測試執行執行緒數量--------單客戶端併發量-------每個訊息的大小\n");
        sb.append("-");
        sb.append(format.format(new Date(startTime)));
        sb.append("---");
        sb.append(format.format(new Date(endTime)));
        sb.append("------");
        sb.append(_count);
        sb.append("------");
        sb.append(useTime);
        sb.append("------");
        sb.append(throughput);
        sb.append("-----------");
        sb.append(threadNumber);
        sb.append("-----------");
        sb.append(throughput / threadNumber );
        sb.append("--------");
        sb.append(strLength);
        sb.append("byte-----");

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

        semaphore.release();
    }


    class RedisWriteTask implements Runnable {

        private Jedis jedis = null;

        RedisWriteTask (Jedis jedis) {

            this.jedis = jedis;
        }

        @Override
        public void run() {

            semaphore.tryAcquire();

            while (runState) {

                try {

                    long number = writeCount.decrementAndGet();

                    if (number < 0) {
                        runState = false;

                        log();

                        break;
                    }

                    String writeKey = ConfigManager.test_string_key + number;

                    jedis.set(writeKey, ConfigManager.test_string_value);

                } catch (Throwable t) {
                    logger.error("",t);

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

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

            }

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

        }

    }

}

詳細下載地址: