之前因為redis中存在2E多條數據未設置失效時間,造成服務器內存一直在減小,查了下redis API也沒有批量設置key失效時間的方法,所以就用java寫了個小程序,性能還不錯,因為剛接觸這個水平有限,有能改進的地方希望大家指出,共同進步。
先簡單的說下代碼,我采用的方式是先導出redis中的所有key,然后把key切割成小文件(因為我導出后文件有幾個G,所有切割成小文件,一個文件500w數據,主要看需求),然后把這些小文件存放到同一目錄下,然后程序去讀取目錄下的小文件,根據小文件中的key設置失效時間並記錄日志,因為我只是需要設置指定前綴key的失效時間,所以我加了個前綴的過濾,日志主要記錄小文件名,這個小文件設置了多少個key。程序設置完一個小文件中的key后會刪除這個小文件。
下面附上小程序代碼
package redis.task; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; import java.util.Scanner; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; /** * 讀取指定目錄下的所有文件中的key設置失效時間 * @author Administrator * */ public class Task2 { // redis ip地址 private static String redis_ip = ""; // redis 端口號 private static int redis_prot = 0; // redis keys文件夾路徑 private static String kyes_path = ""; // 日志輸出路徑 private static String log_path = ""; // redis key前綴 private static String key_prefix = ""; // 失效時間(秒) private static int invalid_time = 0; private static JedisPool pool = null; /** * 獲取配置文件中的相關配置 */ static { // 獲取配置文件路徑 String path = System.getProperty("user.dir"); String file = path + "/config.properties"; file = file.replaceAll("\\\\", "/"); Properties pro = new Properties(); try { pro.load(new FileInputStream(new File(file))); redis_ip = pro.getProperty("redis_ip"); redis_prot = Integer.parseInt(pro.getProperty("redis_prot")); kyes_path = pro.getProperty("keys_path").replaceAll("\\\\", "/"); log_path = pro.getProperty("log_path").replaceAll("\\\\", "/"); key_prefix = pro.getProperty("key_prefix"); invalid_time = Integer.parseInt(pro.getProperty("invalid_time")); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 構建redis連接池 * @return */ public static JedisPool getPool() { if (pool == null) { JedisPoolConfig config = new JedisPoolConfig(); //控制一個pool可分配多少個jedis實例,通過pool.getResource()來獲取; //如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態為exhausted(耗盡)。 config.setMaxActive(500); //控制一個pool最多有多少個狀態為idle(空閑的)的jedis實例。 config.setMaxIdle(5); //表示當borrow(引入)一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋出JedisConnectionException; config.setMaxWait(1000 * 15); //在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的; config.setTestOnBorrow(true); pool = new JedisPool(config, redis_ip, redis_prot); } return pool; } /** * 返還到連接池 * @param pool * @param redis */ public static void returnResource(JedisPool pool, Jedis redis) { if (redis != null) { pool.returnResource(redis); } } /** * 設置key有效時間 * @param key * @param seconds */ public static void expire(String key, int seconds){ JedisPool pool = null; Jedis jedis = null; try { pool = getPool(); jedis = pool.getResource(); jedis.expire(key, seconds); } catch (Exception e) { // 釋放redis對象 pool.returnBrokenResource(jedis); e.printStackTrace(); } finally { // 返還到連接池 returnResource(pool, jedis); } } /** * 獲取指定目錄下的所有文件 * @param resource * @return */ public static File[] getFiles(String resource) { File file = new File(resource); return file.listFiles(); } /** * 向指定文件中追加內容,若文件不存在則先創建文件 * @param fileName * @param content */ public static void appendFile(String fileName, String content) { FileWriter writer = null; try { // 若文件不存在,則先創建文件 File file = new File(fileName); if (!file.exists()) file.createNewFile(); // 采用在文件尾部追加的方式寫文件 writer = new FileWriter(fileName, true); writer.write(content); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.flush(); writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * 讀取文件並設置失效時間,設置redis key 失效時間 * @param filePath * @return key數量 */ public static int readFile(String filePath) { int sum = 0; BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)))); String key = ""; while ((key = br.readLine()) != null) { if (key.startsWith(key_prefix)) { System.out.println("io: " + key); expire(key, invalid_time); sum ++; } } } catch (Exception e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return sum; } /** * 掃描方式讀取文件,設置redis key 失效時間 * @param filePath * @return key數量 */ public static int scannerReadFile(String filePath) { int sum = 0; FileInputStream inputStream = null; Scanner sc = null; try { inputStream = new FileInputStream(filePath); sc = new Scanner(inputStream, "UTF-8"); while (sc.hasNext()) { String key = sc.nextLine(); if (key.startsWith(key_prefix)) { System.out.println("scanner: " + key); expire(key, invalid_time); sum ++; } } if (sc.ioException() != null) { throw sc.ioException(); } } catch (IOException e){ e.printStackTrace(); } finally { try { if (inputStream != null) inputStream.close(); if (sc != null) sc.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return sum; } /** * 迭代方式讀取文件,設置redis key 失效時間 * @param filePath * @return key數量 * @throws Exception */ public static int lineIteratorReadFile (String filePath) { int sum = 0; LineIterator it = null; try { it = FileUtils.lineIterator(new File(filePath), "UTF-8"); while (it.hasNext()) { String key = it.nextLine(); if (key.startsWith(key_prefix)) { System.out.println("iterator: " + key); expire(key, invalid_time); sum ++; } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return sum; } finally { LineIterator.closeQuietly(it); } return sum; } public static void main(String[] args) { int sum = 0; // 根據存放keys的文件夾路徑獲取文件夾下的所有key文件 File[] files = getFiles(kyes_path); if (files != null) { for (File file : files) { String fileName = file.getName(); String folderPath = kyes_path + fileName; try { if (file.isFile()) { appendFile(log_path, getTime(System.currentTimeMillis()) + " " + fileName + ":開始設置失效時間...\r\n"); // 1. 消耗內存最大 // sum = readFile(folderPath); // 2. 消耗內存較小 // sum = scannerReadFile(folderPath); // 3. 消耗內存最小 sum = lineIteratorReadFile(folderPath); // 處理完文件后刪除處理成功文件 file.delete(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { appendFile(log_path, getTime(System.currentTimeMillis()) + " " + fileName + ":設置失效時間完成,共設置 " + sum + " 個key\r\n\r\n"); } } } else { System.out.println(kyes_path + "目錄不存在或為空!"); } } /** * 根據時間戳獲取時間 * @param time * @return */ public static String getTime(long time) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(new Date(time)); } /** * 獲取項目路徑 * @return */ public static String getPath() { return System.getProperty("user.dir"); } }
參數說明:
redis_ip:redis服務器ip地址
redis_prot:redis服務器端口號
keys_path:導出的redis key
log_path:程序日志輸出的路徑
key_prefix:需要設置key的前綴
invalid_time:失效時間(秒)
用到的jar
commons-io-2.2.jar
commons-pool-1.6.jar
jedis-2.2.0.jar