Redis入门

来源:互联网 发布:毒狗药三步倒淘宝 编辑:程序博客网 时间:2024/04/29 18:09

Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值。也就是说,你不必仅仅把字符串当作键所指向的值。下列这些数据类型都可作为值类型。

  • 二进制安全的 字符串 string
  • 二进制安全的 字符串列表 list of string
  • 二进制安全的 字符串集合 set of string,换言之:它是一组无重复未排序的element。可以把它看成Ruby中的 hash–其key等于element,value都等于’true‘。
  • 有序集合sorted set of string,类似于集合set,但其中每个元素都和一个浮点数score(评分)关联。element根据score排序。可以把它看成Ruby中的 hash–其key等于element,value等于score,但元素总是按score的顺序排列,无需额外的排序操作。

Redis 键

Redis key值是二进制安全的,这意味着可以用任何二进制序列作为key,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值。

关于key的几条规则:

  • 太长的键值不是个好主意,例如1024字节的键值就不是个好主意,不仅因为消耗内存,而且在数据中查找这类键值的计算成本很高。
  • 太短的键值通常也不是好主意,如果你要用”u:1000:pwd”来代替”user:1000:password”,这没有什么问题,但后者更易阅读,并且由此增加的空间消耗相对于key object和value object本身来说很小。当然,没人阻止您一定要用更短的键值节省一丁点儿空间。
  • 最好坚持一种模式。例如:”object-type:id:field”就是个不错的注意,像这样”user:1000:password”。我喜欢对多单词的字段名中加上一个点,就像这样:”comment:1234:reply.to”。

字符串类型

这是最简单Redis类型。如果你只用这种类型,Redis就像一个可以持久化的memcached服务器(注:memcache的数据仅保存在内存中,服务器重启后,数据将丢失)。

我们来玩儿一下字符串类型:

$ redis-cli set mykey "my binary safe value"
OK
$ redis-cli get mykey
my binary safe value

正如你所见到的,通常用SET command  GET command来设置和获取字符串值。

值可以是任何种类的字符串(包括二进制数据),例如你可以在一个键下保存一副jpeg图片。值的长度不能超过1GB

虽然字符串是Redis的基本值类型,但你仍然能通过它完成一些有趣的操作。例如:原子递增:

$ redis-cli set counter 100
OK $ redis-cli incr counter
(integer) 101
$ redis-cli incr counter
(integer) 102
$ redis-cli incrby counter 10
(integer) 112

INCR 命令将字符串值解析成整型,将其加一,最后将结果保存为新的字符串值,类似的命令有INCRBYDECR and DECRBY。实际上他们在内部就是同一个命令,只是看上去有点儿不同。

INCR是原子操作意味着什么呢?就是说即使多个客户端对同一个key发出INCR命令,也决不会导致竞争的情况。例如如下情况永远不可能发生:『客户端1和客户端2同时读出“10”,他们俩都对其加到11,然后将新值设置为11』。最终的值一定是12read-increment-set操作完成时,其他客户端不会在同一时间执行任何命令。

对字符串,另一个的令人感兴趣的操作是GETSET命令,行如其名:他为key设置新值并且返回原值。这有什么用处呢?例如:你的系统每当有新用户访问时就用INCR命令操作一个Redis key。你希望每小时对这个信息收集一次。你就可以GETSET这个key并给其赋值0并读取原值。

列表类型

要说清楚列表数据类型,最好先讲一点儿理论背景,在信息技术界List这个词常常被使用不当。例如”Python Lists”就名不副实(名为Linked Lists),但他们实际上是数组(同样的数据类型在Ruby中叫数组)

一般意义上讲,列表就是有序元素的序列:10,20,1,2,3就是一个列表。但用数组实现的List和用Linked List实现的List,在属性方面大不相同。

Redis lists基于Linked Lists实现。这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的。用LPUSH 命令在十个元素的list头部添加新元素,和在千万元素list头部添加新元素的速度相同。

那么,坏消息是什么?在数组实现的list中利用索引访问元素的速度极快,而同样的操作在linked list实现的list上没有那么快。

Redis Lists are implemented with linked lists because for adatabase system it is crucial to be able to add elements to a very long list ina very fast way. Another strong advantage is, as you’ll see in a moment, thatRedis Lists can be taken at constant length in constant time.

Redis Listslinked list实现的原因是:对于数据库系统来说,至关重要的特性是:能非常快的在很大的列表上添加元素。另一个重要因素是,正如你将要看到的:Redis lists能在常数时间取得常数长度。

Redis lists 入门

LPUSH 命令可向list的左边(头部)添加一个新元素,而RPUSH命令可向list的右边(尾部)添加一个新元素。最后LRANGE 命令可从list中取出一定范围的元素

$ redis-cli rpush messages "Hello how are you?"
OK
$ redis-cli rpush messages "Fine thanks. I‘m having fun with Redis"
OK
$ redis-cli rpush messages "I should look into this NOSQL thing ASAP"
OK
$ redis-cli lrange messages 0 2
1. Hello how are you?
2. Fine thanks. I‘m having fun with Redis
3. I should look into this NOSQL thing ASAP

注意LRANGE 带有两个索引,一定范围的第一个和最后一个元素。这两个索引都可以为负来告知Redis从尾部开始计数,因此-1表示最后一个元素,-2表示list中的倒数第二个元素,以此类推。

As you can guess from the example above, lists can be used, forinstance, in order to implement a chat system. Another use is as queues inorder to route messages between different processes. But the key point is thatyou can use Redis lists every time you require to access data in the same orderthey are added. This will not require any SQL ORDER BY operation, will be veryfast, and will scale to millions of elements even with a toy Linux box.

正如你可以从上面的例子中猜到的,list可被用来实现聊天系统。还可以作为不同进程间传递消息的队列。关键是,你可以每次都以原先添加的顺序访问数据。这不需要任何SQL ORDER BY操作,将会非常快,也会很容易扩展到百万级别元素的规模。

例如在评级系统中,比如社会化新闻网站 reddit.com,你可以把每个新提交的链接添加到一个list,用LRANGE可简单的对结果分页。

在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入进博客评论,等等。

 

TYPE key — 用来获取某key的类型
KEYS pattern —匹配所有符合模式的key,比如KEYS *就列出所有的key了,当然,复杂度O(n)

RENAMEoldkeynewkey— key也可以改名

 

MySql快速插入以及批量更新

插入:

MySql提供了可以一次插入多条数据的用法:

[sql] 

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9),(10,11,12)...; 

在程序中可以通过循环,添加Values对应的列表,最后使用一次executeUpdate完成插入操作。但是Mysql语句并不是越长越好,MYsql语句长度有限制,可以查看mysql的配置文件my.inmax_allowed_packet属性,并进行相应设置。

更新:

 

Mysql中没有提供像Insert一样一次更新多条记录,需要逐条语句拼接。

 

[sql] 

update weibo set userName ='xyw' where id = '22';update weibo set userID = '143' where id = '35';  

你可以使用addBatch语句,将拼接起来的SQL语句进行一次性处理,但是效率并不高。

还需要处理resultset的释放问题,否则mysql会报错:"Commands out of sync; you can'trun this command now"

针对update语句,虽然并没有resultset返回,但仍然需要释放。而由于未知原因(可能是sql语句太长?),释放resultset非常耗时,最终算下来得不偿失。

针对以上的不足,可以使用另一种方法执行批量更新。

[sql] 

INSERT INTO tbl_name[col_name1, col_name2,...)] VALUES(col_value1,col_value2,...),(col_value1,col_value2,...)ON DUPLICATE KEY UPDATE userName=VALUES(userName) 

使用这种方法必须满足条件:col_name1, col_name2,...中必须有主键或者唯一键。

userName是要更新的列。

 

如果想一次更新多列,可以在userName=VALUES(userName)后面继续添加,例如:

 

[sql] 

INSERT INTO tbl_name[col_name1, col_name2,...)] VALUES(col_value1,col_value2,...), (col_value1,col_value2,...)ONDUPLICATE KEY UPDATE userName=VALUES(userName), userID = VALUES(userID)  

这样就可以同时更新userNameuserID两个字段。

它的实现原理是,首先Mysql根据表名后面列出的主键,查找表(因为是主键,所以在表中唯一存在)。如果存在该行数据,则按照最后的col_name = values(col_name)列表对相应的字段,按照values列表中给出的值进行更新。建议:表名后面的字段列表,除了主键之外,列出来的最好都作为更新的对象,即在语句最后都要有相应的col_name = values(col_name),否则,你在表名后罗列出来字段,在values中赋值了,但是不是更新的对象,显然是浪费。

 

如果不存在该行数据,则进行插入操作,没有作为更新对象的列按照默认值填充(前提是Mysql运行在非严格模式下。如果在严格模式下,没列都需要有默认值,否则运行出错)。

 

注意:

 

主键可以作为更新的对象,但是只是在表中不存在该记录时起作用,即执行了插入操作,如果表中已经存在了该主键对应的行数据,下次更新时不会再插入该行,而是执行除了主键之外的其他列的更新操作。所以最好不要将主键设置为更新的对象。

 

实例:

 

[sql] 

INSERT INTO keywordtable(id,keyword, userName, userID) VALUES(1, '你好', 'Eliot', 22), (2, 'hello','Jhon', 23),   

(3, '嘻嘻','Jim', 24) ON DUPLICATE KEY UPDATE keyword=VALUES(keyword),userName=VALUES(userName),userID=VALUES(userID);  

除了id外,字段有keyword,userName, userID,他们是要更新的字段。

 

思路:固定哈希表的大小,只是更换key值

 

如果数据集需要使用非常大的内存,但不希望使用一致性哈希或其他方式将数据集分布在不同的节点,还能采用Redis吗?

一个可行的方案是同时使用传统数据库(Mysql或者其他的)和Redis,Redis里面存放状态信息(元数据,小但经常写的信息)和所有其他读写频繁的数据:用户身份验证token,使用Redis List 存放与时间顺序有关的id列表、编码等等。然后使用MySQL(或其他)作为存储引擎来存放更大的数据,创建一个自增长ID作为主键和一个较大的BLOB字段作为数据字段,访问MySQL的数据只能通过主键(ID)。执行查询操作时,通过Redis读取数据,但是当有读取大数据时需要通过主键(ID)访问MySQL数据库。

Jedis

JedisPool的配置参数很大程度上依赖于实际应用需求、软硬件能力。以前没用过commons-pool,所以这次花了一整天专门看这些参数的含义。。。JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。

maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted。

maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例;

whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种。
WHEN_EXHAUSTED_FAIL --> 表示无jedis实例时,直接抛出NoSuchElementException;
WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到maxWait时抛出JedisConnectionException;
WHEN_EXHAUSTED_GROW --> 则表示新建一个jedis实例,也就说设置的maxActive无用;

maxWait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;

testOnBorrow:在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;

testOnReturn:在return给pool时,是否提前进行validate操作;

testWhileIdle:如果为true,表示有一个idleobject evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;

timeBetweenEvictionRunsMillis:表示idleobject evitor两次扫描之间要sleep的毫秒数;

numTestsPerEvictionRun:表示idleobject evitor每次扫描的最多的对象数;

minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短时间,然后才能被idleobject evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;

softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。如果为-1,evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义;

lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last infirst out,即类似cache的最频繁使用队列),如果为False,则表示FIFO队列;

其中JedisPoolConfig对一些参数的默认设置如下:
testWhileIdle=true
minEvictableIdleTimeMills=60000
timeBetweenEvictionRunsMillis=30000
numTestsPerEvictionRun=-1


参考

http://my.oschina.net/gccr/blog/307725


 

0 0
原创粉丝点击