Introduction
Welcome to this comprehensive tutorial on using Redis SQL Functions for IBM i. This powerful integration allows IBM i developers to leverage Redis’s high-performance caching capabilities directly from SQL queries. Whether you’re looking to implement session storage, application caching, or real-time data processing, these functions provide a seamless bridge between your IBM i database and Redis.
Prerequisites
Before we begin, ensure you have:
- Access to an IBM i system with development tools (ILE C compiler, SQL)
- A running Redis server accessible from your IBM i (default: 127.0.0.1:6379)
- Basic familiarity with IBM i development and Redis concepts
Installation and Setup
Step 1: Clone the Repository
git clone https://github.com/krakadin/redis400.git
cd redis400
Step 2: Build the Project
gmake
This will:
- Create the REDIS400 library
- Compile all C modules
- Create the service program
- Register all SQL functions
Step 3: Verify Installation
Check that all objects were created successfully:
SELECT * FROM QSYS2.SYSFUNCS
WHERE SPECIFIC_NAME LIKE 'REDIS_%';
Example:
-- Store a simple key-value pair
VALUES REDIS_SET('app:config:timeout', '30');
-- Store JSON data
VALUES REDIS_SET('user:1001', '{"name":"John","email":"john@example.com"}');
Best Practices:
- Use descriptive key names with a consistent naming convention
- Consider serializing complex data to JSON before storage
- For temporary data, combine with REDIS_EXPIRE (covered later)
2. REDIS_GET – Retrieving Data
Syntax:
SELECT REDIS_GET(key) FROM SYSIBM.SYSDUMMY1;
Example:
-- Retrieve a simple value
SELECT REDIS_GET('app:config:timeout') AS timeout FROM SYSIBM.SYSDUMMY1;
-- Use in a more complex query
SELECT u.user_id, u.user_name, REDIS_GET('user_pref:' || u.user_id) AS preferences
FROM users u
WHERE u.department = 'SALES';
-- From JSON data
SELECT x.*
FROM JSON_TABLE(
REDIS_GET('user:1001'),
'$'
EMPTY ON ERROR COLUMNS(
name VARCHAR(256) PATH '$.name',
email VARCHAR(256) PATH '$.email',
age INTEGER PATH '$.age' DEFAULT 27 ON EMPTY
)
) AS x;
Handling Results:
- Returns NULL if the key doesn’t exist
- For numeric values, you may need to CAST the result:
CAST(REDIS_GET('count') AS INTEGER)
3. REDIS_INCR – Atomic Counter
Syntax:
VALUES REDIS_INCR(key);
Example:
-- Create or increment an order counter
VALUES REDIS_INCR('orders:current_id');
-- Use in an application
SET order_id = REDIS_INCR('orders:current_id');
INSERT INTO orders VALUES(order_id, CURRENT_TIMESTAMP, ...);
Key Features:
- Atomic operation – safe for concurrent access
- If key doesn’t exist, initializes to 0 then increments to 1
- Returns the new value after increment
4. REDIS_DEL – Removing Data
Syntax:
VALUES REDIS_DEL(key);
Example:
-- Remove a single key
VALUES REDIS_DEL('temp:session:abc123');
-- Clean up old data (in a stored procedure)
BEGIN
DECLARE keys_deleted INT;
SET keys_deleted = REDIS_DEL('old:data:*');
-- Returns count of keys deleted
END
Return Value:
- 1 if the key was deleted
- 0 if the key didn’t exist
5. REDIS_EXPIRE – Setting Expiration
Syntax:
SELECT REDIS_EXPIRE(key, seconds) FROM SYSIBM.SYSDUMMY1;
Example:
-- Set a 30-minute expiration
SELECT REDIS_EXPIRE('user:session:abc123', 1800) FROM SYSIBM.SYSDUMMY1;
-- Combine with SET in a transaction
BEGIN
DECLARE success INT;
VALUES REDIS_SET('temp:report:status', 'processing');
SET success = REDIS_EXPIRE('temp:report:status', 3600); -- 1 hour
END
Return Value:
- 1 if timeout was set
- 0 if key doesn’t exist
6. REDIS_TTL – Checking Expiration
Syntax:
SELECT REDIS_TTL(key) FROM SYSIBM.SYSDUMMY1;
Example:
-- Check how long until a session expires
SELECT REDIS_TTL('user:session:abc123') AS seconds_remaining
FROM SYSIBM.SYSDUMMY1;
-- Monitor cache expiration
SELECT cache_key, REDIS_TTL(cache_key) AS ttl
FROM cached_items
WHERE REDIS_TTL(cache_key) BETWEEN 1 AND 300; -- Expiring in 1-5 minutes
Return Values:
- Positive integer: Remaining time in seconds
- -1: Key exists but has no associated expiration
- -2: Key does not exist
7. REDIS_PING – Testing Connectivity
Syntax:
VALUES(REDIS_PING());
Example:
-- Simple connection test
VALUES(REDIS_PING());
-- Use in a health check procedure
BEGIN
DECLARE status VARCHAR(10);
SET status = REDIS_PING();
IF status = 'PONG' THEN
-- Redis is responsive
ELSE
-- Handle connection issue
END IF;
END
Practical Use Cases
1. Session Management
-- Store session data with expiration
VALUES REDIS_SET('session:' || session_id, session_data);
VALUES REDIS_EXPIRE('session:' || session_id, 1800); -- 30 minutes
-- Check session validity
SELECT
REDIS_GET('session:' || session_id) AS session_data,
REDIS_TTL('session:' || session_id) AS ttl
FROM SYSIBM.SYSDUMMY1;
2. Application Caching
-- Cache expensive query results
CREATE PROCEDURE get_product_details(IN product_id INT)
BEGIN
DECLARE cache_key VARCHAR(128);
DECLARE cached_result CLOB;
SET cache_key = 'product:' || product_id;
SET cached_result = REDIS_GET(cache_key);
IF cached_result IS NULL THEN
-- Not in cache, run the query
SELECT JSON_OBJECT(
'id' VALUE p.id,
'name' VALUE p.name,
'price' VALUE p.price,
'stock' VALUE p.stock
) INTO cached_result
FROM products p
WHERE p.id = product_id;
-- Store in cache with 1-hour expiration
VALUES REDIS_SET(cache_key, cached_result);
VALUES REDIS_EXPIRE(cache_key, 3600);
END IF;
-- Return the result
RETURN cached_result;
END
3. Rate Limiting
-- Track API calls per user
CREATE PROCEDURE check_rate_limit(IN user_id INT)
BEGIN
DECLARE key VARCHAR(128);
DECLARE calls INT;
SET key = 'api_limit:' || user_id;
SET calls = REDIS_INCR(key);
-- If this was the first call, set expiration
IF calls = 1 THEN
VALUES REDIS_EXPIRE(key, 3600); -- 1-hour window
END IF;
-- Check if over limit (100 calls/hour)
IF calls > 100 THEN
SIGNAL SQLSTATE '75001' SET MESSAGE_TEXT = 'Rate limit exceeded';
END IF;
END
Performance Considerations
- Network Latency: Each Redis operation involves a network call. Minimize round trips by:
- Using Redis effectively for caching rather than primary storage
- Batching operations where possible
- Connection Management: The UDFs manage connections efficiently, but:
- Avoid extremely frequent calls in tight loops
- Consider connection pooling for high-volume applications
- Data Size: Redis performs best with smaller values:
- Keep individual values under 100KB when possible
- For larger data, consider splitting or using traditional DB storage
Troubleshooting
Common Issues and Solutions:
- Connection Errors:
- Verify Redis server is running:
VALUES(REDIS_PING())
- Check network connectivity between IBM i and Redis server
- Confirm correct host/port in configuration
- Verify Redis server is running:
- Timeout Errors:
- Increase timeout in Redis configuration if needed
- Check for network latency issues
- Function Not Found:
- Verify the functions were created successfully
- Check your library list includes REDIS400
Monitoring Usage
Track Redis function usage:
SELECT
function_name,
COUNT(*) as call_count,
AVG(elapsed_time) as avg_time
FROM QSYS2.FUNCTION_STATISTICS
WHERE function_schema = 'REDIS400'
GROUP BY function_name;
Conclusion
The Redis SQL Functions for IBM i provide a powerful way to integrate Redis’s high-performance caching with your IBM i applications. By mastering these functions, you can:
- Implement efficient caching layers to reduce database load
- Build robust session management systems
- Create atomic counters for ID generation or rate limiting
- Add expiration features to temporary data
- Enhance application performance with Redis’s in-memory speed
For more advanced usage, consider exploring:
- Redis data structures (hashes, lists, sets)
- Pub/sub functionality for event-driven architectures
- Lua scripting for complex atomic operations
Remember to monitor your Redis usage and adjust configurations as your application scales. Happy coding with Redis on IBM i!
Leave a Reply