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();

        }

    }

}

詳細下載地址:

http://download.csdn.net/detail/jia281460530/9710776

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章